Repository navigation
feat: dashboard charts settings - #581
Conversation
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
- Added `addDefaultChart` method to `MosaicDashboardContext` for default chart creation. - Wrapped `RendererComponent` in `DashboardPanelErrorBoundary` in `MosaicDashboardPanel` for error handling. - Updated `MosaicDashboardProfilerPanelRenderer` to use `ProfilerPanelRendererProps`. - Refactored `createMosaicDashboardVgPlotPanelConfig` to accept `VgPlotChartConfig` directly. - Modified `MosaicDashboardToolbar` to trigger `addDefaultChart` instead of `openBuilder`. - Improved `ChartSettings` component structure with a new context for better state management. - Removed deprecated chart settings components (e.g., `BoxPlotSettings`, `BubbleSettings`, etc.) and replaced them with a dynamic settings approach. - Introduced `DynamicChartSettings` for handling chart-specific settings dynamically based on selected chart type. - Updated `generateMosaicChartSpec` to include error handling and improved logging. - Cleaned up imports and exports across various files for better organization and clarity. Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
📝 WalkthroughWalkthroughThis PR reorganizes the Mosaic chart type system by moving chart definitions to a centralized ChangesChart Type System Reorganization
Chart Creation Callback Signature Migration
Dashboard VgPlot Panel Type System
Chart Settings UI System
Dashboard Panel Rendering Improvements
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/mosaic/src/dashboard/MosaicDashboardSlice.ts (1)
403-408:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEvict retained VGPlot charts on any config change, not just
config.vgplot.After this refactor, most
VgPlotChartConfigupdates no longer carry a top-levelvgplotfield. With the current check, changing histogram/line/etc. settings won't evict the retained chart, so the dashboard can keep rendering stale chart instances after a settings edit.Suggested fix
if (panel.type === MOSAIC_DASHBOARD_VGPLOT_PANEL_TYPE) { - return Boolean( - patch.config && - Object.prototype.hasOwnProperty.call(patch.config, 'vgplot'), - ); + return patch.config !== undefined; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/MosaicDashboardSlice.ts` around lines 403 - 408, The current eviction check for VgPlot panels only returns true when patch.config has a top-level 'vgplot' property, which misses most VgPlotChartConfig updates; change the condition in the MOSAIC_DASHBOARD_VGPLOT_PANEL_TYPE branch to evict on any config change by checking presence of patch.config (e.g., return Boolean(patch.config)) instead of using Object.prototype.hasOwnProperty.call(patch.config, 'vgplot'), and ensure references to VgPlotChartConfig and the patch handling logic in MosaicDashboardSlice (the code that inspects panel.type === MOSAIC_DASHBOARD_VGPLOT_PANEL_TYPE) are updated accordingly.
🧹 Nitpick comments (13)
packages/mosaic/src/dashboard/chart-settings/ChartSettingsContext.tsx (1)
41-47: ⚡ Quick winInline
valueobject creates a new reference on every render, causing all context consumers to re-render unnecessarily.Stabilize the context value with
useMemoso consumers only re-render when the inputs actually change.♻️ Proposed refactor
-import {createContext, type ReactNode, useContext} from 'react'; +import {createContext, type ReactNode, useContext, useMemo} from 'react';export function ChartSettingsProvider({ tableName, config, columns, onChange, children, }: ChartSettingsProviderProps) { + const value = useMemo( + () => ({tableName, config, columns, onChange}), + [tableName, config, columns, onChange], + ); return ( - <ChartSettingsContext.Provider - value={{tableName, config, columns, onChange}} - > + <ChartSettingsContext.Provider value={value}> {children} </ChartSettingsContext.Provider> ); }As per coding guidelines, "Keep the compound context/provider layer thin and stable."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/chart-settings/ChartSettingsContext.tsx` around lines 41 - 47, The Provider is passing an inline object to ChartSettingsContext.Provider causing a new reference each render; wrap the value in React.useMemo and return {tableName, config, columns, onChange} inside it so the object identity is stable, and use [tableName, config, columns, onChange] (or the minimal real dependencies) as the dependency array; update the ChartSettingsContext.Provider to pass this memoizedValue instead of the inline object so consumers only re-render when those inputs actually change.packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx (1)
31-41: 💤 Low value
onResizeshould be wrapped inuseCallbackfor stable reference.A new function is allocated every render and passed as the
onResizeprop, triggering unnecessary reconciliation insideResizablePanel.♻️ Proposed refactor
- const onResize = () => { + const onResize = React.useCallback(() => { const isCollapsed = panelRef.current?.isCollapsed(); if (isCollapsed && isOpen) { onIsOpenChange?.(false); } if (!isCollapsed && !isOpen) { onIsOpenChange?.(true); } - }; + }, [isOpen, onIsOpenChange]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx` around lines 31 - 41, The onResize function is recreated each render causing unstable props to ResizablePanel; wrap the handler in React.useCallback to memoize it (referencing panelRef.current, isOpen, and onIsOpenChange) so its identity remains stable across renders. Update the onResize declaration (used as prop to ResizablePanel) to use useCallback and include only the necessary dependencies (panelRef, isOpen, onIsOpenChange) to avoid stale closures while preventing unnecessary re-renders.packages/mosaic/src/chart-types/ecdf/definition.ts (1)
9-14: ⚡ Quick winExtract
FG_COLORandtitleFromDescriptiontochartTypeUtils.ts— duplicated across every chart-type definition.
titleFromDescription(lines 11-14) andFG_COLOR(line 9) are copy-pasted verbatim intoecdf/definition.ts,box-plot/definition.ts,heatmap/definition.ts, and (based on context)histogram/definition.ts. Moving them tochartTypeUtils.tseliminates the risk of diverging quietly.♻️ Proposed refactor
In
chartTypeUtils.ts:+export const FG_COLOR = 'var(--color-chart-1)'; + +export function titleFromDescription(description: string) { + return (fieldValues: Record<string, string>) => + buildDefaultChartTitle(description, fieldValues); +}Then in each definition file:
import { buildDefaultChartTitle, + FG_COLOR, + titleFromDescription, QUANTITATIVE_COLUMN_TYPES, } from '../../chart-builders/chartTypeUtils'; - -const FG_COLOR = 'var(--color-chart-1)'; - -function titleFromDescription(description: string) { - return (fieldValues: Record<string, string>) => - buildDefaultChartTitle(description, fieldValues); -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-types/ecdf/definition.ts` around lines 9 - 14, The FG_COLOR constant and titleFromDescription function are duplicated across chart-type definitions; extract them into a new exported helpers module (chartTypeUtils.ts), export FG_COLOR and titleFromDescription (which should call buildDefaultChartTitle as before), then replace the local declarations in each chart-type definition (ecdf, box-plot, heatmap, histogram) with imports from chartTypeUtils.ts so all files use the single shared FG_COLOR and titleFromDescription.packages/mosaic/src/chart-types/custom-spec/definition.ts (1)
6-9: ⚡ Quick win
titleFromDescriptionis the same function defined inline-chart/definition.ts— consolidate intochartTypeUtils.See the extraction suggestion on
line-chart/definition.ts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-types/custom-spec/definition.ts` around lines 6 - 9, The titleFromDescription function is duplicated; extract it into the shared utility module chartTypeUtils (create and export a function titleFromDescription(description: string): (fieldValues: Record<string,string>) => string that delegates to buildDefaultChartTitle), update both usages in custom-spec/definition.ts and line-chart/definition.ts to import the shared titleFromDescription from chartTypeUtils, and remove the duplicate definition from each file so only the exported utility remains.packages/mosaic/src/chart-builders/hooks/useChartFieldForm.ts (1)
15-20: ⚡ Quick win
useCallback([onChange])provides no stability when callers pass an inlineonChange.Both current call sites create
onChangeas an inline arrow on every render (e.g.,(key, value) => setFieldValue(key, value as string)inChartBuilderFields.tsx). A single value that is "always new" is enough to break memoization for an entire component. SinceonChangeis unstable at both call sites,handleFieldChangewill be a new reference on every render regardless of theuseCallback.Either require callers to stabilize
onChangebefore passing it in (document this contract), or drop theuseCallbackwrapper since it adds overhead without benefit in the current usage pattern.♻️ Option A — drop the wrapper (honest, no false stability promise)
export function useChartFieldForm({ fields, values, onChange, }: UseChartFieldFormProps) { - const handleFieldChange = useCallback( - (fieldKey: string, value: unknown) => { - onChange(fieldKey, value); - }, - [onChange], - ); - return { fields, values, - handleFieldChange, + handleFieldChange: onChange, }; }♻️ Option B — callers stabilize onChange (keeps the hook's contract)
In
ChartBuilderFields.tsx, wrap withuseCallback:+const handleChange = useCallback( + (key: string, value: unknown) => setFieldValue(key, value as string), + [setFieldValue], +); const {fields, handleFieldChange} = useChartFieldForm({ fields: selectedTemplate?.fields || [], values: fieldValues, - onChange: (key: string, value: unknown) => - setFieldValue(key, value as string), + onChange: handleChange, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-builders/hooks/useChartFieldForm.ts` around lines 15 - 20, The useCallback around handleFieldChange in useChartFieldForm is ineffective because callers pass an inline unstable onChange; remove the misleading memoization by replacing the useCallback wrapper with a plain function export (i.e., change handleFieldChange in useChartFieldForm to a direct function that calls onChange), or alternatively stabilize the caller-side onChange (wrap the inline (key, value) => setFieldValue(...) in ChartBuilderFields.tsx with useCallback) if you want to preserve the hook's memoization contract—pick one approach and apply consistently to handleFieldChange and its caller(s).packages/mosaic/src/chart-types/line-chart/definition.ts (1)
12-15: ⚡ Quick win
titleFromDescriptionis duplicated across chart-type definitions — extract it tochartTypeUtils.ts.The same helper is defined verbatim in
custom-spec/definition.ts(and possibly other chart-type definitions). Since both files already importbuildDefaultChartTitlefromchartTypeUtils, the natural home is to exporttitleFromDescriptionfrom there.♻️ Suggested extraction
In
packages/mosaic/src/chart-builders/chartTypeUtils.ts, add:+export function titleFromDescription(description: string) { + return (fieldValues: Record<string, string>) => + buildDefaultChartTitle(description, fieldValues); +}Then in each definition file:
import { buildDefaultChartTitle, + titleFromDescription, QUANTITATIVE_COLUMN_TYPES, NUMERIC_COLUMN_TYPES, } from '../../chart-builders/chartTypeUtils'; -function titleFromDescription(description: string) { - return (fieldValues: Record<string, string>) => - buildDefaultChartTitle(description, fieldValues); -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-types/line-chart/definition.ts` around lines 12 - 15, The helper titleFromDescription is duplicated across chart-type definitions; extract and export it from the shared chartTypeUtils module where buildDefaultChartTitle already lives, then remove the duplicate definitions. Specifically, add an exported function titleFromDescription(description: string) that returns (fieldValues) => buildDefaultChartTitle(description, fieldValues) in the chartTypeUtils module, and in each chart definition (e.g., the line-chart definition that currently declares titleFromDescription) replace the local function with an import of the shared titleFromDescription and remove the redundant code.packages/mosaic/src/chart-types/histogram/definition.ts (1)
9-15: ⚡ Quick winExtract shared chart-definition helpers to
chartTypeUtils.ts.
BG_COLOR,FG_COLOR, andtitleFromDescriptionare identically defined in bothhistogram/definition.ts(lines 9–15) andcount-plot/definition.ts(lines 9–15), and presumably in all 8 chart-type definition files. WithbuildDefaultChartTitlealready living inchartTypeUtils.ts, the curried wrapper belongs there too.♻️ Suggested extraction
In
packages/mosaic/src/chart-builders/chartTypeUtils.ts, add:+export const CHART_BG_COLOR = 'var(--color-chart-overlay)'; +export const CHART_FG_COLOR = 'var(--color-chart-1)'; + +export function titleFromDescription(description: string) { + return (fieldValues: Record<string, string>) => + buildDefaultChartTitle(description, fieldValues); +}Then in each definition file:
-const BG_COLOR = 'var(--color-chart-overlay)'; -const FG_COLOR = 'var(--color-chart-1)'; - -function titleFromDescription(description: string) { - return (fieldValues: Record<string, string>) => - buildDefaultChartTitle(description, fieldValues); -} import { buildDefaultChartTitle, + CHART_BG_COLOR as BG_COLOR, + CHART_FG_COLOR as FG_COLOR, + titleFromDescription, QUANTITATIVE_COLUMN_TYPES, } from '../../chart-builders/chartTypeUtils';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-types/histogram/definition.ts` around lines 9 - 15, BG_COLOR, FG_COLOR, and titleFromDescription are duplicated across multiple chart definition files; move these shared helpers into chartTypeUtils.ts and import them from there. Specifically, add BG_COLOR and FG_COLOR constants and a titleFromDescription(description: string) curried wrapper that calls buildDefaultChartTitle in packages/mosaic/src/chart-builders/chartTypeUtils.ts, then remove the duplicated definitions from histogram/definition.ts (and other chart definition files) and replace them with imports of BG_COLOR, FG_COLOR, and titleFromDescription from chartTypeUtils.ts.packages/mosaic/src/chart-types/count-plot/definition.ts (1)
9-15: ⚡ Quick winSame duplication as
histogram/definition.ts.
BG_COLOR,FG_COLOR, andtitleFromDescriptionare byte-for-byte identical to lines 9–15 inhistogram/definition.ts. See the suggested extraction comment on that file.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/chart-types/count-plot/definition.ts` around lines 9 - 15, BG_COLOR, FG_COLOR and titleFromDescription are duplicated; extract them into a shared module (e.g., export constants BG_COLOR, FG_COLOR and a function titleFromDescription from a new/shared helper like chartConstants or chartTitleUtils) and replace the local definitions in both count-plot and histogram definitions with imports of those symbols; update usages to reference the imported BG_COLOR, FG_COLOR and titleFromDescription so both files reuse the single implementation.packages/mosaic/src/dashboard/MosaicDashboard.tsx (1)
106-124: ⚡ Quick winNarrow
handleAddDefaultChartdeps todashboard?.selectedTableto avoid context churn.The callback only reads
dashboard?.selectedTable, butdashboard(the full object) is in the deps array. SinceaddPanelmutatesdashboard, every panel addition recreateshandleAddDefaultChart, which then re-evaluatescontextValue(line 148), re-rendering all context consumers. Compare withhandleCreateChartat line 103, which correctly scopes todashboard?.selectedTable.🔧 Proposed fix
- const handleAddDefaultChart = useCallback(() => { - if (!dashboard?.selectedTable) { - return; - } - - // Create empty chart panel - user will select type and fields - const panel = createMosaicDashboardVgPlotPanelConfig( - 'New Chart', - { - chartType: 'histogram', // Default type, but with empty settings - settings: {}, - vgplot: null, - settingsOpen: true, // Open settings by default - }, - {tableName: dashboard.selectedTable}, - ); - - addPanel(dashboardId, panel); - }, [addPanel, dashboard, dashboardId]); + const handleAddDefaultChart = useCallback(() => { + if (!dashboard?.selectedTable) { + return; + } + + // Create empty chart panel - user will select type and fields + const panel = createMosaicDashboardVgPlotPanelConfig( + 'New Chart', + { + chartType: 'histogram', + settings: {}, + vgplot: null, + settingsOpen: true, + }, + {tableName: dashboard?.selectedTable}, + ); + + addPanel(dashboardId, panel); + }, [addPanel, dashboard?.selectedTable, dashboardId]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/MosaicDashboard.tsx` around lines 106 - 124, The handleAddDefaultChart callback closes over the whole dashboard object causing unnecessary re-creations; change its dependency array to only include the specific values it reads (dashboard?.selectedTable, addPanel, dashboardId) by replacing "dashboard" with "dashboard?.selectedTable" so that handleAddDefaultChart only re-runs when the selected table changes; keep the rest of the implementation the same and ensure references to createMosaicDashboardVgPlotPanelConfig and addPanel remain unchanged.packages/mosaic/__tests__/generateMosaicChartSpec.test.ts (1)
101-126: ⚡ Quick winConsider using
jest.spyOninstead of direct registry mutation.Direct mutation of
mosaicChartTypes.histogramis fragile—if an assertion fails before line 124, the restore never runs, potentially breaking subsequent tests. Usingjest.spyOnwithmockRestore()in afinallyblock orafterEachis safer.♻️ Proposed safer pattern
it('handles createSpec throwing an error', () => { const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const createSpecSpy = jest.spyOn(mosaicChartTypes.histogram, 'createSpec') + .mockImplementation(() => { + throw new Error('Test error'); + }); - // Mock a chart type that throws - const originalHistogram = mosaicChartTypes.histogram; - mosaicChartTypes.histogram = { - ...originalHistogram, - createSpec: () => { - throw new Error('Test error'); - }, - }; - const result = generateMosaicChartSpec('sales', 'histogram', { field: 'amount', }); expect(result).toBeNull(); expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining('Failed to generate spec'), expect.any(Error), ); - // Restore - mosaicChartTypes.histogram = originalHistogram; + createSpecSpy.mockRestore(); consoleSpy.mockRestore(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/__tests__/generateMosaicChartSpec.test.ts` around lines 101 - 126, The test directly mutates mosaicChartTypes.histogram which is fragile; instead spy on the registry entry and restore it reliably. Replace the direct assignment to mosaicChartTypes.histogram with jest.spyOn(mosaicChartTypes, 'histogram') (or spy on the createSpec method) to mock createSpec to throw, call generateMosaicChartSpec('sales','histogram', {...}) as before, and ensure you call mockRestore() in a finally block or in afterEach so the original mosaicChartTypes.histogram is always restored; reference the mosaicChartTypes.histogram symbol, the createSpec method, generateMosaicChartSpec, jest.spyOn, and mockRestore when making the change.packages/mosaic/src/dashboard/chart-settings/DynamicChartSettings.tsx (1)
16-48: ⚡ Quick winInline callback inside
memocauses unnecessary re-renders.The
onChangecallback on line 21-23 creates a new function reference on every render, which propagates throughuseChartFieldFormand triggers child re-renders, partially defeatingmemo(). Wrap it withuseCallback.♻️ Proposed fix
-export const DynamicChartSettings: FC<DynamicChartSettingsProps> = memo( - ({chartTypeDefinition, columns, values, onChange}) => { +export const DynamicChartSettings: FC<DynamicChartSettingsProps> = memo( + ({chartTypeDefinition, columns, values, onChange}) => { + const handleChange = useCallback( + (key: string, value: unknown) => { + onChange({...values, [key]: value}); + }, + [onChange, values], + ); + const {fields, handleFieldChange} = useChartFieldForm({ fields: chartTypeDefinition.fields, values, - onChange: (key, value) => { - onChange({...values, [key]: value}); - }, + onChange: handleChange, });Note: You'll need to add
useCallbackto the import on line 1.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/chart-settings/DynamicChartSettings.tsx` around lines 16 - 48, The inline onChange passed into useChartFieldForm inside DynamicChartSettings creates a new function each render and defeats memoization; wrap that callback in useCallback (and add useCallback to the imports) so the function reference is stable before calling useChartFieldForm — e.g. create a memoized const onFieldChange = useCallback((key, value) => onChange({...values, [key]: value}), [onChange, values]) and pass onFieldChange to useChartFieldForm; keep the rest (fields, handleFieldChange, FieldSelectorInput usage) unchanged.packages/mosaic/src/dashboard/chart-settings/ChartSettings.tsx (1)
55-76: 💤 Low valueConsider wrapping
handleChartTypeChangeinuseCallback.The handler creates a new function reference on every render. If
ChartTypeSelectoris memoized or passes this to memoized children, this could cause unnecessary re-renders.♻️ Proposed fix
const ChartSettingsTypeSelector: FC = () => { const {config, columns, onChange} = useChartSettingsContext(); - const handleChartTypeChange = (newChartType: VgPlotChartType) => { + const handleChartTypeChange = useCallback((newChartType: VgPlotChartType) => { // When changing chart type, clear settings and don't show chart // until user selects all required fields onChange({ chartType: newChartType, settings: {}, vgplot: null, settingsOpen: config.settingsOpen, }); - }; + }, [config.settingsOpen, onChange]); return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/chart-settings/ChartSettings.tsx` around lines 55 - 76, The handler handleChartTypeChange in ChartSettingsTypeSelector should be memoized with React's useCallback to avoid creating a new function reference each render; wrap the existing function in useCallback and include the correct dependency array (at minimum onChange and config.settingsOpen) so it only changes when those values change, ensure useCallback is imported from React, and keep the function body unchanged (still calling onChange to reset chartType, settings, vgplot, and settingsOpen) so ChartTypeSelector receives a stable onChange prop.packages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsx (1)
18-34: 💤 Low valueSimplify
toRenderableMosaicSpecby removing unnecessary try-catch.The only operations inside the try block (type checking, destructuring, returning) cannot throw. The defensive try-catch adds complexity without catching real errors.
♻️ Simplified version
function toRenderableMosaicSpec(vgplot: unknown): Spec | null { - try { - if (!vgplot || typeof vgplot !== 'object' || Array.isArray(vgplot)) { - return null; - } - - const vgplotRecord = vgplot as Spec; - - // eslint-disable-next-line `@typescript-eslint/no-unused-vars` - const {$schema, ...mosaicSpec} = vgplotRecord; - - return mosaicSpec; - } catch (error) { - console.error('[toRenderableMosaicSpec] Failed to parse spec:', error); + if (!vgplot || typeof vgplot !== 'object' || Array.isArray(vgplot)) { return null; } + + // eslint-disable-next-line `@typescript-eslint/no-unused-vars` + const {$schema, ...mosaicSpec} = vgplot as Spec; + + return mosaicSpec; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsx` around lines 18 - 34, Remove the unnecessary try-catch in toRenderableMosaicSpec: keep the input validation (check for falsy, non-object, or array) and the destructuring that strips $schema from the vgplotRecord, then directly return mosaicSpec (typed as Spec | null). Update the function body in MosaicDashboardVgPlotPanelRenderer.tsx to eliminate the try/catch block and associated catch logging while preserving the early return of null on invalid input and the cast to Spec for vgplotRecord.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/sqlrooms-cli-ui/src/store.ts`:
- Line 84: There is a duplicate import for VgPlotPanelConfig from
'@sqlrooms/mosaic'; remove the standalone import and add VgPlotPanelConfig to
the existing import declaration that already imports from '@sqlrooms/mosaic' (so
the single import statement includes VgPlotPanelConfig alongside the other
symbols), ensuring no duplicate-imports lint error and preserving the original
exported names and usage in store.ts.
In `@packages/mosaic/__tests__/DashboardPanelErrorBoundary.test.tsx`:
- Around line 5-49: Tests only exercise the no-error path because
renderToStaticMarkup is SSR and error boundaries don't catch errors there; add a
client-side test that renders DashboardPanelErrorBoundary with a child component
that throws to verify the fallback UI ("Failed to load panel") and that errors
are suppressed. Fix by replacing or adding a test that mounts
DashboardPanelErrorBoundary in a real DOM (either by adding
`@testing-library/react` and using its render() to assert the fallback, or by
using ReactDOM.createRoot to render the boundary with a throwing component, then
assert the DOM contains "Failed to load panel" and unmount/cleanup), targeting
DashboardPanelErrorBoundary and its getDerivedStateFromError behavior.
In `@packages/mosaic/src/chart-types/ecdf/definition.ts`:
- Around line 33-59: The y encoding currently uses a cumulative sum of the field
which produces a running total instead of an eCDF; in createSpec change the y
encodings for both the areaY and lineY marks to use cumulative counts by
replacing y: {sum: field, cumulative: true} with y: {count: null, cumulative:
true} so the plot represents the empirical cumulative distribution (apply the
same change for the areaY and lineY entries referencing field).
In `@packages/mosaic/src/chart-types/index.ts`:
- Around line 61-63: The function createDefaultChartTypes currently references
ChartTypeDefinition from ../chart-builders/types, creating a circular
dependency; remove the explicit return type import from chart-types/index.ts and
let TypeScript infer the return type for createDefaultChartTypes (or
alternatively extract ChartTypeDefinition into a neutral module both packages
can import). Update the createDefaultChartTypes signature to omit the
import-based return type so chart-types no longer imports chart-builders/types
and rerun the dependency check.
In `@packages/mosaic/src/dashboard/generateMosaicChartSpec.ts`:
- Around line 1-3: Change the first import to a type-only import for Spec (use
"import type {Spec} from '@uwdata/mosaic-spec'") and merge the two imports from
'../chart-types' into a single import that exports mosaicChartTypes,
VgPlotChartSettings, and VgPlotChartType so you only have one module specifier
for '../chart-types' (update references to Spec, mosaicChartTypes,
VgPlotChartSettings, and VgPlotChartType accordingly).
In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx`:
- Around line 55-66: MosaicDashboardPanelLayout currently hardcodes the header
text "Chart settings"; add an optional prop settingsTitle?: string to the
MosaicDashboardPanelLayout component (with a default value of "Chart settings")
and replace the hardcoded span content with this prop so callers can override
the panel title; update the component's props/type signature and any call sites
as needed, and ensure the collapse button behavior
(panelRef.current?.collapse()) remains unchanged.
In `@packages/mosaic/src/dashboard/MosaicDashboardSlice.ts`:
- Around line 77-89: LegacyPanelConfig currently allows any string for "type" so
modern types can fall through to the legacy branch; change LegacyPanelConfig's
type schema to explicitly exclude the known modern panel type strings (e.g.
'vgplot' and 'profiler') so only true legacy payloads match. Concretely, update
the "type" entry in LegacyPanelConfig (the z.object that defines
id/type/title/source/config) to use a refinement or predicate (for example
z.string().refine(t => t !== 'vgplot' && t !== 'profiler')) or an explicit union
of allowed legacy literals, leaving VgPlotPanelConfig, ProfilerPanel and the
discriminatedUnion('type', [VgPlotPanelConfig, ProfilerPanel]) unchanged.
---
Outside diff comments:
In `@packages/mosaic/src/dashboard/MosaicDashboardSlice.ts`:
- Around line 403-408: The current eviction check for VgPlot panels only returns
true when patch.config has a top-level 'vgplot' property, which misses most
VgPlotChartConfig updates; change the condition in the
MOSAIC_DASHBOARD_VGPLOT_PANEL_TYPE branch to evict on any config change by
checking presence of patch.config (e.g., return Boolean(patch.config)) instead
of using Object.prototype.hasOwnProperty.call(patch.config, 'vgplot'), and
ensure references to VgPlotChartConfig and the patch handling logic in
MosaicDashboardSlice (the code that inspects panel.type ===
MOSAIC_DASHBOARD_VGPLOT_PANEL_TYPE) are updated accordingly.
---
Nitpick comments:
In `@packages/mosaic/__tests__/generateMosaicChartSpec.test.ts`:
- Around line 101-126: The test directly mutates mosaicChartTypes.histogram
which is fragile; instead spy on the registry entry and restore it reliably.
Replace the direct assignment to mosaicChartTypes.histogram with
jest.spyOn(mosaicChartTypes, 'histogram') (or spy on the createSpec method) to
mock createSpec to throw, call generateMosaicChartSpec('sales','histogram',
{...}) as before, and ensure you call mockRestore() in a finally block or in
afterEach so the original mosaicChartTypes.histogram is always restored;
reference the mosaicChartTypes.histogram symbol, the createSpec method,
generateMosaicChartSpec, jest.spyOn, and mockRestore when making the change.
In `@packages/mosaic/src/chart-builders/hooks/useChartFieldForm.ts`:
- Around line 15-20: The useCallback around handleFieldChange in
useChartFieldForm is ineffective because callers pass an inline unstable
onChange; remove the misleading memoization by replacing the useCallback wrapper
with a plain function export (i.e., change handleFieldChange in
useChartFieldForm to a direct function that calls onChange), or alternatively
stabilize the caller-side onChange (wrap the inline (key, value) =>
setFieldValue(...) in ChartBuilderFields.tsx with useCallback) if you want to
preserve the hook's memoization contract—pick one approach and apply
consistently to handleFieldChange and its caller(s).
In `@packages/mosaic/src/chart-types/count-plot/definition.ts`:
- Around line 9-15: BG_COLOR, FG_COLOR and titleFromDescription are duplicated;
extract them into a shared module (e.g., export constants BG_COLOR, FG_COLOR and
a function titleFromDescription from a new/shared helper like chartConstants or
chartTitleUtils) and replace the local definitions in both count-plot and
histogram definitions with imports of those symbols; update usages to reference
the imported BG_COLOR, FG_COLOR and titleFromDescription so both files reuse the
single implementation.
In `@packages/mosaic/src/chart-types/custom-spec/definition.ts`:
- Around line 6-9: The titleFromDescription function is duplicated; extract it
into the shared utility module chartTypeUtils (create and export a function
titleFromDescription(description: string): (fieldValues: Record<string,string>)
=> string that delegates to buildDefaultChartTitle), update both usages in
custom-spec/definition.ts and line-chart/definition.ts to import the shared
titleFromDescription from chartTypeUtils, and remove the duplicate definition
from each file so only the exported utility remains.
In `@packages/mosaic/src/chart-types/ecdf/definition.ts`:
- Around line 9-14: The FG_COLOR constant and titleFromDescription function are
duplicated across chart-type definitions; extract them into a new exported
helpers module (chartTypeUtils.ts), export FG_COLOR and titleFromDescription
(which should call buildDefaultChartTitle as before), then replace the local
declarations in each chart-type definition (ecdf, box-plot, heatmap, histogram)
with imports from chartTypeUtils.ts so all files use the single shared FG_COLOR
and titleFromDescription.
In `@packages/mosaic/src/chart-types/histogram/definition.ts`:
- Around line 9-15: BG_COLOR, FG_COLOR, and titleFromDescription are duplicated
across multiple chart definition files; move these shared helpers into
chartTypeUtils.ts and import them from there. Specifically, add BG_COLOR and
FG_COLOR constants and a titleFromDescription(description: string) curried
wrapper that calls buildDefaultChartTitle in
packages/mosaic/src/chart-builders/chartTypeUtils.ts, then remove the duplicated
definitions from histogram/definition.ts (and other chart definition files) and
replace them with imports of BG_COLOR, FG_COLOR, and titleFromDescription from
chartTypeUtils.ts.
In `@packages/mosaic/src/chart-types/line-chart/definition.ts`:
- Around line 12-15: The helper titleFromDescription is duplicated across
chart-type definitions; extract and export it from the shared chartTypeUtils
module where buildDefaultChartTitle already lives, then remove the duplicate
definitions. Specifically, add an exported function
titleFromDescription(description: string) that returns (fieldValues) =>
buildDefaultChartTitle(description, fieldValues) in the chartTypeUtils module,
and in each chart definition (e.g., the line-chart definition that currently
declares titleFromDescription) replace the local function with an import of the
shared titleFromDescription and remove the redundant code.
In `@packages/mosaic/src/dashboard/chart-settings/ChartSettings.tsx`:
- Around line 55-76: The handler handleChartTypeChange in
ChartSettingsTypeSelector should be memoized with React's useCallback to avoid
creating a new function reference each render; wrap the existing function in
useCallback and include the correct dependency array (at minimum onChange and
config.settingsOpen) so it only changes when those values change, ensure
useCallback is imported from React, and keep the function body unchanged (still
calling onChange to reset chartType, settings, vgplot, and settingsOpen) so
ChartTypeSelector receives a stable onChange prop.
In `@packages/mosaic/src/dashboard/chart-settings/ChartSettingsContext.tsx`:
- Around line 41-47: The Provider is passing an inline object to
ChartSettingsContext.Provider causing a new reference each render; wrap the
value in React.useMemo and return {tableName, config, columns, onChange} inside
it so the object identity is stable, and use [tableName, config, columns,
onChange] (or the minimal real dependencies) as the dependency array; update the
ChartSettingsContext.Provider to pass this memoizedValue instead of the inline
object so consumers only re-render when those inputs actually change.
In `@packages/mosaic/src/dashboard/chart-settings/DynamicChartSettings.tsx`:
- Around line 16-48: The inline onChange passed into useChartFieldForm inside
DynamicChartSettings creates a new function each render and defeats memoization;
wrap that callback in useCallback (and add useCallback to the imports) so the
function reference is stable before calling useChartFieldForm — e.g. create a
memoized const onFieldChange = useCallback((key, value) => onChange({...values,
[key]: value}), [onChange, values]) and pass onFieldChange to useChartFieldForm;
keep the rest (fields, handleFieldChange, FieldSelectorInput usage) unchanged.
In `@packages/mosaic/src/dashboard/MosaicDashboard.tsx`:
- Around line 106-124: The handleAddDefaultChart callback closes over the whole
dashboard object causing unnecessary re-creations; change its dependency array
to only include the specific values it reads (dashboard?.selectedTable,
addPanel, dashboardId) by replacing "dashboard" with "dashboard?.selectedTable"
so that handleAddDefaultChart only re-runs when the selected table changes; keep
the rest of the implementation the same and ensure references to
createMosaicDashboardVgPlotPanelConfig and addPanel remain unchanged.
In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx`:
- Around line 31-41: The onResize function is recreated each render causing
unstable props to ResizablePanel; wrap the handler in React.useCallback to
memoize it (referencing panelRef.current, isOpen, and onIsOpenChange) so its
identity remains stable across renders. Update the onResize declaration (used as
prop to ResizablePanel) to use useCallback and include only the necessary
dependencies (panelRef, isOpen, onIsOpenChange) to avoid stale closures while
preventing unnecessary re-renders.
In `@packages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsx`:
- Around line 18-34: Remove the unnecessary try-catch in toRenderableMosaicSpec:
keep the input validation (check for falsy, non-object, or array) and the
destructuring that strips $schema from the vgplotRecord, then directly return
mosaicSpec (typed as Spec | null). Update the function body in
MosaicDashboardVgPlotPanelRenderer.tsx to eliminate the try/catch block and
associated catch logging while preserving the early return of null on invalid
input and the cast to Spec for vgplotRecord.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cfb57297-cae8-41d5-8d87-a22ca10cd537
📒 Files selected for processing (68)
apps/sqlrooms-cli-ui/src/createDashboardChartTemplateTool.tsapps/sqlrooms-cli-ui/src/store.tsexamples/deckgl-mosaic/src/components/filters/FiltersPanel.tsxpackages/mosaic/__tests__/ChartSettings.test.tsxpackages/mosaic/__tests__/DashboardPanelErrorBoundary.test.tsxpackages/mosaic/__tests__/MosaicDashboardSlice.test.tspackages/mosaic/__tests__/generateMosaicChartSpec.test.tspackages/mosaic/src/MosaicChartBuilder.tspackages/mosaic/src/chart-builders/ChartBuilderActions.tsxpackages/mosaic/src/chart-builders/ChartBuilderContent.tsxpackages/mosaic/src/chart-builders/ChartBuilderContext.tsxpackages/mosaic/src/chart-builders/ChartBuilderDialog.tsxpackages/mosaic/src/chart-builders/ChartBuilderFields.tsxpackages/mosaic/src/chart-builders/ChartBuilderRoot.tsxpackages/mosaic/src/chart-builders/builders.tspackages/mosaic/src/chart-builders/chartTypeUtils.tspackages/mosaic/src/chart-builders/chartTypes.tspackages/mosaic/src/chart-builders/createChartBuilderStore.tspackages/mosaic/src/chart-builders/hooks/useChartFieldForm.tspackages/mosaic/src/chart-builders/types.tspackages/mosaic/src/chart-types/box-plot/definition.tspackages/mosaic/src/chart-types/box-plot/index.tspackages/mosaic/src/chart-types/box-plot/schema.tspackages/mosaic/src/chart-types/bubble-chart/definition.tspackages/mosaic/src/chart-types/bubble-chart/index.tspackages/mosaic/src/chart-types/bubble-chart/schema.tspackages/mosaic/src/chart-types/count-plot/definition.tspackages/mosaic/src/chart-types/count-plot/index.tspackages/mosaic/src/chart-types/count-plot/schema.tspackages/mosaic/src/chart-types/custom-spec/definition.tspackages/mosaic/src/chart-types/custom-spec/index.tspackages/mosaic/src/chart-types/custom-spec/schema.tspackages/mosaic/src/chart-types/ecdf/definition.tspackages/mosaic/src/chart-types/ecdf/index.tspackages/mosaic/src/chart-types/ecdf/schema.tspackages/mosaic/src/chart-types/heatmap/definition.tspackages/mosaic/src/chart-types/heatmap/index.tspackages/mosaic/src/chart-types/heatmap/schema.tspackages/mosaic/src/chart-types/histogram/definition.tspackages/mosaic/src/chart-types/histogram/index.tspackages/mosaic/src/chart-types/histogram/schema.tspackages/mosaic/src/chart-types/index.tspackages/mosaic/src/chart-types/line-chart/definition.tspackages/mosaic/src/chart-types/line-chart/index.tspackages/mosaic/src/chart-types/line-chart/schema.tspackages/mosaic/src/chart-types/registry.tspackages/mosaic/src/dashboard/DashboardPanelErrorBoundary.tsxpackages/mosaic/src/dashboard/MosaicDashboard.tsxpackages/mosaic/src/dashboard/MosaicDashboardContext.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanel.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsxpackages/mosaic/src/dashboard/MosaicDashboardProfilerPanelRenderer.tsxpackages/mosaic/src/dashboard/MosaicDashboardSlice.tspackages/mosaic/src/dashboard/MosaicDashboardToolbar.tsxpackages/mosaic/src/dashboard/MosaicDashboardVgPlotHeaderActions.tsxpackages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettings.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettingsContext.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettingsPanel.tsxpackages/mosaic/src/dashboard/chart-settings/ChartTypeSelector.tsxpackages/mosaic/src/dashboard/chart-settings/DynamicChartSettings.tsxpackages/mosaic/src/dashboard/chart-settings/index.tspackages/mosaic/src/dashboard/chart-settings/useTableColumns.tspackages/mosaic/src/dashboard/defaultPanelRenderers.tspackages/mosaic/src/dashboard/generateMosaicChartSpec.tspackages/mosaic/src/index.tspackages/ui/src/components/resizable.tsxpackages/ui/src/index.ts
💤 Files with no reviewable changes (1)
- packages/mosaic/src/chart-builders/chartTypes.ts
| createSpec: (tableName, {field}): Spec => | ||
| ({ | ||
| plot: [ | ||
| { | ||
| mark: 'areaY', | ||
| data: {from: tableName, filterBy: '$brush'}, | ||
| x: field, | ||
| y: {sum: field, cumulative: true}, | ||
| fill: FG_COLOR, | ||
| fillOpacity: 0.3, | ||
| }, | ||
| { | ||
| mark: 'lineY', | ||
| data: {from: tableName, filterBy: '$brush'}, | ||
| x: field, | ||
| y: {sum: field, cumulative: true}, | ||
| stroke: FG_COLOR, | ||
| }, | ||
| {select: 'intervalX', as: '$brush'}, | ||
| ], | ||
| xLabel: field, | ||
| yLabel: 'Cumulative', | ||
| height: 250, | ||
| width: 380, | ||
| margins: {left: 50, right: 20, top: 20, bottom: 50}, | ||
| params: {brush: {select: 'crossfilter'}}, | ||
| }) as Spec, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Mosaic vgplot spec y channel "count cumulative" eCDF example
💡 Result:
In Mosaic vgplot, the y channel supports expressions including aggregates like count and window functions for cumulative computations, enabling empirical Cumulative Distribution Function (eCDF) plots. No official built-in eCDF mark exists, but you can create one using a line or area mark with a custom SQL transform for cumulative proportions. To plot an eCDF: 1. Bin the data on x if needed (e.g., bin(column)). 2. Compute cumulative count using a window function: count() OVER (ORDER BY x_column ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW). 3. Normalize by total count for proportions: divide by count() OVER . 4. Use lineY or areaY mark with y set to this cumulative expression. Example JavaScript spec (assuming a table "data" with numeric column "value"): import * as vg from "@uwdata/vgplot"; vg.plot( vg.lineY( vg.from("data", { select: { x: "value", y_cum: "SUM(count) OVER (ORDER BY value ROWS UNBOUNDED PRECEDING) / SUM(count) OVER " }, groupBy: ["value"], aggregate: { count: "count(*)" } }), { x: "x", y: "y_cum" } ), vg.width(600), vg.height(400) ); This groups by binned/unique values, aggregates counts per bin, then computes the running cumulative proportion via window SUM (equivalent to cumulative count normalized). For YAML/JSON spec mode, use equivalent field expressions in the mark's data transform. Official docs confirm y channels accept aggregate/window expressions via Mosaic SQL (e.g., count, window functions like cumulative sums). DuckDB supports cume_dist directly for normalized eCDF values: cume_dist OVER (ORDER BY value). Use y: "cume_dist OVER (ORDER BY value)" for simpler proportion eCDF. Examples appear in Mosaic vgplot demos (e.g., histograms with y: count), extensible to cumulatives. Legacy Mosaic R has plotCumfreq for eCDFs, but vgplot is the modern JS grammar.
Citations:
- 1: https://uwdata.github.io/mosaic/vgplot/
- 2: https://idl.uw.edu/mosaic/api/vgplot/marks.html
- 3: https://cscheid.github.io/quarto-dashboard-ojs-examples/flights.html
- 4: https://uwdata.github.io/mosaic/api/vgplot/marks.html
- 5: https://uwdata.github.io/mosaic/api/sql/aggregate-functions.html
- 6: https://www.duckdb.org/docs/current/sql/functions/window_functions
🏁 Script executed:
# Read the ECDF definition file to confirm the current implementation
cat -n packages/mosaic/src/chart-types/ecdf/definition.tsRepository: sqlrooms/sqlrooms
Length of output: 2267
🏁 Script executed:
# Search for other eCDF references or tests in the codebase
rg "eCDF|cumulative.*count|ecdf" --type ts --type tsx -iRepository: sqlrooms/sqlrooms
Length of output: 89
🌐 Web query:
Observable Plot cumulative aggregation "sum" vs "count" semantics
💡 Result:
In Observable Plot (Plot.js), the “sum” vs “count” distinction is purely about which reducer you choose inside an aggregation transform. 1) group transform (aggregates categorical groups) - Reducer options include: - count: number of elements in the group - sum: sum of the specified input channel values - Semantics rule: if you specify reducer sum, but you do not bind that output channel to an input channel to sum, then Plot treats sum as equivalent to count. 2) bin transform (aggregates quantitative/temporal into bins) - Similar reducer options exist for binned data, including count and sum. 3) cumulative behavior - “cumulative” is not about sum vs count; it’s a separate option on the bin transform that makes bins accumulate across bin thresholds (cumulative distribution). Practical example for your “stacked bar totals at top” goal - To label the total at the top of each x-category’s stacked bar, you typically compute a per-x total using groupX with y: "sum" (summing the y input values for each x group), then use that aggregated total for the text mark. If you want, paste a small schema/sample of your tidy data (names of x, y, z and what columns contain), and I’ll show the exact Plot snippet tailored to your field names.
Citations:
- 1: https://observablehq.com/plot/transforms/group
- 2: https://observablehq.com/plot/transforms/bin
- 3: https://observablehq.com/plot/transforms/map
- 4: https://stackoverflow.com/questions/77688049/observable-plot-stacked-bar-chart-totals-sum-at-top-of-each-stacked-bar
- 5: https://talk.observablehq.com/t/observable-plot-stacked-bar-chart-totals-sum-at-top-of-each-stacked-bar/8636
🌐 Web query:
Observable Plot y-channel aggregation "sum" "count" cumulative bin transform semantics
💡 Result:
In Observable Plot, the bin transform (e.g., binX, binY) supports y-channel aggregations like "count" and "sum" via the outputs object, such as {y: "count"} or {y: "sum"}. The "count" reducer computes the number of elements (frequency) in each bin. The "sum" reducer computes the sum of values from the specified y input channel; if no y input is provided, "sum" behaves identically to "count". The cumulative option on the bin transform produces cumulative distributions by adjusting bin contents: if positive (e.g., true or 1), each bin includes all lesser bins (cumulative from low to high); if -1, each bin includes all greater bins (reverse cumulative). This affects aggregation: for {y: "count"}, y becomes the cumulative count up to that bin; for {y: "sum"}, y becomes the cumulative sum of the y values up to that bin (requiring a y input channel). Example for cumulative count histogram: Plot.rectY(data, Plot.binX({y: "count"}, {x: "weight", cumulative: true})) Example for per-bin sum (non-cumulative): Plot.binX({y: "sum"}, {x: "culmen_length_mm", y: "body_mass_g"}) For cumulative sum, combine with cumulative: true, ensuring y input is defined. Aggregations respect series (z, fill, stroke) subdivisions. These semantics apply similarly to hexbin and group transforms.
Citations:
- 1: https://observablehq.com/plot/transforms/hexbin
- 2: https://observablehq.com/plot/transforms/bin
- 3: https://observablehq.com/@observablehq/plot-cumulative-histogram
🏁 Script executed:
# Search for histogram definition to see how it handles cumulative
rg -A 10 "cumulative" packages/mosaic/src/chart-types/ --type tsRepository: sqlrooms/sqlrooms
Length of output: 3119
🏁 Script executed:
# Search for tests or examples related to eCDF
rg -i "ecdf\|empirical.*cumulative" --type tsRepository: sqlrooms/sqlrooms
Length of output: 43
🏁 Script executed:
# Look for Observable Plot or vgplot spec documentation references
rg "@uwdata/mosaic\|vgplot" packages/mosaic/src -l | head -5Repository: sqlrooms/sqlrooms
Length of output: 43
🏁 Script executed:
# Search for count aggregation usage in chart definitions
rg "count.*cumulative|y:.*count" packages/mosaic/src/chart-types/ --type ts -B 2 -A 2Repository: sqlrooms/sqlrooms
Length of output: 2328
🏁 Script executed:
# Look for histogram definition to see cumulative count pattern
fd definition.ts packages/mosaic/src/chart-types/histogram/ -x cat -n {}Repository: sqlrooms/sqlrooms
Length of output: 2349
🌐 Web query:
Mosaic vgplot "count" aggregation syntax y-channel specification
💡 Result:
In Mosaic vgplot, the "count" aggregation is specified in encoding channels like the y-channel using the syntax vg.count in JavaScript or {count: } (or {count: null}) in YAML/JSON specs. For a histogram with vertical bars (binned x, count on y), use rectY or barY marks: JavaScript example: plot( rectY(from("trips"), {x: bin("time"), y: count, fill: "steelblue"}), ... ) YAML/JSON equivalent: - mark: rectY data: {from: "trips"} x: {bin: "time"} y: {count: } fill: steelblue When aggregates like count are used, non-aggregate fields (e.g., the binned x) are automatically included in GROUP BY. This pushes aggregation to the database (e.g., DuckDB). For other channels (fill, r), count can similarly be used, e.g., in hexbin: {x: "i", y: "v", fill: count}. This follows Observable Plot semantics adapted for Mosaic SQL query generation.
Citations:
- 1: https://uwdata.github.io/mosaic/vgplot/
- 2: https://uwdata.github.io/mosaic/api/vgplot/marks.html
- 3: https://idl.cs.washington.edu/files/2024-Mosaic-TVCG.pdf
- 4: https://observablehq.observablehq.cloud/framework/lib/mosaic
- 5: https://idl.uw.edu/mosaic/examples/mark-types.html
- 6: https://idl.uw.edu/mosaic/examples/sorted-bars.html
y: {sum: field, cumulative: true} computes cumulative sum of values, not an eCDF.
An eCDF (Empirical Cumulative Distribution Function) maps each value x to the proportion (or count) of observations ≤ x. This requires cumulative count, not cumulative sum of the field. For data [1, 2, 3]:
{sum: field, cumulative: true}→ running total[1, 3, 6](wrong shape){count: null, cumulative: true}→[1, 2, 3]— the correct eCDF count shape
The current implementation will plot the wrong curve entirely. Fix both marks (lines 40 and 48) to use {count: null, cumulative: true}.
Proposed fix
- y: {sum: field, cumulative: true},
+ y: {count: null, cumulative: true},Apply identically to both the areaY mark (line 40) and the lineY mark (line 48).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| createSpec: (tableName, {field}): Spec => | |
| ({ | |
| plot: [ | |
| { | |
| mark: 'areaY', | |
| data: {from: tableName, filterBy: '$brush'}, | |
| x: field, | |
| y: {sum: field, cumulative: true}, | |
| fill: FG_COLOR, | |
| fillOpacity: 0.3, | |
| }, | |
| { | |
| mark: 'lineY', | |
| data: {from: tableName, filterBy: '$brush'}, | |
| x: field, | |
| y: {sum: field, cumulative: true}, | |
| stroke: FG_COLOR, | |
| }, | |
| {select: 'intervalX', as: '$brush'}, | |
| ], | |
| xLabel: field, | |
| yLabel: 'Cumulative', | |
| height: 250, | |
| width: 380, | |
| margins: {left: 50, right: 20, top: 20, bottom: 50}, | |
| params: {brush: {select: 'crossfilter'}}, | |
| }) as Spec, | |
| createSpec: (tableName, {field}): Spec => | |
| ({ | |
| plot: [ | |
| { | |
| mark: 'areaY', | |
| data: {from: tableName, filterBy: '$brush'}, | |
| x: field, | |
| y: {count: null, cumulative: true}, | |
| fill: FG_COLOR, | |
| fillOpacity: 0.3, | |
| }, | |
| { | |
| mark: 'lineY', | |
| data: {from: tableName, filterBy: '$brush'}, | |
| x: field, | |
| y: {count: null, cumulative: true}, | |
| stroke: FG_COLOR, | |
| }, | |
| {select: 'intervalX', as: '$brush'}, | |
| ], | |
| xLabel: field, | |
| yLabel: 'Cumulative', | |
| height: 250, | |
| width: 380, | |
| margins: {left: 50, right: 20, top: 20, bottom: 50}, | |
| params: {brush: {select: 'crossfilter'}}, | |
| }) as Spec, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/mosaic/src/chart-types/ecdf/definition.ts` around lines 33 - 59, The
y encoding currently uses a cumulative sum of the field which produces a running
total instead of an eCDF; in createSpec change the y encodings for both the
areaY and lineY marks to use cumulative counts by replacing y: {sum: field,
cumulative: true} with y: {count: null, cumulative: true} so the plot represents
the empirical cumulative distribution (apply the same change for the areaY and
lineY entries referencing field).
There was a problem hiding this comment.
@dmitriy-kostianetskiy to fix this in a separate PR
There was a problem hiding this comment.
@dmitriy-kostianetskiy, understood! Would you like me to open a GitHub issue to track this fix so it doesn't get lost? I can create one with a reference back to this PR and comment.
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx (2)
29-39: ⚡ Quick winMemoize
onResizewithuseCallbackto avoid passing a new function reference on every render.
onResizeis recreated on every render and passed as a prop toResizablePanel. Wrap it inuseCallbackwith[isOpen, onIsOpenChange]as dependencies.♻️ Proposed fix
-const onResize = () => { +const onResize = React.useCallback(() => { const isCollapsed = panelRef.current?.isCollapsed(); if (isCollapsed && isOpen) { onIsOpenChange?.(false); } if (!isCollapsed && !isOpen) { onIsOpenChange?.(true); } -}; +}, [isOpen, onIsOpenChange]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx` around lines 29 - 39, The onResize handler is recreated each render and should be memoized: wrap the onResize function in React's useCallback with dependencies [isOpen, onIsOpenChange] so the same function reference is passed to ResizablePanel; inside the memoized callback keep the existing logic that reads panelRef.current?.isCollapsed() and calls onIsOpenChange?.(false) or onIsOpenChange?.(true) based on isCollapsed and isOpen.
21-27: ⚡ Quick winGuard
expand()/collapse()with a current-state check to avoid redundant calls.The effect unconditionally calls
expand()orcollapse()even when the panel is already in the desired state. This can fireonResizeunnecessarily (e.g., when the user manually drags the panel, which already updatesisOpenviaonResize → onIsOpenChange, and then the effect re-runs and callscollapse()on an already-collapsed panel, triggeringonResizea second time). The safe pattern used elsewhere in the codebase guards on current state first:// Context: packages/layout/src/node-renderers/split-node-renderer/CollapsiblePanelWrapper.tsx useEffect(() => { if (collapsed && !handle.isCollapsed()) { handle.collapse(); } else if (!collapsed && handle.isCollapsed()) { handle.expand(); } }, [collapsed]);♻️ Proposed fix
useEffect(() => { - if (isOpen) { - panelRef.current?.expand(); - } else { - panelRef.current?.collapse(); + if (isOpen && panelRef.current?.isCollapsed()) { + panelRef.current.expand(); + } else if (!isOpen && !panelRef.current?.isCollapsed()) { + panelRef.current?.collapse(); } }, [isOpen]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx` around lines 21 - 27, The effect in MosaicDashboardPanelLayout.tsx currently calls panelRef.current?.expand() or ?.collapse() unconditionally; change it to first read the panel's current state (e.g., panelRef.current?.isCollapsed()) and only call expand() when isOpen is true and isCollapsed() is true, or call collapse() when isOpen is false and isCollapsed() is false, to avoid redundant calls and extra onResize triggers; update the useEffect that references isOpen and panelRef to perform that guarded check before invoking expand()/collapse().packages/mosaic/src/chart-builders/types.ts (1)
15-29: ⚡ Quick winPreserve
TSettingsthrough exported aliases/helpersThe new generic
ChartTypeDefinition<TSettings>is currently collapsed to defaultanyin this adapter layer. Threading generics here keeps settings typing intact for downstream callers.Suggested patch
-export interface ChartBuilderTemplate extends ChartTypeDefinition { +export interface ChartBuilderTemplate<TSettings = any> + extends ChartTypeDefinition<TSettings> { /** Icon component */ icon: ComponentType<{className?: string}>; } @@ -export type ChartSpec = ChartTypeDefinition; +export type ChartSpec<TSettings = any> = ChartTypeDefinition<TSettings>; @@ export function toChartTypeDefinition( - template: ChartBuilderTemplate, -): ChartTypeDefinition { + template: ChartBuilderTemplate<TSettings>, +): ChartTypeDefinition<TSettings> { @@ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/mosaic/src/chart-builders/types.ts` around lines 15 - 29, The exported types and helper should preserve the ChartTypeDefinition generic parameter so TSettings isn't lost: make ChartBuilderTemplate generic (ChartBuilderTemplate<TSettings> extends ChartTypeDefinition<TSettings>) and update ChartSpec to be a generic alias (ChartSpec<TSettings> = ChartTypeDefinition<TSettings>), then change toChartTypeDefinition to accept template: ChartBuilderTemplate<TSettings> and return ChartTypeDefinition<TSettings>; update any internal references to use the generic TSettings so downstream callers keep the settings type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/mosaic/__tests__/ChartSettings.test.tsx`:
- Around line 45-68: The test leaves console.error mocked on failure because
consoleSpy.mockRestore() is only called on the success path; wrap the assertions
and render in a try/finally that always calls consoleSpy.mockRestore(), or add
global cleanup via afterEach(() => jest.restoreAllMocks()) so console.error is
always restored; update the test that uses consoleSpy and ChartSettings.Root /
ChartSettings.Fields to ensure consoleSpy.mockRestore() is executed in finally
(or use jest.restoreAllMocks() in beforeEach/afterEach).
In `@packages/mosaic/src/chart-types/box-plot/definition.ts`:
- Around line 11-14: Extract the duplicated helper into chart-builders/constants
by adding and exporting titleFromDescription, then in each chart definition
(box-plot/definition.ts, heatmap/definition.ts, ecdf/definition.ts,
line-chart/definition.ts, count-plot/definition.ts) remove the local
titleFromDescription function and import the shared titleFromDescription and
buildDefaultChartTitle; to eliminate the duplicated literal description string
in each file, assign the description to a const (e.g., const description = "…")
and use that const for both the object’s description field and for title:
titleFromDescription(description).
In `@packages/mosaic/src/dashboard/chart-settings/ChartSettings.tsx`:
- Around line 65-73: The header always renders the close Button even when
onClose is undefined; update ChartSettings.Header to only render the Button (and
XIcon) when the onClose prop is provided—i.e., guard the Button with a
conditional check on onClose so the close control is not shown if no handler
exists (refer to ChartSettings.Header, onClose, Button, and XIcon to locate the
JSX to change).
- Around line 117-122: The required-field check in ChartSettings.tsx (variable
allRequiredFieldsFilled iterating chartTypeDef.fields) treats undefined as not
required; change the predicate to treat a field as required unless
field.required === false (i.e., use field.required !== false) so it matches
canCreateChartFromType/isChartTypeAvailable and
ChartSettingsFields.handleSettingsChange behavior; ensure the value presence
check (value !== undefined && value !== null && value !== '') remains the same
for those fields.
---
Nitpick comments:
In `@packages/mosaic/src/chart-builders/types.ts`:
- Around line 15-29: The exported types and helper should preserve the
ChartTypeDefinition generic parameter so TSettings isn't lost: make
ChartBuilderTemplate generic (ChartBuilderTemplate<TSettings> extends
ChartTypeDefinition<TSettings>) and update ChartSpec to be a generic alias
(ChartSpec<TSettings> = ChartTypeDefinition<TSettings>), then change
toChartTypeDefinition to accept template: ChartBuilderTemplate<TSettings> and
return ChartTypeDefinition<TSettings>; update any internal references to use the
generic TSettings so downstream callers keep the settings type.
In `@packages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsx`:
- Around line 29-39: The onResize handler is recreated each render and should be
memoized: wrap the onResize function in React's useCallback with dependencies
[isOpen, onIsOpenChange] so the same function reference is passed to
ResizablePanel; inside the memoized callback keep the existing logic that reads
panelRef.current?.isCollapsed() and calls onIsOpenChange?.(false) or
onIsOpenChange?.(true) based on isCollapsed and isOpen.
- Around line 21-27: The effect in MosaicDashboardPanelLayout.tsx currently
calls panelRef.current?.expand() or ?.collapse() unconditionally; change it to
first read the panel's current state (e.g., panelRef.current?.isCollapsed()) and
only call expand() when isOpen is true and isCollapsed() is true, or call
collapse() when isOpen is false and isCollapsed() is false, to avoid redundant
calls and extra onResize triggers; update the useEffect that references isOpen
and panelRef to perform that guarded check before invoking expand()/collapse().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1b2fb603-6875-4610-afa8-e1bbea409ff9
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (27)
apps/sqlrooms-cli-ui/src/store.tspackages/mosaic/__tests__/ChartSettings.test.tsxpackages/mosaic/__tests__/MosaicDashboardSlice.test.tspackages/mosaic/__tests__/MosaicProfilerStatusBar.test.tsxpackages/mosaic/__tests__/generateMosaicChartSpec.test.tspackages/mosaic/package.jsonpackages/mosaic/src/chart-builders/chartTypeUtils.tspackages/mosaic/src/chart-builders/constants.tspackages/mosaic/src/chart-builders/types.tspackages/mosaic/src/chart-types/base-types.tspackages/mosaic/src/chart-types/box-plot/definition.tspackages/mosaic/src/chart-types/bubble-chart/definition.tspackages/mosaic/src/chart-types/count-plot/definition.tspackages/mosaic/src/chart-types/custom-spec/definition.tspackages/mosaic/src/chart-types/ecdf/definition.tspackages/mosaic/src/chart-types/heatmap/definition.tspackages/mosaic/src/chart-types/histogram/definition.tspackages/mosaic/src/chart-types/index.tspackages/mosaic/src/chart-types/line-chart/definition.tspackages/mosaic/src/chart-types/registry.tspackages/mosaic/src/dashboard/MosaicDashboardPanelLayout.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettings.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettingsPanel.tsxpackages/mosaic/src/dashboard/chart-settings/ChartTypeSelector.tsxpackages/mosaic/src/dashboard/generateMosaicChartSpec.tspackages/mosaic/test/setup.tspackages/mosaic/tsconfig.json
✅ Files skipped from review due to trivial changes (5)
- packages/mosaic/tsconfig.json
- packages/mosaic/test/setup.ts
- packages/mosaic/tests/generateMosaicChartSpec.test.ts
- packages/mosaic/src/chart-builders/constants.ts
- packages/mosaic/package.json
🚧 Files skipped from review as they are similar to previous changes (10)
- packages/mosaic/src/chart-types/registry.ts
- packages/mosaic/tests/MosaicDashboardSlice.test.ts
- packages/mosaic/src/chart-types/histogram/definition.ts
- packages/mosaic/src/chart-types/index.ts
- packages/mosaic/src/dashboard/generateMosaicChartSpec.ts
- packages/mosaic/src/dashboard/chart-settings/ChartSettingsPanel.tsx
- packages/mosaic/src/chart-types/bubble-chart/definition.ts
- packages/mosaic/src/dashboard/chart-settings/ChartTypeSelector.tsx
- packages/mosaic/src/chart-types/custom-spec/definition.ts
- apps/sqlrooms-cli-ui/src/store.ts
| .mosaicDashboard.getDashboard(artifactId) | ||
| ?.panels.find((panel) => panel.type === 'vgplot')?.config.vgplot; | ||
| ?.panels.find( | ||
| (panel): panel is VgPlotPanelConfig => panel.type === 'vgplot', |
There was a problem hiding this comment.
nit: maybe extract a type guard function?
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/sqlrooms-cli-ui/src/store.ts (1)
339-357:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse the same
isVgPlotPanelConfigguard in the update path.Line 340 narrows only by
panel.type === 'vgplot', then mutatesprimaryPanel.config. That can break on legacy/stale persisted panel configs that still havetype: 'vgplot'but no longer match current config shape. Reusing the guard here makes update behavior consistent with Line 364 and avoids parse-time failures.🔧 Minimal fix
- const primaryPanel = dashboard?.panels.find( - (panel) => panel.type === 'vgplot', - ); + const primaryPanel = dashboard?.panels.find(isVgPlotPanelConfig);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/sqlrooms-cli-ui/src/store.ts` around lines 339 - 357, The update path currently finds primaryPanel by panel.type === 'vgplot' and directly spreads primaryPanel.config; change it to first check isVgPlotPanelConfig(primaryPanel?.config) before calling get().mosaicDashboard.updatePanel(artifactId, primaryPanel.id, ...). If the guard fails, fall back to addPanel as the code already does; ensure you reference primaryPanel, isVgPlotPanelConfig, get().mosaicDashboard.updatePanel, artifactId and vgplot when making the conditional so the update only runs when the config shape matches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/mosaic/src/dashboard/MosaicDashboardVgPlotHeaderActions.tsx`:
- Around line 44-53: The icon-only settings Button lacks an explicit accessible
name and state; update the Button (the JSX using SettingsIcon and
onClick={handleToggleSettings}) to include an aria-label like "Chart settings"
and add aria-pressed={isSettingsOpen} so screen readers announce the control and
its toggle state (leave title if desired but ensure aria-label and aria-pressed
are present).
---
Outside diff comments:
In `@apps/sqlrooms-cli-ui/src/store.ts`:
- Around line 339-357: The update path currently finds primaryPanel by
panel.type === 'vgplot' and directly spreads primaryPanel.config; change it to
first check isVgPlotPanelConfig(primaryPanel?.config) before calling
get().mosaicDashboard.updatePanel(artifactId, primaryPanel.id, ...). If the
guard fails, fall back to addPanel as the code already does; ensure you
reference primaryPanel, isVgPlotPanelConfig, get().mosaicDashboard.updatePanel,
artifactId and vgplot when making the conditional so the update only runs when
the config shape matches.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f37a8d0a-1c68-4781-94b1-c9494129b062
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (22)
apps/sqlrooms-cli-ui/src/createDashboardChartTemplateTool.tsapps/sqlrooms-cli-ui/src/store.tsexamples/deckgl-mosaic/src/components/filters/FiltersPanel.tsxpackages/mosaic/__tests__/ChartSettings.test.tsxpackages/mosaic/__tests__/MosaicDashboardSlice.test.tspackages/mosaic/src/chart-builders/FieldSelectorInput.tsxpackages/mosaic/src/chart-builders/chartTypeUtils.tspackages/mosaic/src/chart-builders/constants.tspackages/mosaic/src/chart-types/box-plot/definition.tspackages/mosaic/src/chart-types/bubble-chart/definition.tspackages/mosaic/src/chart-types/count-plot/definition.tspackages/mosaic/src/chart-types/custom-spec/definition.tspackages/mosaic/src/chart-types/ecdf/definition.tspackages/mosaic/src/chart-types/heatmap/definition.tspackages/mosaic/src/chart-types/histogram/definition.tspackages/mosaic/src/chart-types/line-chart/definition.tspackages/mosaic/src/dashboard/MosaicDashboardSlice.tspackages/mosaic/src/dashboard/MosaicDashboardVgPlotHeaderActions.tsxpackages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsxpackages/mosaic/src/dashboard/chart-settings/ChartSettings.tsxpackages/mosaic/src/dashboard/chart-settings/ChartTypeSelector.tsxpackages/mosaic/src/index.ts
✅ Files skipped from review due to trivial changes (3)
- packages/mosaic/src/chart-builders/FieldSelectorInput.tsx
- packages/mosaic/src/chart-builders/constants.ts
- packages/mosaic/src/chart-types/ecdf/definition.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- packages/mosaic/tests/MosaicDashboardSlice.test.ts
- packages/mosaic/tests/ChartSettings.test.tsx
- packages/mosaic/src/chart-types/heatmap/definition.ts
- examples/deckgl-mosaic/src/components/filters/FiltersPanel.tsx
- packages/mosaic/src/dashboard/chart-settings/ChartTypeSelector.tsx
- packages/mosaic/src/dashboard/MosaicDashboardVgPlotPanelRenderer.tsx
- packages/mosaic/src/chart-types/count-plot/definition.ts
- packages/mosaic/src/dashboard/chart-settings/ChartSettings.tsx
- packages/mosaic/src/dashboard/MosaicDashboardSlice.ts
- packages/mosaic/src/index.ts
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="data-[state=active]:bg-accent h-6 w-6" | ||
| title="Chart settings" | ||
| onClick={handleToggleSettings} | ||
| data-state={isSettingsOpen ? 'active' : 'inactive'} | ||
| > | ||
| <SettingsIcon className="h-3.5 w-3.5" /> | ||
| </Button> |
There was a problem hiding this comment.
Add an explicit accessible name to the icon-only settings button.
The button is icon-only and currently relies on title. Add aria-label (and optionally aria-pressed since this is a toggle) so screen readers can reliably announce and track state.
Suggested change
<Button
variant="ghost"
size="icon"
className="data-[state=active]:bg-accent h-6 w-6"
title="Chart settings"
+ aria-label="Chart settings"
+ aria-pressed={isSettingsOpen}
onClick={handleToggleSettings}
data-state={isSettingsOpen ? 'active' : 'inactive'}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="data-[state=active]:bg-accent h-6 w-6" | |
| title="Chart settings" | |
| onClick={handleToggleSettings} | |
| data-state={isSettingsOpen ? 'active' : 'inactive'} | |
| > | |
| <SettingsIcon className="h-3.5 w-3.5" /> | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| className="data-[state=active]:bg-accent h-6 w-6" | |
| title="Chart settings" | |
| aria-label="Chart settings" | |
| aria-pressed={isSettingsOpen} | |
| onClick={handleToggleSettings} | |
| data-state={isSettingsOpen ? 'active' : 'inactive'} | |
| > | |
| <SettingsIcon className="h-3.5 w-3.5" /> | |
| </Button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/mosaic/src/dashboard/MosaicDashboardVgPlotHeaderActions.tsx` around
lines 44 - 53, The icon-only settings Button lacks an explicit accessible name
and state; update the Button (the JSX using SettingsIcon and
onClick={handleToggleSettings}) to include an aria-label like "Chart settings"
and add aria-pressed={isSettingsOpen} so screen readers announce the control and
its toggle state (leave title if desired but ensure aria-label and aria-pressed
are present).
Screen.Recording.2026-05-04.at.14.56.51.mov
Summary by CodeRabbit
New Features
Bug Fixes
Improvements