bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

Description

@os-project-manager

Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

What was measured

Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
scatter-chart020none
plugin-charts:scatter-chart020none
pie-chart020none
plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

Why

packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

Why it matters

Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

Not measured

  • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
  • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

Refs: objectui#7194 · PR #7400 (the control row above).

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

      Description

      @os-project-manager

      Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

      What was measured

      Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

      schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
      scatter-chart020none
      plugin-charts:scatter-chart020none
      pie-chart020none
      plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

      Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

      Why

      packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

      chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

      Why it matters

      Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

      It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

      Not measured

      • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
      • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

      Refs: objectui#7194 · PR #7400 (the control row above).

      Generated by Claude Code

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

          Description

          @os-project-manager

          Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

          What was measured

          Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

          schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
          scatter-chart020none
          plugin-charts:scatter-chart020none
          pie-chart020none
          plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

          Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

          Why

          packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

          chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

          Why it matters

          Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

          It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

          Not measured

          • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
          • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

          Refs: objectui#7194 · PR #7400 (the control row above).

          Generated by Claude Code

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

              Description

              @os-project-manager

              Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

              What was measured

              Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

              schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
              scatter-chart020none
              plugin-charts:scatter-chart020none
              pie-chart020none
              plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

              Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

              Why

              packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

              chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

              Why it matters

              Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

              It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

              Not measured

              • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
              • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

              Refs: objectui#7194 · PR #7400 (the control row above).

              Generated by Claude Code

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

                  Description

                  @os-project-manager

                  Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

                  What was measured

                  Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

                  schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
                  scatter-chart020none
                  plugin-charts:scatter-chart020none
                  pie-chart020none
                  plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

                  Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

                  Why

                  packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

                  chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

                  Why it matters

                  Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

                  It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

                  Not measured

                  • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
                  • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

                  Refs: objectui#7194 · PR #7400 (the control row above).

                  Generated by Claude Code

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

                      Description

                      @os-project-manager

                      Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

                      What was measured

                      Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

                      schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
                      scatter-chart020none
                      plugin-charts:scatter-chart020none
                      pie-chart020none
                      plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

                      Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

                      Why

                      packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

                      chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

                      Why it matters

                      Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

                      It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

                      Not measured

                      • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
                      • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

                      Refs: objectui#7194 · PR #7400 (the control row above).

                      Generated by Claude Code

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

                          Description

                          @os-project-manager

                          Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

                          What was measured

                          Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

                          schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
                          scatter-chart020none
                          plugin-charts:scatter-chart020none
                          pie-chart020none
                          plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

                          Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

                          Why

                          packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

                          chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

                          Why it matters

                          Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

                          It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

                          Not measured

                          • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
                          • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

                          Refs: objectui#7194 · PR #7400 (the control row above).

                          Generated by Claude Code

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401

                              Description

                              @os-project-manager

                              Found while implementing objectui#7194 (PR #7400). Not fixed there — a different mechanism and outside that card's scope.

                              What was measured

                              Rendered through the real SDUI path (SchemaRenderer from @object-ui/react, with @object-ui/plugin-charts imported so its registrations run, in vitest with the real Recharts and a 480x320 container), each schema carrying xAxisKey: 'xm', series: [{ dataKey: 'ym' }, { dataKey: 'zm' }] and two numeric rows:

                              schema type.recharts-scatter.recharts-bar.recharts-piedata-chart-error
                              scatter-chart020none
                              plugin-charts:scatter-chart020none
                              pie-chart020none
                              plugin-charts:chart with chartType: 'scatter' (control)000scatter-multi-series (PR #7400's refusal, i.e. the scatter arm was reached)

                              Three chart-type registrations rendered a two-series bar chart. Only the generic chart type with an explicit chartType reached the family it names.

                              Why

                              packages/plugin-charts/src/index.tsx registers pie-chart, donut-chart, radar-chart, scatter-chart (and siblings) as ChartRenderer with defaultProps: { chartType: FAMILY }. Nothing on the SDUI path reads that option: git grep defaultProps packages/react/src packages/core/src finds one consumer, WidgetRegistry.ts (manifest defaultProps for widgets), and none in SchemaRenderer. ChartRenderer then computes chartType: schema.chartType ?? spec.chartType, both undefined for these schemas, and AdvancedChartImpl defaults to 'bar'.

                              chart:bar is registered through a separate ChartBarRenderer wrapper that sets the family itself, which is why that one works — and why the fix is a decision (apply defaultProps in the renderer for every registration, or give each chart-type registration its own wrapper like chart:bar) rather than a one-liner.

                              Why it matters

                              Same class as objectui#7194: valid data, a confidently wrong picture, no refusal that can fire. An author who writes type: 'pie-chart' gets a bar chart with no diagnostic. The documented chart-type registrations (content/docs/plugins/plugin-charts.mdx lists them) are therefore all one family.

                              It also blinds a sweep: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:pie-chart, donut-chart, radar-chart and scatter-chart entries and waits on [data-slot="chart"] — all four pass as bar charts, so the pie / donut / radar / scatter renderers are not actually swept there. (This is how the finding surfaced: PR #7400's refusal should have broken that sweep's two-series scatter-chart entry and did not.)

                              Not measured

                              • Whether any published example or tenant metadata uses the FAMILY-chart types (in-repo: packages/plugin-charts/examples/chart-examples.ts uses type: 'pie-chart' — it would draw a bar).
                              • Whether WidgetRegistry's manifest defaultProps path is affected (it has its own consumer; likely not).

                              Refs: objectui#7194 · PR #7400 (the control row above).

                              Generated by Claude Code

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions