@carto/ps-react-ui 4.7.1 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/category-DwaeYjpX.js +656 -0
- package/dist/category-DwaeYjpX.js.map +1 -0
- package/dist/change-column-Cidl_M-4.js +1110 -0
- package/dist/change-column-Cidl_M-4.js.map +1 -0
- package/dist/data-zoom-layout-BH0LPwSy.js +28 -0
- package/dist/data-zoom-layout-BH0LPwSy.js.map +1 -0
- package/dist/echart-CU0KmClP.js +176 -0
- package/dist/echart-CU0KmClP.js.map +1 -0
- package/dist/exports-Cx-f6m6U.js +63 -0
- package/dist/exports-Cx-f6m6U.js.map +1 -0
- package/dist/formula-DuC0NQLH.js +79 -0
- package/dist/formula-DuC0NQLH.js.map +1 -0
- package/dist/markdown-BD1jcknS.js +8326 -0
- package/dist/markdown-BD1jcknS.js.map +1 -0
- package/dist/merge-options-DCkkHZIf.js +34 -0
- package/dist/merge-options-DCkkHZIf.js.map +1 -0
- package/dist/{styles-BYTyKQFP.js → option-builders-F-c9ELi1.js} +25 -45
- package/dist/option-builders-F-c9ELi1.js.map +1 -0
- package/dist/png-item-CS4z1iSH.js +45 -0
- package/dist/png-item-CS4z1iSH.js.map +1 -0
- package/dist/range-DsqTjSpg.js +186 -0
- package/dist/range-DsqTjSpg.js.map +1 -0
- package/dist/spread-CTuIXZSM.js +67 -0
- package/dist/spread-CTuIXZSM.js.map +1 -0
- package/dist/style-DVnT6HC1.js +131 -0
- package/dist/style-DVnT6HC1.js.map +1 -0
- package/dist/styles-cohnxh9F.js +23 -0
- package/dist/styles-cohnxh9F.js.map +1 -0
- package/dist/table-HIpXuq4G.js +390 -0
- package/dist/table-HIpXuq4G.js.map +1 -0
- package/dist/transforms-Cdx4fkU5.js +106 -0
- package/dist/transforms-Cdx4fkU5.js.map +1 -0
- package/dist/types/widgets/echart/utils.test.d.ts +1 -0
- package/dist/types/widgets/formula/config.test.d.ts +1 -0
- package/dist/types/widgets/stores/widget-store-branches.test.d.ts +1 -0
- package/dist/types/widgets/table/config.test.d.ts +1 -0
- package/dist/types/widgets-v2/actions/brush-toggle/brush-toggle.d.ts +56 -0
- package/dist/types/widgets-v2/actions/brush-toggle/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/brush-toggle/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/brush-toggle/style.d.ts +12 -0
- package/dist/types/widgets-v2/actions/brush-toggle/transforms.d.ts +11 -0
- package/dist/types/widgets-v2/actions/brush-toggle/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/actions/change-column/change-column-icon.d.ts +2 -0
- package/dist/types/widgets-v2/actions/change-column/change-column.d.ts +29 -0
- package/dist/types/widgets-v2/actions/change-column/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/change-column/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/change-column/sortable-column-item.d.ts +14 -0
- package/dist/types/widgets-v2/actions/change-column/style.d.ts +33 -0
- package/dist/types/widgets-v2/actions/change-column/types.d.ts +10 -0
- package/dist/types/widgets-v2/actions/download/download.d.ts +18 -0
- package/dist/types/widgets-v2/actions/download/exports.d.ts +37 -0
- package/dist/types/widgets-v2/actions/download/icons.d.ts +12 -0
- package/dist/types/widgets-v2/actions/download/index.d.ts +6 -0
- package/dist/types/widgets-v2/actions/download/labels.d.ts +11 -0
- package/dist/types/widgets-v2/actions/download/png-item.d.ts +24 -0
- package/dist/types/widgets-v2/actions/download/style.d.ts +1 -0
- package/dist/types/widgets-v2/actions/download/types.d.ts +35 -0
- package/dist/types/widgets-v2/actions/fullscreen/fullscreen.d.ts +59 -0
- package/dist/types/widgets-v2/actions/fullscreen/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/fullscreen/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/fullscreen/style.d.ts +48 -0
- package/dist/types/widgets-v2/actions/fullscreen/types.d.ts +14 -0
- package/dist/types/widgets-v2/actions/index.d.ts +9 -0
- package/dist/types/widgets-v2/actions/lock-selection/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/lock-selection/labels.d.ts +6 -0
- package/dist/types/widgets-v2/actions/lock-selection/lock-selection.d.ts +36 -0
- package/dist/types/widgets-v2/actions/lock-selection/style.d.ts +12 -0
- package/dist/types/widgets-v2/actions/lock-selection/transforms.d.ts +6 -0
- package/dist/types/widgets-v2/actions/relative-data/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/relative-data/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/relative-data/relative-data.d.ts +39 -0
- package/dist/types/widgets-v2/actions/relative-data/style.d.ts +12 -0
- package/dist/types/widgets-v2/actions/relative-data/transforms.d.ts +30 -0
- package/dist/types/widgets-v2/actions/relative-data/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/actions/searcher/filter.d.ts +6 -0
- package/dist/types/widgets-v2/actions/searcher/index.d.ts +4 -0
- package/dist/types/widgets-v2/actions/searcher/labels.d.ts +7 -0
- package/dist/types/widgets-v2/actions/searcher/searcher-toggle.d.ts +23 -0
- package/dist/types/widgets-v2/actions/searcher/searcher.d.ts +11 -0
- package/dist/types/widgets-v2/actions/searcher/style.d.ts +16 -0
- package/dist/types/widgets-v2/actions/stack-toggle/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/stack-toggle/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/stack-toggle/stack-toggle.d.ts +10 -0
- package/dist/types/widgets-v2/actions/stack-toggle/style.d.ts +12 -0
- package/dist/types/widgets-v2/actions/stack-toggle/transforms.d.ts +13 -0
- package/dist/types/widgets-v2/actions/stack-toggle/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/index.d.ts +3 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/style.d.ts +12 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/transforms.d.ts +51 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/actions/zoom-toggle/zoom-toggle.d.ts +35 -0
- package/dist/types/widgets-v2/bar/download.d.ts +24 -0
- package/dist/types/widgets-v2/bar/index.d.ts +4 -0
- package/dist/types/widgets-v2/bar/options.d.ts +43 -0
- package/dist/types/widgets-v2/bar/options.test.d.ts +1 -0
- package/dist/types/widgets-v2/bar/skeleton.d.ts +6 -0
- package/dist/types/widgets-v2/bar/types.d.ts +41 -0
- package/dist/types/widgets-v2/category/category-ui.d.ts +81 -0
- package/dist/types/widgets-v2/category/category.d.ts +48 -0
- package/dist/types/widgets-v2/category/components/category-bar-stacked.d.ts +28 -0
- package/dist/types/widgets-v2/category/components/category-bar.d.ts +23 -0
- package/dist/types/widgets-v2/category/components/category-legend.d.ts +18 -0
- package/dist/types/widgets-v2/category/components/category-row-multi.d.ts +31 -0
- package/dist/types/widgets-v2/category/components/category-row-other.d.ts +13 -0
- package/dist/types/widgets-v2/category/components/category-row-single.d.ts +28 -0
- package/dist/types/widgets-v2/category/components/category-row-stacked.d.ts +38 -0
- package/dist/types/widgets-v2/category/download.d.ts +16 -0
- package/dist/types/widgets-v2/category/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/category/index.d.ts +10 -0
- package/dist/types/widgets-v2/category/skeleton.d.ts +11 -0
- package/dist/types/widgets-v2/category/style.d.ts +166 -0
- package/dist/types/widgets-v2/category/types.d.ts +49 -0
- package/dist/types/widgets-v2/echart/echart-ui.d.ts +44 -0
- package/dist/types/widgets-v2/echart/echart.d.ts +75 -0
- package/dist/types/widgets-v2/echart/index.d.ts +4 -0
- package/dist/types/widgets-v2/echart/shared-resize-observer.d.ts +5 -0
- package/dist/types/widgets-v2/echart/shared-resize-observer.test.d.ts +1 -0
- package/dist/types/widgets-v2/echart/style.d.ts +6 -0
- package/dist/types/widgets-v2/echart/use-chart-selection.d.ts +51 -0
- package/dist/types/widgets-v2/formula/delta.d.ts +22 -0
- package/dist/types/widgets-v2/formula/download.d.ts +20 -0
- package/dist/types/widgets-v2/formula/formula-ui.d.ts +20 -0
- package/dist/types/widgets-v2/formula/formula.d.ts +8 -0
- package/dist/types/widgets-v2/formula/index.d.ts +11 -0
- package/dist/types/widgets-v2/formula/note.d.ts +11 -0
- package/dist/types/widgets-v2/formula/prefix.d.ts +12 -0
- package/dist/types/widgets-v2/formula/series.d.ts +16 -0
- package/dist/types/widgets-v2/formula/skeleton.d.ts +4 -0
- package/dist/types/widgets-v2/formula/style.d.ts +29 -0
- package/dist/types/widgets-v2/formula/suffix.d.ts +12 -0
- package/dist/types/widgets-v2/formula/types.d.ts +40 -0
- package/dist/types/widgets-v2/formula/value.d.ts +14 -0
- package/dist/types/widgets-v2/histogram/download.d.ts +17 -0
- package/dist/types/widgets-v2/histogram/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/histogram/index.d.ts +5 -0
- package/dist/types/widgets-v2/histogram/options.d.ts +42 -0
- package/dist/types/widgets-v2/histogram/options.test.d.ts +1 -0
- package/dist/types/widgets-v2/histogram/skeleton.d.ts +9 -0
- package/dist/types/widgets-v2/histogram/transforms.d.ts +17 -0
- package/dist/types/widgets-v2/histogram/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/histogram/types.d.ts +47 -0
- package/dist/types/widgets-v2/index.d.ts +107 -0
- package/dist/types/widgets-v2/markdown/download.d.ts +16 -0
- package/dist/types/widgets-v2/markdown/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/markdown/index.d.ts +6 -0
- package/dist/types/widgets-v2/markdown/markdown-content.d.ts +34 -0
- package/dist/types/widgets-v2/markdown/markdown-ui.d.ts +12 -0
- package/dist/types/widgets-v2/markdown/markdown.d.ts +6 -0
- package/dist/types/widgets-v2/markdown/skeleton.d.ts +4 -0
- package/dist/types/widgets-v2/markdown/style.d.ts +61 -0
- package/dist/types/widgets-v2/markdown/types.d.ts +4 -0
- package/dist/types/widgets-v2/note/labels.d.ts +5 -0
- package/dist/types/widgets-v2/note/style.d.ts +26 -0
- package/dist/types/widgets-v2/note/widget-note.d.ts +46 -0
- package/dist/types/widgets-v2/pie/download.d.ts +17 -0
- package/dist/types/widgets-v2/pie/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/pie/index.d.ts +4 -0
- package/dist/types/widgets-v2/pie/options.d.ts +35 -0
- package/dist/types/widgets-v2/pie/options.test.d.ts +1 -0
- package/dist/types/widgets-v2/pie/skeleton.d.ts +4 -0
- package/dist/types/widgets-v2/pie/types.d.ts +50 -0
- package/dist/types/widgets-v2/provider/widget-provider.d.ts +32 -0
- package/dist/types/widgets-v2/range/index.d.ts +4 -0
- package/dist/types/widgets-v2/range/range-ui.d.ts +19 -0
- package/dist/types/widgets-v2/range/range.d.ts +19 -0
- package/dist/types/widgets-v2/range/skeleton.d.ts +9 -0
- package/dist/types/widgets-v2/range/style.d.ts +40 -0
- package/dist/types/widgets-v2/range/types.d.ts +37 -0
- package/dist/types/widgets-v2/scatterplot/download.d.ts +16 -0
- package/dist/types/widgets-v2/scatterplot/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/scatterplot/index.d.ts +5 -0
- package/dist/types/widgets-v2/scatterplot/options.d.ts +42 -0
- package/dist/types/widgets-v2/scatterplot/options.test.d.ts +1 -0
- package/dist/types/widgets-v2/scatterplot/skeleton.d.ts +12 -0
- package/dist/types/widgets-v2/scatterplot/transforms.d.ts +17 -0
- package/dist/types/widgets-v2/scatterplot/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/scatterplot/types.d.ts +50 -0
- package/dist/types/widgets-v2/selection-summary/labels.d.ts +6 -0
- package/dist/types/widgets-v2/selection-summary/selection-summary.d.ts +22 -0
- package/dist/types/widgets-v2/selection-summary/style.d.ts +23 -0
- package/dist/types/widgets-v2/spread/download.d.ts +15 -0
- package/dist/types/widgets-v2/spread/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/spread/index.d.ts +6 -0
- package/dist/types/widgets-v2/spread/separator.d.ts +7 -0
- package/dist/types/widgets-v2/spread/skeleton.d.ts +9 -0
- package/dist/types/widgets-v2/spread/spread-ui.d.ts +18 -0
- package/dist/types/widgets-v2/spread/spread.d.ts +5 -0
- package/dist/types/widgets-v2/spread/types.d.ts +25 -0
- package/dist/types/widgets-v2/state/labels.d.ts +7 -0
- package/dist/types/widgets-v2/state/labels.test.d.ts +1 -0
- package/dist/types/widgets-v2/state/style.d.ts +19 -0
- package/dist/types/widgets-v2/state/widget-state.d.ts +19 -0
- package/dist/types/widgets-v2/stores/index.d.ts +8 -0
- package/dist/types/widgets-v2/stores/pipeline-middleware.d.ts +5 -0
- package/dist/types/widgets-v2/stores/pipeline-middleware.test.d.ts +1 -0
- package/dist/types/widgets-v2/stores/transforms.d.ts +4 -0
- package/dist/types/widgets-v2/stores/transforms.test.d.ts +1 -0
- package/dist/types/widgets-v2/stores/types.d.ts +55 -0
- package/dist/types/widgets-v2/stores/use-echart-instance.d.ts +15 -0
- package/dist/types/widgets-v2/stores/use-transform-enabled.d.ts +17 -0
- package/dist/types/widgets-v2/stores/use-transform.d.ts +12 -0
- package/dist/types/widgets-v2/stores/widget-context.d.ts +2 -0
- package/dist/types/widgets-v2/stores/widget-store-registry.d.ts +74 -0
- package/dist/types/widgets-v2/stores/widget-store-registry.test.d.ts +1 -0
- package/dist/types/widgets-v2/subheader/style.d.ts +10 -0
- package/dist/types/widgets-v2/subheader/subheader.d.ts +11 -0
- package/dist/types/widgets-v2/table/download.d.ts +18 -0
- package/dist/types/widgets-v2/table/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/table/helpers.d.ts +32 -0
- package/dist/types/widgets-v2/table/helpers.test.d.ts +1 -0
- package/dist/types/widgets-v2/table/index.d.ts +7 -0
- package/dist/types/widgets-v2/table/labels.d.ts +22 -0
- package/dist/types/widgets-v2/table/skeleton.d.ts +22 -0
- package/dist/types/widgets-v2/table/style.d.ts +44 -0
- package/dist/types/widgets-v2/table/table-ui.d.ts +38 -0
- package/dist/types/widgets-v2/table/table.d.ts +50 -0
- package/dist/types/widgets-v2/table/types.d.ts +37 -0
- package/dist/types/widgets-v2/test-utils.d.ts +52 -0
- package/dist/types/widgets-v2/timeseries/download.d.ts +17 -0
- package/dist/types/widgets-v2/timeseries/download.test.d.ts +1 -0
- package/dist/types/widgets-v2/timeseries/index.d.ts +4 -0
- package/dist/types/widgets-v2/timeseries/options.d.ts +39 -0
- package/dist/types/widgets-v2/timeseries/options.test.d.ts +1 -0
- package/dist/types/widgets-v2/timeseries/skeleton.d.ts +8 -0
- package/dist/types/widgets-v2/timeseries/types.d.ts +56 -0
- package/dist/types/widgets-v2/toolbox/labels.d.ts +5 -0
- package/dist/types/widgets-v2/toolbox/style.d.ts +30 -0
- package/dist/types/widgets-v2/toolbox/toolbox.d.ts +49 -0
- package/dist/types/widgets-v2/utils/data-zoom-layout.d.ts +11 -0
- package/dist/types/widgets-v2/utils/index.d.ts +2 -0
- package/dist/types/widgets-v2/utils/merge-options.d.ts +12 -0
- package/dist/types/widgets-v2/utils/merge-options.test.d.ts +1 -0
- package/dist/types/widgets-v2/wrapper/index.d.ts +4 -0
- package/dist/types/widgets-v2/wrapper/labels.d.ts +6 -0
- package/dist/types/widgets-v2/wrapper/style.d.ts +111 -0
- package/dist/types/widgets-v2/wrapper/widget-actions.d.ts +22 -0
- package/dist/types/widgets-v2/wrapper/widget-content.d.ts +12 -0
- package/dist/types/widgets-v2/wrapper/widget-wrapper.d.ts +51 -0
- package/dist/use-transform-DXPN3nY7.js +110 -0
- package/dist/use-transform-DXPN3nY7.js.map +1 -0
- package/dist/widget-context-DTGO0Yta.js +13 -0
- package/dist/widget-context-DTGO0Yta.js.map +1 -0
- package/dist/widget-store-registry-_W4Z4xp-.js +178 -0
- package/dist/widget-store-registry-_W4Z4xp-.js.map +1 -0
- package/dist/widgets/bar.js +14 -13
- package/dist/widgets/bar.js.map +1 -1
- package/dist/widgets/histogram.js +8 -7
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/pie.js +19 -18
- package/dist/widgets/pie.js.map +1 -1
- package/dist/widgets/scatterplot.js +8 -7
- package/dist/widgets/scatterplot.js.map +1 -1
- package/dist/widgets/timeseries.js +11 -10
- package/dist/widgets/timeseries.js.map +1 -1
- package/dist/widgets/utils.js +8 -7
- package/dist/widgets/utils.js.map +1 -1
- package/dist/widgets-v2/actions.js +43 -0
- package/dist/widgets-v2/actions.js.map +1 -0
- package/dist/widgets-v2/bar.js +327 -0
- package/dist/widgets-v2/bar.js.map +1 -0
- package/dist/widgets-v2/category.js +104 -0
- package/dist/widgets-v2/category.js.map +1 -0
- package/dist/widgets-v2/echart.js +57 -0
- package/dist/widgets-v2/echart.js.map +1 -0
- package/dist/widgets-v2/formula.js +74 -0
- package/dist/widgets-v2/formula.js.map +1 -0
- package/dist/widgets-v2/histogram.js +350 -0
- package/dist/widgets-v2/histogram.js.map +1 -0
- package/dist/widgets-v2/markdown.js +68 -0
- package/dist/widgets-v2/markdown.js.map +1 -0
- package/dist/widgets-v2/pie.js +381 -0
- package/dist/widgets-v2/pie.js.map +1 -0
- package/dist/widgets-v2/range.js +52 -0
- package/dist/widgets-v2/range.js.map +1 -0
- package/dist/widgets-v2/scatterplot.js +405 -0
- package/dist/widgets-v2/scatterplot.js.map +1 -0
- package/dist/widgets-v2/spread.js +72 -0
- package/dist/widgets-v2/spread.js.map +1 -0
- package/dist/widgets-v2/stores.js +42 -0
- package/dist/widgets-v2/stores.js.map +1 -0
- package/dist/widgets-v2/table.js +78 -0
- package/dist/widgets-v2/table.js.map +1 -0
- package/dist/widgets-v2/timeseries.js +352 -0
- package/dist/widgets-v2/timeseries.js.map +1 -0
- package/dist/widgets-v2/utils.js +7 -0
- package/dist/widgets-v2/utils.js.map +1 -0
- package/dist/widgets-v2.js +953 -0
- package/dist/widgets-v2.js.map +1 -0
- package/package.json +73 -5
- package/src/components/lasso-tool/chip.test.tsx +176 -0
- package/src/components/lasso-tool/lasso-tool-inline.test.tsx +171 -0
- package/src/components/lasso-tool/lasso-tool.test.tsx +198 -0
- package/src/components/list-data/list-data.test.tsx +73 -0
- package/src/components/no-data-alert/no-data-alert.test.tsx +38 -0
- package/src/components/responsive-drawer/responsive-drawer.test.tsx +68 -0
- package/src/widgets/actions/brush-toggle/brush-overlay.test.tsx +465 -0
- package/src/widgets/actions/brush-toggle/brush-toggle.test.tsx +208 -0
- package/src/widgets/actions/change-column/change-column-dnd.test.tsx +193 -0
- package/src/widgets/actions/change-column/sortable-column-item.test.tsx +124 -0
- package/src/widgets/actions/zoom-toggle/zoom-toggle.test.tsx +322 -0
- package/src/widgets/category/components/category-rows.test.tsx +213 -0
- package/src/widgets/echart/utils.test.ts +277 -0
- package/src/widgets/formula/config.test.ts +37 -0
- package/src/widgets/range/components/range-item.test.tsx +243 -0
- package/src/widgets/stores/widget-store-branches.test.ts +275 -0
- package/src/widgets/table/config.test.ts +65 -0
- package/src/widgets/utils/chart-config/option-builders.test.ts +188 -0
- package/src/widgets-v2/PERFORMANCE.md +189 -0
- package/src/widgets-v2/actions/brush-toggle/brush-toggle.test.tsx +180 -0
- package/src/widgets-v2/actions/brush-toggle/brush-toggle.tsx +154 -0
- package/src/widgets-v2/actions/brush-toggle/index.ts +3 -0
- package/src/widgets-v2/actions/brush-toggle/labels.ts +9 -0
- package/src/widgets-v2/actions/brush-toggle/style.ts +11 -0
- package/src/widgets-v2/actions/brush-toggle/transforms.test.ts +47 -0
- package/src/widgets-v2/actions/brush-toggle/transforms.ts +31 -0
- package/src/widgets-v2/actions/change-column/change-column-icon.tsx +14 -0
- package/src/widgets-v2/actions/change-column/change-column.test.tsx +59 -0
- package/src/widgets-v2/actions/change-column/change-column.tsx +180 -0
- package/src/widgets-v2/actions/change-column/index.ts +7 -0
- package/src/widgets-v2/actions/change-column/labels.ts +9 -0
- package/src/widgets-v2/actions/change-column/sortable-column-item.tsx +56 -0
- package/src/widgets-v2/actions/change-column/style.ts +32 -0
- package/src/widgets-v2/actions/change-column/types.ts +11 -0
- package/src/widgets-v2/actions/download/download.test.tsx +327 -0
- package/src/widgets-v2/actions/download/download.tsx +144 -0
- package/src/widgets-v2/actions/download/exports.test.tsx +198 -0
- package/src/widgets-v2/actions/download/exports.ts +115 -0
- package/src/widgets-v2/actions/download/icons.tsx +26 -0
- package/src/widgets-v2/actions/download/index.ts +13 -0
- package/src/widgets-v2/actions/download/labels.ts +16 -0
- package/src/widgets-v2/actions/download/png-item.test.tsx +72 -0
- package/src/widgets-v2/actions/download/png-item.tsx +52 -0
- package/src/widgets-v2/actions/download/style.ts +3 -0
- package/src/widgets-v2/actions/download/types.ts +32 -0
- package/src/widgets-v2/actions/fullscreen/fullscreen.test.tsx +150 -0
- package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +230 -0
- package/src/widgets-v2/actions/fullscreen/index.ts +7 -0
- package/src/widgets-v2/actions/fullscreen/labels.ts +9 -0
- package/src/widgets-v2/actions/fullscreen/style.ts +59 -0
- package/src/widgets-v2/actions/fullscreen/types.ts +15 -0
- package/src/widgets-v2/actions/index.ts +82 -0
- package/src/widgets-v2/actions/lock-selection/index.ts +10 -0
- package/src/widgets-v2/actions/lock-selection/labels.ts +11 -0
- package/src/widgets-v2/actions/lock-selection/lock-selection.test.tsx +187 -0
- package/src/widgets-v2/actions/lock-selection/lock-selection.tsx +130 -0
- package/src/widgets-v2/actions/lock-selection/style.ts +11 -0
- package/src/widgets-v2/actions/lock-selection/transforms.ts +27 -0
- package/src/widgets-v2/actions/relative-data/index.ts +3 -0
- package/src/widgets-v2/actions/relative-data/labels.ts +9 -0
- package/src/widgets-v2/actions/relative-data/relative-data.test.tsx +71 -0
- package/src/widgets-v2/actions/relative-data/relative-data.tsx +107 -0
- package/src/widgets-v2/actions/relative-data/style.ts +11 -0
- package/src/widgets-v2/actions/relative-data/transforms.test.ts +151 -0
- package/src/widgets-v2/actions/relative-data/transforms.ts +70 -0
- package/src/widgets-v2/actions/searcher/filter.ts +28 -0
- package/src/widgets-v2/actions/searcher/index.ts +8 -0
- package/src/widgets-v2/actions/searcher/labels.ts +13 -0
- package/src/widgets-v2/actions/searcher/searcher-toggle.tsx +91 -0
- package/src/widgets-v2/actions/searcher/searcher.test.tsx +92 -0
- package/src/widgets-v2/actions/searcher/searcher.tsx +112 -0
- package/src/widgets-v2/actions/searcher/style.ts +15 -0
- package/src/widgets-v2/actions/stack-toggle/index.ts +3 -0
- package/src/widgets-v2/actions/stack-toggle/labels.ts +9 -0
- package/src/widgets-v2/actions/stack-toggle/stack-toggle.test.tsx +61 -0
- package/src/widgets-v2/actions/stack-toggle/stack-toggle.tsx +54 -0
- package/src/widgets-v2/actions/stack-toggle/style.ts +11 -0
- package/src/widgets-v2/actions/stack-toggle/transforms.test.ts +43 -0
- package/src/widgets-v2/actions/stack-toggle/transforms.ts +25 -0
- package/src/widgets-v2/actions/zoom-toggle/index.ts +9 -0
- package/src/widgets-v2/actions/zoom-toggle/labels.ts +9 -0
- package/src/widgets-v2/actions/zoom-toggle/style.ts +11 -0
- package/src/widgets-v2/actions/zoom-toggle/transforms.test.ts +148 -0
- package/src/widgets-v2/actions/zoom-toggle/transforms.ts +171 -0
- package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.test.tsx +107 -0
- package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.tsx +106 -0
- package/src/widgets-v2/bar/download.test.tsx +91 -0
- package/src/widgets-v2/bar/download.tsx +66 -0
- package/src/widgets-v2/bar/index.ts +10 -0
- package/src/widgets-v2/bar/options.test.ts +317 -0
- package/src/widgets-v2/bar/options.ts +326 -0
- package/src/widgets-v2/bar/skeleton.test.tsx +19 -0
- package/src/widgets-v2/bar/skeleton.tsx +69 -0
- package/src/widgets-v2/bar/types.ts +46 -0
- package/src/widgets-v2/category/category-ui.test.tsx +746 -0
- package/src/widgets-v2/category/category-ui.tsx +389 -0
- package/src/widgets-v2/category/category.relative-data.test.tsx +107 -0
- package/src/widgets-v2/category/category.stack-toggle.test.tsx +85 -0
- package/src/widgets-v2/category/category.test.tsx +305 -0
- package/src/widgets-v2/category/category.tsx +121 -0
- package/src/widgets-v2/category/components/category-bar-stacked.test.tsx +121 -0
- package/src/widgets-v2/category/components/category-bar-stacked.tsx +73 -0
- package/src/widgets-v2/category/components/category-bar.test.tsx +64 -0
- package/src/widgets-v2/category/components/category-bar.tsx +49 -0
- package/src/widgets-v2/category/components/category-legend.test.tsx +51 -0
- package/src/widgets-v2/category/components/category-legend.tsx +39 -0
- package/src/widgets-v2/category/components/category-row-multi.tsx +86 -0
- package/src/widgets-v2/category/components/category-row-other.test.tsx +28 -0
- package/src/widgets-v2/category/components/category-row-other.tsx +33 -0
- package/src/widgets-v2/category/components/category-row-single.tsx +76 -0
- package/src/widgets-v2/category/components/category-row-stacked.test.tsx +244 -0
- package/src/widgets-v2/category/components/category-row-stacked.tsx +99 -0
- package/src/widgets-v2/category/download.test.ts +71 -0
- package/src/widgets-v2/category/download.ts +54 -0
- package/src/widgets-v2/category/index.ts +32 -0
- package/src/widgets-v2/category/skeleton.test.tsx +26 -0
- package/src/widgets-v2/category/skeleton.tsx +74 -0
- package/src/widgets-v2/category/style.ts +290 -0
- package/src/widgets-v2/category/types.ts +54 -0
- package/src/widgets-v2/echart/echart-ui.test.tsx +232 -0
- package/src/widgets-v2/echart/echart-ui.tsx +184 -0
- package/src/widgets-v2/echart/echart.test.tsx +229 -0
- package/src/widgets-v2/echart/echart.tsx +199 -0
- package/src/widgets-v2/echart/index.ts +22 -0
- package/src/widgets-v2/echart/shared-resize-observer.test.ts +91 -0
- package/src/widgets-v2/echart/shared-resize-observer.ts +56 -0
- package/src/widgets-v2/echart/style.ts +8 -0
- package/src/widgets-v2/echart/use-chart-selection.test.tsx +118 -0
- package/src/widgets-v2/echart/use-chart-selection.ts +115 -0
- package/src/widgets-v2/formula/delta.tsx +61 -0
- package/src/widgets-v2/formula/download.test.tsx +65 -0
- package/src/widgets-v2/formula/download.tsx +69 -0
- package/src/widgets-v2/formula/formula-ui.test.tsx +91 -0
- package/src/widgets-v2/formula/formula-ui.tsx +66 -0
- package/src/widgets-v2/formula/formula.test.tsx +50 -0
- package/src/widgets-v2/formula/formula.tsx +34 -0
- package/src/widgets-v2/formula/index.ts +17 -0
- package/src/widgets-v2/formula/note.tsx +25 -0
- package/src/widgets-v2/formula/prefix.tsx +25 -0
- package/src/widgets-v2/formula/series.tsx +67 -0
- package/src/widgets-v2/formula/skeleton.test.tsx +21 -0
- package/src/widgets-v2/formula/skeleton.tsx +27 -0
- package/src/widgets-v2/formula/style.ts +31 -0
- package/src/widgets-v2/formula/subcomponents.test.tsx +107 -0
- package/src/widgets-v2/formula/suffix.tsx +25 -0
- package/src/widgets-v2/formula/types.ts +44 -0
- package/src/widgets-v2/formula/value.tsx +31 -0
- package/src/widgets-v2/histogram/download.test.ts +94 -0
- package/src/widgets-v2/histogram/download.ts +60 -0
- package/src/widgets-v2/histogram/index.ts +10 -0
- package/src/widgets-v2/histogram/options.test.ts +304 -0
- package/src/widgets-v2/histogram/options.ts +337 -0
- package/src/widgets-v2/histogram/skeleton.test.tsx +16 -0
- package/src/widgets-v2/histogram/skeleton.tsx +70 -0
- package/src/widgets-v2/histogram/transforms.test.ts +46 -0
- package/src/widgets-v2/histogram/transforms.ts +30 -0
- package/src/widgets-v2/histogram/types.ts +51 -0
- package/src/widgets-v2/index.ts +201 -0
- package/src/widgets-v2/markdown/download.test.ts +66 -0
- package/src/widgets-v2/markdown/download.ts +53 -0
- package/src/widgets-v2/markdown/index.ts +6 -0
- package/src/widgets-v2/markdown/markdown-content.test.tsx +155 -0
- package/src/widgets-v2/markdown/markdown-content.tsx +72 -0
- package/src/widgets-v2/markdown/markdown-ui.test.tsx +75 -0
- package/src/widgets-v2/markdown/markdown-ui.tsx +55 -0
- package/src/widgets-v2/markdown/markdown.test.tsx +39 -0
- package/src/widgets-v2/markdown/markdown.tsx +17 -0
- package/src/widgets-v2/markdown/skeleton.test.tsx +15 -0
- package/src/widgets-v2/markdown/skeleton.tsx +32 -0
- package/src/widgets-v2/markdown/style.ts +53 -0
- package/src/widgets-v2/markdown/types.ts +4 -0
- package/src/widgets-v2/note/labels.ts +9 -0
- package/src/widgets-v2/note/style.ts +26 -0
- package/src/widgets-v2/note/widget-note.test.tsx +158 -0
- package/src/widgets-v2/note/widget-note.tsx +172 -0
- package/src/widgets-v2/pie/download.test.ts +78 -0
- package/src/widgets-v2/pie/download.ts +55 -0
- package/src/widgets-v2/pie/index.ts +10 -0
- package/src/widgets-v2/pie/options.test.ts +585 -0
- package/src/widgets-v2/pie/options.ts +509 -0
- package/src/widgets-v2/pie/skeleton.test.tsx +17 -0
- package/src/widgets-v2/pie/skeleton.tsx +32 -0
- package/src/widgets-v2/pie/types.ts +55 -0
- package/src/widgets-v2/provider/widget-provider.test.tsx +119 -0
- package/src/widgets-v2/provider/widget-provider.tsx +111 -0
- package/src/widgets-v2/range/index.ts +4 -0
- package/src/widgets-v2/range/range-ui.test.tsx +130 -0
- package/src/widgets-v2/range/range-ui.tsx +211 -0
- package/src/widgets-v2/range/range.test.tsx +68 -0
- package/src/widgets-v2/range/range.tsx +46 -0
- package/src/widgets-v2/range/skeleton.test.tsx +17 -0
- package/src/widgets-v2/range/skeleton.tsx +47 -0
- package/src/widgets-v2/range/style.ts +41 -0
- package/src/widgets-v2/range/types.ts +37 -0
- package/src/widgets-v2/scatterplot/download.test.ts +71 -0
- package/src/widgets-v2/scatterplot/download.ts +54 -0
- package/src/widgets-v2/scatterplot/index.ts +11 -0
- package/src/widgets-v2/scatterplot/options.test.ts +399 -0
- package/src/widgets-v2/scatterplot/options.ts +421 -0
- package/src/widgets-v2/scatterplot/skeleton.test.tsx +17 -0
- package/src/widgets-v2/scatterplot/skeleton.tsx +84 -0
- package/src/widgets-v2/scatterplot/transforms.test.ts +97 -0
- package/src/widgets-v2/scatterplot/transforms.ts +38 -0
- package/src/widgets-v2/scatterplot/types.ts +55 -0
- package/src/widgets-v2/selection-summary/labels.ts +11 -0
- package/src/widgets-v2/selection-summary/selection-summary.test.tsx +53 -0
- package/src/widgets-v2/selection-summary/selection-summary.tsx +62 -0
- package/src/widgets-v2/selection-summary/style.ts +23 -0
- package/src/widgets-v2/spread/download.test.ts +64 -0
- package/src/widgets-v2/spread/download.ts +59 -0
- package/src/widgets-v2/spread/index.ts +6 -0
- package/src/widgets-v2/spread/separator.tsx +11 -0
- package/src/widgets-v2/spread/skeleton.test.tsx +17 -0
- package/src/widgets-v2/spread/skeleton.tsx +38 -0
- package/src/widgets-v2/spread/spread-ui.test.tsx +108 -0
- package/src/widgets-v2/spread/spread-ui.tsx +52 -0
- package/src/widgets-v2/spread/spread.test.tsx +50 -0
- package/src/widgets-v2/spread/spread.tsx +31 -0
- package/src/widgets-v2/spread/types.ts +27 -0
- package/src/widgets-v2/state/labels.test.ts +33 -0
- package/src/widgets-v2/state/labels.ts +20 -0
- package/src/widgets-v2/state/style.ts +25 -0
- package/src/widgets-v2/state/widget-state.test.tsx +294 -0
- package/src/widgets-v2/state/widget-state.tsx +184 -0
- package/src/widgets-v2/stores/index.ts +49 -0
- package/src/widgets-v2/stores/pipeline-middleware.test.ts +187 -0
- package/src/widgets-v2/stores/pipeline-middleware.ts +91 -0
- package/src/widgets-v2/stores/transforms.test.ts +162 -0
- package/src/widgets-v2/stores/transforms.ts +70 -0
- package/src/widgets-v2/stores/types.ts +64 -0
- package/src/widgets-v2/stores/use-echart-instance.test.tsx +91 -0
- package/src/widgets-v2/stores/use-echart-instance.ts +29 -0
- package/src/widgets-v2/stores/use-transform-enabled.test.tsx +127 -0
- package/src/widgets-v2/stores/use-transform-enabled.ts +25 -0
- package/src/widgets-v2/stores/use-transform.test.tsx +262 -0
- package/src/widgets-v2/stores/use-transform.ts +158 -0
- package/src/widgets-v2/stores/widget-context.test.tsx +58 -0
- package/src/widgets-v2/stores/widget-context.ts +15 -0
- package/src/widgets-v2/stores/widget-store-registry.test.ts +292 -0
- package/src/widgets-v2/stores/widget-store-registry.ts +248 -0
- package/src/widgets-v2/subheader/style.ts +12 -0
- package/src/widgets-v2/subheader/subheader.test.tsx +30 -0
- package/src/widgets-v2/subheader/subheader.tsx +16 -0
- package/src/widgets-v2/table/download.test.ts +75 -0
- package/src/widgets-v2/table/download.ts +47 -0
- package/src/widgets-v2/table/helpers.test.ts +214 -0
- package/src/widgets-v2/table/helpers.ts +136 -0
- package/src/widgets-v2/table/index.ts +23 -0
- package/src/widgets-v2/table/labels.tsx +41 -0
- package/src/widgets-v2/table/skeleton.test.tsx +26 -0
- package/src/widgets-v2/table/skeleton.tsx +65 -0
- package/src/widgets-v2/table/style.ts +46 -0
- package/src/widgets-v2/table/table-ui.test.tsx +200 -0
- package/src/widgets-v2/table/table-ui.tsx +331 -0
- package/src/widgets-v2/table/table.test.tsx +119 -0
- package/src/widgets-v2/table/table.tsx +174 -0
- package/src/widgets-v2/table/types.ts +44 -0
- package/src/widgets-v2/test-utils.ts +107 -0
- package/src/widgets-v2/timeseries/download.test.ts +95 -0
- package/src/widgets-v2/timeseries/download.ts +86 -0
- package/src/widgets-v2/timeseries/index.ts +10 -0
- package/src/widgets-v2/timeseries/options.test.ts +379 -0
- package/src/widgets-v2/timeseries/options.ts +341 -0
- package/src/widgets-v2/timeseries/skeleton.test.tsx +13 -0
- package/src/widgets-v2/timeseries/skeleton.tsx +76 -0
- package/src/widgets-v2/timeseries/types.ts +61 -0
- package/src/widgets-v2/toolbox/labels.ts +9 -0
- package/src/widgets-v2/toolbox/style.ts +33 -0
- package/src/widgets-v2/toolbox/toolbox.test.tsx +200 -0
- package/src/widgets-v2/toolbox/toolbox.tsx +309 -0
- package/src/widgets-v2/utils/data-zoom-layout.ts +26 -0
- package/src/widgets-v2/utils/index.ts +2 -0
- package/src/widgets-v2/utils/merge-options.test.ts +52 -0
- package/src/widgets-v2/utils/merge-options.ts +50 -0
- package/src/widgets-v2/wrapper/index.ts +14 -0
- package/src/widgets-v2/wrapper/labels.ts +11 -0
- package/src/widgets-v2/wrapper/style.ts +134 -0
- package/src/widgets-v2/wrapper/widget-actions.test.tsx +52 -0
- package/src/widgets-v2/wrapper/widget-actions.tsx +43 -0
- package/src/widgets-v2/wrapper/widget-content.test.tsx +27 -0
- package/src/widgets-v2/wrapper/widget-content.tsx +29 -0
- package/src/widgets-v2/wrapper/widget-wrapper.test.tsx +159 -0
- package/src/widgets-v2/wrapper/widget-wrapper.tsx +178 -0
- package/dist/styles-BYTyKQFP.js.map +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Theme } from '@mui/material'
|
|
2
|
+
import type { EChartsOption } from 'echarts'
|
|
3
|
+
|
|
4
|
+
/** A single point — `[x, y]` tuple. */
|
|
5
|
+
export type ScatterplotDatum = readonly [number, number]
|
|
6
|
+
|
|
7
|
+
/** Scatterplot widget data — array of series, each a list of `[x, y]` tuples. */
|
|
8
|
+
export type ScatterplotWidgetData = readonly (readonly ScatterplotDatum[])[]
|
|
9
|
+
|
|
10
|
+
/** Inputs to the structural-only {@link scatterplotOptions} builder. */
|
|
11
|
+
export interface ScatterplotOptionsInput {
|
|
12
|
+
theme: Theme
|
|
13
|
+
/** Optional formatter for x-axis values. */
|
|
14
|
+
xFormatter?: (value: number) => string
|
|
15
|
+
/** Optional formatter for y-axis values. */
|
|
16
|
+
yFormatter?: (value: number) => string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Combined inputs for the scatterplot option factory creator. Carries
|
|
21
|
+
* everything the widget needs across BOTH phases — the structural-build
|
|
22
|
+
* (`theme`, `xFormatter`, `yFormatter`, `optionsOverride`) AND the data
|
|
23
|
+
* merge (`seriesNames`, `symbolSize`, `selection`).
|
|
24
|
+
*/
|
|
25
|
+
export interface ScatterplotOptionFactoryInput {
|
|
26
|
+
theme: Theme
|
|
27
|
+
/**
|
|
28
|
+
* Optional x-axis value formatter. Drives the structural x-axis
|
|
29
|
+
* label and the reactive tooltip path: when RelativeData overrides
|
|
30
|
+
* `state.formatter` (the y formatter) the merger rebuilds the tooltip
|
|
31
|
+
* and reads `xFormatter` to render coordinates correctly. xFormatter
|
|
32
|
+
* itself isn't relativized — x stays raw.
|
|
33
|
+
*/
|
|
34
|
+
xFormatter?: (value: number) => string
|
|
35
|
+
/** Optional formatter for y-axis values (structural baseline). */
|
|
36
|
+
yFormatter?: (value: number) => string
|
|
37
|
+
/** Series names — drives the legend and `series[i].name`. */
|
|
38
|
+
seriesNames?: readonly string[]
|
|
39
|
+
/** Symbol size in px (default `8`). */
|
|
40
|
+
symbolSize?: number
|
|
41
|
+
/**
|
|
42
|
+
* Selected point keys, one per `${seriesIndex}:${dataIndex}` pair. Points
|
|
43
|
+
* not in this list render dimmed (`itemStyle.opacity: 0.15`). `null`/empty
|
|
44
|
+
* means no selection.
|
|
45
|
+
*/
|
|
46
|
+
selection?: readonly string[] | null
|
|
47
|
+
/**
|
|
48
|
+
* Consumer-supplied partial option merged into the structural option at
|
|
49
|
+
* structural-build time. Lets stories override pieces of the theme-aware
|
|
50
|
+
* base without forking the structural builder.
|
|
51
|
+
*/
|
|
52
|
+
optionsOverride?: Partial<EChartsOption>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type ScatterplotEChartsOption = EChartsOption
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SelectionSummaryLabels {
|
|
2
|
+
allSelected: string
|
|
3
|
+
selections: (count: number) => string
|
|
4
|
+
clear: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_SELECTION_SUMMARY_LABELS: SelectionSummaryLabels = {
|
|
8
|
+
allSelected: 'All selected',
|
|
9
|
+
selections: (count) => `${count} selected`,
|
|
10
|
+
clear: 'Clear',
|
|
11
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
3
|
+
import StarIcon from '@mui/icons-material/Star'
|
|
4
|
+
import { SelectionSummary } from './selection-summary'
|
|
5
|
+
|
|
6
|
+
describe('<SelectionSummary>', () => {
|
|
7
|
+
it('renders nothing when total === 0 (no data to summarize)', () => {
|
|
8
|
+
const { container } = render(<SelectionSummary count={0} total={0} />)
|
|
9
|
+
expect(container.textContent).toBe('')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('renders the "All selected" label when count === 0 and total > 0 (R34)', () => {
|
|
13
|
+
render(<SelectionSummary count={0} total={10} />)
|
|
14
|
+
expect(screen.getByText('All selected')).toBeTruthy()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('renders the count + Clear link when count > 0', () => {
|
|
18
|
+
const onClear = vi.fn()
|
|
19
|
+
render(<SelectionSummary count={3} total={10} onClear={onClear} />)
|
|
20
|
+
expect(screen.getByText(/3/)).toBeTruthy()
|
|
21
|
+
fireEvent.click(screen.getByText('Clear'))
|
|
22
|
+
expect(onClear).toHaveBeenCalledTimes(1)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('hides the Clear link when no onClear is provided', () => {
|
|
26
|
+
render(<SelectionSummary count={3} total={10} />)
|
|
27
|
+
expect(screen.queryByText('Clear')).toBeNull()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('honors custom labels for empty + populated + clear', () => {
|
|
31
|
+
render(
|
|
32
|
+
<SelectionSummary
|
|
33
|
+
count={3}
|
|
34
|
+
total={10}
|
|
35
|
+
labels={{
|
|
36
|
+
allSelected: 'Showing all',
|
|
37
|
+
selections: (n) => `${n} pinned`,
|
|
38
|
+
clear: 'Reset',
|
|
39
|
+
}}
|
|
40
|
+
onClear={() => undefined}
|
|
41
|
+
/>,
|
|
42
|
+
)
|
|
43
|
+
expect(screen.getByText('3 pinned')).toBeTruthy()
|
|
44
|
+
expect(screen.getByText('Reset')).toBeTruthy()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('renders the custom icon component when provided in the empty state', () => {
|
|
48
|
+
const { container } = render(
|
|
49
|
+
<SelectionSummary count={0} total={10} icon={StarIcon} />,
|
|
50
|
+
)
|
|
51
|
+
expect(container.querySelector('[data-testid="StarIcon"]')).toBeTruthy()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Box, Button, Typography, type SvgIconProps } from '@mui/material'
|
|
2
|
+
import type { ComponentType } from 'react'
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_SELECTION_SUMMARY_LABELS,
|
|
5
|
+
type SelectionSummaryLabels,
|
|
6
|
+
} from './labels'
|
|
7
|
+
import { styles } from './style'
|
|
8
|
+
|
|
9
|
+
export interface SelectionSummaryProps {
|
|
10
|
+
/** Number of currently selected items. */
|
|
11
|
+
count: number
|
|
12
|
+
/** Optional total — when 0 the component renders nothing (no data to summarize). */
|
|
13
|
+
total?: number
|
|
14
|
+
/** Clear callback. Shown only when count > 0. */
|
|
15
|
+
onClear?: () => void
|
|
16
|
+
labels?: Partial<SelectionSummaryLabels>
|
|
17
|
+
icon?: ComponentType<SvgIconProps>
|
|
18
|
+
iconProps?: SvgIconProps
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Render rules:
|
|
23
|
+
* - `total === 0` → render nothing.
|
|
24
|
+
* - `count === 0` → render `labels.allSelected` (matches v1 default),
|
|
25
|
+
* prefixed with the optional `icon` glyph when provided.
|
|
26
|
+
* - `count > 0` → render `labels.selections(count)` + Clear (when `onClear`).
|
|
27
|
+
*/
|
|
28
|
+
export function SelectionSummary({
|
|
29
|
+
count,
|
|
30
|
+
total,
|
|
31
|
+
onClear,
|
|
32
|
+
labels,
|
|
33
|
+
icon: Icon,
|
|
34
|
+
iconProps,
|
|
35
|
+
}: SelectionSummaryProps) {
|
|
36
|
+
if (total === 0) return null
|
|
37
|
+
const _labels = { ...DEFAULT_SELECTION_SUMMARY_LABELS, ...labels }
|
|
38
|
+
|
|
39
|
+
if (count === 0) {
|
|
40
|
+
return (
|
|
41
|
+
<Box sx={styles.root}>
|
|
42
|
+
{Icon ? <Icon fontSize='small' {...iconProps} /> : null}
|
|
43
|
+
<Typography variant='caption' sx={styles.label}>
|
|
44
|
+
{_labels.allSelected}
|
|
45
|
+
</Typography>
|
|
46
|
+
</Box>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Box sx={styles.root}>
|
|
52
|
+
<Typography variant='caption' sx={styles.label}>
|
|
53
|
+
{_labels.selections(count)}
|
|
54
|
+
</Typography>
|
|
55
|
+
{onClear && (
|
|
56
|
+
<Button size='small' variant='text' onClick={onClear}>
|
|
57
|
+
{_labels.clear}
|
|
58
|
+
</Button>
|
|
59
|
+
)}
|
|
60
|
+
</Box>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export const styles = {
|
|
4
|
+
root: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
gap: 0.5,
|
|
8
|
+
minHeight: 24,
|
|
9
|
+
},
|
|
10
|
+
label: {
|
|
11
|
+
color: 'text.secondary',
|
|
12
|
+
},
|
|
13
|
+
clear: {
|
|
14
|
+
color: 'primary.main',
|
|
15
|
+
cursor: 'pointer',
|
|
16
|
+
fontWeight: 500,
|
|
17
|
+
'&:hover': { textDecoration: 'underline' },
|
|
18
|
+
},
|
|
19
|
+
icon: {
|
|
20
|
+
fontSize: 16,
|
|
21
|
+
color: 'text.secondary',
|
|
22
|
+
},
|
|
23
|
+
} satisfies Record<string, SxProps<Theme>>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import { createSpreadDownloadConfig } from './download'
|
|
3
|
+
import type { SpreadWidgetData } from './types'
|
|
4
|
+
|
|
5
|
+
const data: SpreadWidgetData = [
|
|
6
|
+
{
|
|
7
|
+
min: 1,
|
|
8
|
+
max: 9,
|
|
9
|
+
prefix: '$',
|
|
10
|
+
suffix: 'k',
|
|
11
|
+
note: 'q1',
|
|
12
|
+
series: { name: 'Revenue' },
|
|
13
|
+
},
|
|
14
|
+
{ min: 0, max: 10 },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
let csvText = ''
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
csvText = ''
|
|
21
|
+
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock')
|
|
22
|
+
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined)
|
|
23
|
+
const RealBlob = global.Blob
|
|
24
|
+
vi.stubGlobal(
|
|
25
|
+
'Blob',
|
|
26
|
+
class extends RealBlob {
|
|
27
|
+
constructor(parts: BlobPart[], opts?: BlobPropertyBag) {
|
|
28
|
+
csvText = typeof parts[0] === 'string' ? parts[0] : ''
|
|
29
|
+
super(parts, opts)
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('createSpreadDownloadConfig', () => {
|
|
36
|
+
it('CSV-only by default', () => {
|
|
37
|
+
expect(
|
|
38
|
+
createSpreadDownloadConfig({ filename: 's', getData: () => data }).map(
|
|
39
|
+
(i) => i.id,
|
|
40
|
+
),
|
|
41
|
+
).toEqual(['csv'])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('prepends PNG when getCaptureEl is provided', () => {
|
|
45
|
+
const items = createSpreadDownloadConfig({
|
|
46
|
+
filename: 's',
|
|
47
|
+
getData: () => data,
|
|
48
|
+
getCaptureEl: () => document.createElement('div'),
|
|
49
|
+
})
|
|
50
|
+
expect(items.map((i) => i.id)).toEqual(['png', 'csv'])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('CSV resolve emits one row per item with optional fields blanked', async () => {
|
|
54
|
+
const items = createSpreadDownloadConfig({
|
|
55
|
+
filename: 's',
|
|
56
|
+
getData: () => data,
|
|
57
|
+
})
|
|
58
|
+
const handle = await items.find((i) => i.id === 'csv')!.resolve()
|
|
59
|
+
expect(handle.filename).toBe('s.csv')
|
|
60
|
+
expect(csvText).toBe(
|
|
61
|
+
'series,prefix,min,max,suffix,note\nRevenue,$,1,9,k,q1\n,,0,10,,',
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildPngDownloadItem,
|
|
3
|
+
downloadToCSV,
|
|
4
|
+
type DownloadItem,
|
|
5
|
+
} from '../actions/download'
|
|
6
|
+
import type { SpreadWidgetData } from './types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Download menu items for the Spread widget. Always includes a CSV item
|
|
10
|
+
* with `series, prefix, min, max, suffix, note` columns (one row per
|
|
11
|
+
* entry). When `getCaptureEl` is supplied, prepends a PNG item that
|
|
12
|
+
* rasterises the captured element via `html2canvas`.
|
|
13
|
+
*/
|
|
14
|
+
export function createSpreadDownloadConfig(args: {
|
|
15
|
+
filename: string
|
|
16
|
+
getData: () => SpreadWidgetData
|
|
17
|
+
getCaptureEl?: () => HTMLElement | null
|
|
18
|
+
pngPixelRatio?: number
|
|
19
|
+
pngBackgroundColor?: string | null
|
|
20
|
+
}): DownloadItem[] {
|
|
21
|
+
const items: DownloadItem[] = []
|
|
22
|
+
if (args.getCaptureEl) {
|
|
23
|
+
items.push(
|
|
24
|
+
buildPngDownloadItem({
|
|
25
|
+
filename: args.filename,
|
|
26
|
+
getCaptureEl: args.getCaptureEl,
|
|
27
|
+
pixelRatio: args.pngPixelRatio,
|
|
28
|
+
backgroundColor: args.pngBackgroundColor,
|
|
29
|
+
}),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
items.push({
|
|
33
|
+
id: 'csv',
|
|
34
|
+
label: 'Download as CSV',
|
|
35
|
+
resolve: () => {
|
|
36
|
+
const data = args.getData()
|
|
37
|
+
const rows: unknown[][] = [
|
|
38
|
+
['series', 'prefix', 'min', 'max', 'suffix', 'note'],
|
|
39
|
+
]
|
|
40
|
+
for (const item of data) {
|
|
41
|
+
rows.push([
|
|
42
|
+
item.series?.name ?? '',
|
|
43
|
+
item.prefix ?? '',
|
|
44
|
+
item.min,
|
|
45
|
+
item.max,
|
|
46
|
+
item.suffix ?? '',
|
|
47
|
+
item.note ?? '',
|
|
48
|
+
])
|
|
49
|
+
}
|
|
50
|
+
const handle = downloadToCSV(rows)
|
|
51
|
+
return Promise.resolve({
|
|
52
|
+
url: handle.url,
|
|
53
|
+
filename: `${args.filename}.csv`,
|
|
54
|
+
revoke: handle.revoke,
|
|
55
|
+
})
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
return items
|
|
59
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { Spread } from './spread'
|
|
2
|
+
export { SpreadUI, type SpreadUIProps } from './spread-ui'
|
|
3
|
+
export { Separator } from './separator'
|
|
4
|
+
export { SpreadSkeleton, type SpreadSkeletonProps } from './skeleton'
|
|
5
|
+
export { createSpreadDownloadConfig } from './download'
|
|
6
|
+
export type { SpreadDataItem, SpreadWidgetData } from './types'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Value } from '../formula'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Em-dash separator placed between a Spread row's `min` and `max` values.
|
|
5
|
+
* Built from the Formula {@link Value} primitive so typography (h5/600,
|
|
6
|
+
* baseline alignment) matches the numbers around it. Renders with
|
|
7
|
+
* `text.secondary` so it visually recedes between the two bounds.
|
|
8
|
+
*/
|
|
9
|
+
export function Separator() {
|
|
10
|
+
return <Value color='inherit'>-</Value>
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
import { SpreadSkeleton } from './skeleton'
|
|
4
|
+
|
|
5
|
+
describe('<SpreadSkeleton>', () => {
|
|
6
|
+
it('renders the default skeleton', () => {
|
|
7
|
+
const { container } = render(<SpreadSkeleton />)
|
|
8
|
+
expect(container.firstChild).not.toBeNull()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('renders the requested number of rows', () => {
|
|
12
|
+
const { container } = render(<SpreadSkeleton count={2} />)
|
|
13
|
+
expect(
|
|
14
|
+
container.querySelectorAll('.MuiSkeleton-root').length,
|
|
15
|
+
).toBeGreaterThan(1)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Box, Skeleton } from '@mui/material'
|
|
2
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
3
|
+
|
|
4
|
+
const styles = {
|
|
5
|
+
root: {
|
|
6
|
+
display: 'flex',
|
|
7
|
+
flexDirection: 'column',
|
|
8
|
+
gap: 2,
|
|
9
|
+
py: 1,
|
|
10
|
+
},
|
|
11
|
+
row: {
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
gap: 1.5,
|
|
15
|
+
},
|
|
16
|
+
block: { width: 200, height: 32 },
|
|
17
|
+
} satisfies Record<string, SxProps<Theme>>
|
|
18
|
+
|
|
19
|
+
export interface SpreadSkeletonProps {
|
|
20
|
+
count?: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Loading placeholder for the Spread widget. Renders `count` rows, each
|
|
25
|
+
* sized for the `min — max` pair so the layout doesn't jump once data
|
|
26
|
+
* resolves. Mirrors the row-per-item structure of {@link SpreadUI}.
|
|
27
|
+
*/
|
|
28
|
+
export function SpreadSkeleton({ count = 1 }: SpreadSkeletonProps) {
|
|
29
|
+
return (
|
|
30
|
+
<Box sx={styles.root}>
|
|
31
|
+
{Array.from({ length: count }).map((_, i) => (
|
|
32
|
+
<Box key={`row-${i}`} sx={styles.row}>
|
|
33
|
+
<Skeleton variant='rectangular' sx={styles.block} />
|
|
34
|
+
</Box>
|
|
35
|
+
))}
|
|
36
|
+
</Box>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { SpreadUI } from './spread-ui'
|
|
4
|
+
|
|
5
|
+
describe('<SpreadUI>', () => {
|
|
6
|
+
it('renders a single min/max pair', () => {
|
|
7
|
+
render(<SpreadUI items={[{ min: 10, max: 100 }]} />)
|
|
8
|
+
expect(screen.getByText('10')).toBeTruthy()
|
|
9
|
+
expect(screen.getByText('100')).toBeTruthy()
|
|
10
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('applies the formatter to both bounds', () => {
|
|
14
|
+
render(
|
|
15
|
+
<SpreadUI
|
|
16
|
+
items={[{ min: 1234, max: 9876 }]}
|
|
17
|
+
formatter={(n) => n.toLocaleString('en-US')}
|
|
18
|
+
/>,
|
|
19
|
+
)
|
|
20
|
+
expect(screen.getByText('1,234')).toBeTruthy()
|
|
21
|
+
expect(screen.getByText('9,876')).toBeTruthy()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('renders prefix and suffix when provided', () => {
|
|
25
|
+
render(
|
|
26
|
+
<SpreadUI
|
|
27
|
+
items={[{ min: 5, max: 95, prefix: 'Range', suffix: 'kWh' }]}
|
|
28
|
+
/>,
|
|
29
|
+
)
|
|
30
|
+
expect(screen.getByText('Range')).toBeTruthy()
|
|
31
|
+
expect(screen.getByText('kWh')).toBeTruthy()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('renders multiple items as separate rows', () => {
|
|
35
|
+
render(
|
|
36
|
+
<SpreadUI
|
|
37
|
+
items={[
|
|
38
|
+
{ min: 0, max: 100, prefix: 'Q1' },
|
|
39
|
+
{ min: 50, max: 200, prefix: 'Q2' },
|
|
40
|
+
{ min: 100, max: 300, prefix: 'Q3' },
|
|
41
|
+
]}
|
|
42
|
+
/>,
|
|
43
|
+
)
|
|
44
|
+
expect(screen.getByText('Q1')).toBeTruthy()
|
|
45
|
+
expect(screen.getByText('Q2')).toBeTruthy()
|
|
46
|
+
expect(screen.getByText('Q3')).toBeTruthy()
|
|
47
|
+
// One separator dash per item.
|
|
48
|
+
expect(screen.getAllByText('-')).toHaveLength(3)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('renders nothing visible when items is empty', () => {
|
|
52
|
+
const { container } = render(<SpreadUI items={[]} />)
|
|
53
|
+
expect(container.querySelectorAll('h5').length).toBe(0)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('renders a series avatar with the first letter of the name', () => {
|
|
57
|
+
render(
|
|
58
|
+
<SpreadUI
|
|
59
|
+
items={[
|
|
60
|
+
{ min: 1, max: 9, series: { name: 'Revenue', color: '#1E5BB5' } },
|
|
61
|
+
]}
|
|
62
|
+
/>,
|
|
63
|
+
)
|
|
64
|
+
expect(screen.getByLabelText('Revenue').textContent).toBe('R')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('renders the note when provided', () => {
|
|
68
|
+
render(<SpreadUI items={[{ min: 1, max: 9, note: 'Last 7 days' }]} />)
|
|
69
|
+
expect(screen.getByText('Last 7 days')).toBeTruthy()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('omits series / note when fields are absent', () => {
|
|
73
|
+
const { container } = render(<SpreadUI items={[{ min: 1, max: 2 }]} />)
|
|
74
|
+
expect(container.querySelector('.MuiAvatar-root')).toBeNull()
|
|
75
|
+
expect(container.querySelector('p')).toBeNull()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('renders the row in order: Series → Prefix → Min → — → Max → Suffix', () => {
|
|
79
|
+
const { container } = render(
|
|
80
|
+
<SpreadUI
|
|
81
|
+
items={[
|
|
82
|
+
{
|
|
83
|
+
min: 10,
|
|
84
|
+
max: 20,
|
|
85
|
+
prefix: '$',
|
|
86
|
+
suffix: 'USD',
|
|
87
|
+
series: { name: 'Revenue' },
|
|
88
|
+
},
|
|
89
|
+
]}
|
|
90
|
+
/>,
|
|
91
|
+
)
|
|
92
|
+
// h5 / h6 / caption typography variants cover prefix, value, separator, suffix.
|
|
93
|
+
const typographies = Array.from(
|
|
94
|
+
container.querySelectorAll('.MuiTypography-root'),
|
|
95
|
+
).map((el) => el.textContent ?? '')
|
|
96
|
+
// The Series avatar is not Typography; we look at the inline-row text.
|
|
97
|
+
expect(typographies).toEqual(['$', '10', '-', '20', 'USD'])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('still renders both bounds when a per-item color is supplied', () => {
|
|
101
|
+
// Color threading goes through MUI's `sx` so the runtime inline style
|
|
102
|
+
// isn't populated in JSDOM — we just check that both bounds render
|
|
103
|
+
// normally with the color prop set.
|
|
104
|
+
render(<SpreadUI items={[{ min: 1, max: 2, color: 'success.main' }]} />)
|
|
105
|
+
expect(screen.getByText('1')).toBeTruthy()
|
|
106
|
+
expect(screen.getByText('2')).toBeTruthy()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Box } from '@mui/material'
|
|
2
|
+
import type { Ref } from 'react'
|
|
3
|
+
import { Note, Prefix, Series, Suffix, Value } from '../formula'
|
|
4
|
+
import { styles } from '../formula/style'
|
|
5
|
+
import { Separator } from './separator'
|
|
6
|
+
import type { SpreadDataItem } from './types'
|
|
7
|
+
|
|
8
|
+
export interface SpreadUIProps {
|
|
9
|
+
items: readonly SpreadDataItem[]
|
|
10
|
+
/** Number formatter — applied to each item's `min` and `max`. */
|
|
11
|
+
formatter?: (value: number) => string
|
|
12
|
+
/** Forwarded to the root `<Box>` so consumers can capture the DOM (PNG export). */
|
|
13
|
+
ref?: Ref<HTMLDivElement>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Pure presentational component for the Spread widget. Each item renders as
|
|
18
|
+
* a row containing (in order): optional `Series` avatar, a body with
|
|
19
|
+
* `[prefix] min — max [suffix]` plus optional `Note`. Reuses the Formula
|
|
20
|
+
* primitives ({@link Series}, {@link Prefix}, {@link Value}, {@link Suffix},
|
|
21
|
+
* {@link Note}) and the Formula row styles so Spread and Formula stay
|
|
22
|
+
* visually aligned when used together.
|
|
23
|
+
*/
|
|
24
|
+
export function SpreadUI({ items, formatter, ref }: SpreadUIProps) {
|
|
25
|
+
const fmt = formatter ?? ((n: number) => String(n))
|
|
26
|
+
return (
|
|
27
|
+
<Box ref={ref} sx={styles.root}>
|
|
28
|
+
{items.map((item, i) => (
|
|
29
|
+
// Composite of series name + min/max bounds for stability across
|
|
30
|
+
// reorders; falls back to the index when bounds happen to collide.
|
|
31
|
+
<Box
|
|
32
|
+
key={`spread-${item.series?.name ?? ''}-${item.min}-${item.max}-${i}`}
|
|
33
|
+
sx={styles.row}
|
|
34
|
+
>
|
|
35
|
+
{item.series ? (
|
|
36
|
+
<Series name={item.series.name} color={item.series.color} />
|
|
37
|
+
) : null}
|
|
38
|
+
<Box sx={styles.body}>
|
|
39
|
+
<Box sx={styles.valueRow}>
|
|
40
|
+
{item.prefix ? <Prefix>{item.prefix}</Prefix> : null}
|
|
41
|
+
<Value color={item.color}>{fmt(item.min)}</Value>
|
|
42
|
+
<Separator />
|
|
43
|
+
<Value color={item.color}>{fmt(item.max)}</Value>
|
|
44
|
+
{item.suffix ? <Suffix>{item.suffix}</Suffix> : null}
|
|
45
|
+
</Box>
|
|
46
|
+
{item.note ? <Note>{item.note}</Note> : null}
|
|
47
|
+
</Box>
|
|
48
|
+
</Box>
|
|
49
|
+
))}
|
|
50
|
+
</Box>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
import { Spread } from './spread'
|
|
4
|
+
import { Provider } from '../provider/widget-provider'
|
|
5
|
+
import type { SpreadWidgetData } from './types'
|
|
6
|
+
|
|
7
|
+
vi.mock('./spread-ui', () => ({
|
|
8
|
+
SpreadUI: ({
|
|
9
|
+
items,
|
|
10
|
+
formatter,
|
|
11
|
+
}: {
|
|
12
|
+
items: SpreadWidgetData
|
|
13
|
+
formatter?: (value: number) => string
|
|
14
|
+
}) => (
|
|
15
|
+
<div data-testid='spread-ui' data-count={items.length}>
|
|
16
|
+
{items
|
|
17
|
+
.map(
|
|
18
|
+
(it) =>
|
|
19
|
+
`${formatter ? formatter(it.min) : it.min}—${formatter ? formatter(it.max) : it.max}`,
|
|
20
|
+
)
|
|
21
|
+
.join('|')}
|
|
22
|
+
</div>
|
|
23
|
+
),
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
describe('<Spread> bridge', () => {
|
|
27
|
+
it('forwards data + formatter to SpreadUI', () => {
|
|
28
|
+
const data: SpreadWidgetData = [
|
|
29
|
+
{ min: 1, max: 9 },
|
|
30
|
+
{ min: 0, max: 10 },
|
|
31
|
+
]
|
|
32
|
+
const { getByTestId } = render(
|
|
33
|
+
<Provider id='sp-1' data={data} formatter={(n) => `${n}k`}>
|
|
34
|
+
<Spread />
|
|
35
|
+
</Provider>,
|
|
36
|
+
)
|
|
37
|
+
const ui = getByTestId('spread-ui')
|
|
38
|
+
expect(ui.dataset.count).toBe('2')
|
|
39
|
+
expect(ui.textContent).toBe('1k—9k|0k—10k')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('renders zero items when data is null', () => {
|
|
43
|
+
const { getByTestId } = render(
|
|
44
|
+
<Provider id='sp-2' data={null}>
|
|
45
|
+
<Spread />
|
|
46
|
+
</Provider>,
|
|
47
|
+
)
|
|
48
|
+
expect(getByTestId('spread-ui').dataset.count).toBe('0')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useWidgetId, useWidgetShallow } from '../stores'
|
|
2
|
+
import { SpreadUI } from './spread-ui'
|
|
3
|
+
import type { SpreadWidgetData } from './types'
|
|
4
|
+
|
|
5
|
+
interface SpreadSlice {
|
|
6
|
+
data: SpreadWidgetData
|
|
7
|
+
formatter?: (value: number) => string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Stable empty-data sentinel — keeps the slice's `data` reference stable
|
|
11
|
+
// when the underlying store value is null/undefined so the shallow-equality
|
|
12
|
+
// gate in `useWidgetShallow` doesn't re-render every commit.
|
|
13
|
+
const EMPTY_DATA: SpreadWidgetData = Object.freeze([]) as SpreadWidgetData
|
|
14
|
+
|
|
15
|
+
const spreadSelector = (s: {
|
|
16
|
+
data: unknown
|
|
17
|
+
formatter?: (value: number) => string
|
|
18
|
+
}): SpreadSlice => ({
|
|
19
|
+
data: (s.data ?? EMPTY_DATA) as SpreadWidgetData,
|
|
20
|
+
formatter: s.formatter,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Stateful Spread bridge — reads `data` (post-pipeline) and `formatter` from
|
|
25
|
+
* the per-widget store and forwards them to the pure {@link SpreadUI}.
|
|
26
|
+
*/
|
|
27
|
+
export function Spread() {
|
|
28
|
+
const id = useWidgetId()
|
|
29
|
+
const slice = useWidgetShallow(id, spreadSelector)
|
|
30
|
+
return <SpreadUI items={slice.data} formatter={slice.formatter} />
|
|
31
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { FormulaSeries } from '../formula'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A single min/max pair rendered by the Spread widget. Mirrors
|
|
5
|
+
* {@link FormulaDataItem} — same optional `series`, `prefix`, `suffix`,
|
|
6
|
+
* `color`, and `note` slots — but the value is a range (`min` … `max`)
|
|
7
|
+
* rather than a single `value`.
|
|
8
|
+
*/
|
|
9
|
+
export interface SpreadDataItem {
|
|
10
|
+
min: number
|
|
11
|
+
max: number
|
|
12
|
+
prefix?: string
|
|
13
|
+
suffix?: string
|
|
14
|
+
/** Override the values' text colour (e.g. `'success.main'`). */
|
|
15
|
+
color?: string
|
|
16
|
+
/** Optional series indicator — coloured avatar with first letter of `name`. */
|
|
17
|
+
series?: FormulaSeries
|
|
18
|
+
/** Optional caption rendered below the values. */
|
|
19
|
+
note?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Spread widget data — an array of `{min, max, …}` pairs rendered
|
|
24
|
+
* top-to-bottom, one row per entry. Visual structure matches the Formula
|
|
25
|
+
* widget so the two can sit side-by-side without style drift.
|
|
26
|
+
*/
|
|
27
|
+
export type SpreadWidgetData = readonly SpreadDataItem[]
|