@carto/ps-react-ui 4.3.3 → 4.3.5
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/components.js +3 -3
- package/dist/components.js.map +1 -1
- package/dist/{lasso-tool-BwRzEW7k.js → lasso-tool-wFqOD6wk.js} +13 -13
- package/dist/lasso-tool-wFqOD6wk.js.map +1 -0
- package/dist/types/components/common-types.d.ts +41 -0
- package/dist/types/components/types.d.ts +1 -1
- package/dist/types/widgets/echart/echart-ui.d.ts +1 -1
- package/dist/types/widgets/echart/types.d.ts +4 -0
- package/dist/widgets/actions.js +1 -1
- package/dist/widgets/bar.js +1 -1
- package/dist/widgets/category.js +1 -1
- package/dist/widgets/echart.js +96 -85
- package/dist/widgets/echart.js.map +1 -1
- package/dist/widgets/formula.js +1 -1
- package/dist/widgets/histogram.js +1 -1
- package/dist/widgets/markdown.js +1 -1
- package/dist/widgets/pie.js +1 -1
- package/dist/widgets/scatterplot.js +1 -1
- package/dist/widgets/spread.js +1 -1
- package/dist/widgets/table.js +1 -1
- package/dist/widgets/timeseries.js +1 -1
- package/dist/widgets/toolbar-actions.js.map +1 -1
- package/dist/widgets/wrapper.js +1 -1
- package/package.json +8 -3
- package/src/components/basemaps/basemaps.test.tsx +196 -0
- package/src/components/basemaps/basemaps.tsx +128 -0
- package/src/components/basemaps/const.ts +13 -0
- package/src/components/basemaps/group-wrapper.test.tsx +38 -0
- package/src/components/basemaps/group-wrapper.tsx +28 -0
- package/src/components/basemaps/group.test.tsx +52 -0
- package/src/components/basemaps/group.tsx +42 -0
- package/src/components/basemaps/header.test.tsx +54 -0
- package/src/components/basemaps/header.tsx +36 -0
- package/src/components/basemaps/styles.ts +76 -0
- package/src/components/basemaps/types.ts +30 -0
- package/src/components/common-types.ts +1 -0
- package/src/components/geolocation-controls/const.ts +6 -0
- package/src/components/geolocation-controls/geolocation-controls.test.tsx +133 -0
- package/src/components/geolocation-controls/geolocation-controls.tsx +95 -0
- package/src/components/geolocation-controls/types.ts +17 -0
- package/src/components/index.ts +64 -0
- package/src/components/lasso-tool/chip.tsx +37 -0
- package/src/components/lasso-tool/const.tsx +70 -0
- package/src/components/lasso-tool/icons.tsx +91 -0
- package/src/components/lasso-tool/lasso-tool-inline.test.tsx +168 -0
- package/src/components/lasso-tool/lasso-tool-inline.tsx +245 -0
- package/src/components/lasso-tool/lasso-tool.test.tsx +212 -0
- package/src/components/lasso-tool/lasso-tool.tsx +479 -0
- package/src/components/lasso-tool/styles.ts +143 -0
- package/src/components/lasso-tool/types.ts +114 -0
- package/src/components/list-data/list-data-skeleton.test.tsx +10 -0
- package/src/components/list-data/list-data-skeleton.tsx +40 -0
- package/src/components/list-data/list-data.test.tsx +94 -0
- package/src/components/list-data/list-data.tsx +106 -0
- package/src/components/list-data/styles.ts +37 -0
- package/src/components/list-data/types.ts +25 -0
- package/src/components/measurement-tools/const.tsx +108 -0
- package/src/components/measurement-tools/icons.tsx +54 -0
- package/src/components/measurement-tools/measurement-tools.test.tsx +165 -0
- package/src/components/measurement-tools/measurement-tools.tsx +443 -0
- package/src/components/measurement-tools/styles.ts +91 -0
- package/src/components/measurement-tools/types.ts +77 -0
- package/src/components/no-data-alert/no-data-alert.test.tsx +31 -0
- package/src/components/no-data-alert/no-data-alert.tsx +59 -0
- package/src/components/responsive-drawer/responsive-drawer.test.tsx +91 -0
- package/src/components/responsive-drawer/responsive-drawer.tsx +53 -0
- package/src/components/smart-tooltip/smart-tooltip.test.tsx +168 -0
- package/src/components/smart-tooltip/smart-tooltip.tsx +40 -0
- package/src/components/tooltip/tooltip.test.tsx +86 -0
- package/src/components/tooltip/tooltip.tsx +30 -0
- package/src/components/types.ts +1 -0
- package/src/components/zoom-controls/styles.ts +27 -0
- package/src/components/zoom-controls/types.ts +19 -0
- package/src/components/zoom-controls/zoom-controls.test.tsx +101 -0
- package/src/components/zoom-controls/zoom-controls.tsx +114 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-debounce.ts +55 -0
- package/src/hooks/use-skeleton.test.tsx +32 -0
- package/src/hooks/use-skeleton.ts +19 -0
- package/src/hooks/use-widget-ref.ts +33 -0
- package/src/widgets/README.md +277 -0
- package/src/widgets/_shared/chart-config/config-factory.ts +67 -0
- package/src/widgets/_shared/chart-config/csv-modifiers.ts +56 -0
- package/src/widgets/_shared/chart-config/index.ts +21 -0
- package/src/widgets/_shared/chart-config/option-builders.ts +203 -0
- package/src/widgets/_shared/skeleton/index.ts +5 -0
- package/src/widgets/_shared/skeleton/styles.ts +20 -0
- package/src/widgets/actions/change-column/change-column-icon.tsx +10 -0
- package/src/widgets/actions/change-column/change-column.test.tsx +163 -0
- package/src/widgets/actions/change-column/change-column.tsx +141 -0
- package/src/widgets/actions/change-column/sortable-column-item.tsx +49 -0
- package/src/widgets/actions/change-column/types.ts +20 -0
- package/src/widgets/actions/download/download.test.tsx +322 -0
- package/src/widgets/actions/download/download.tsx +118 -0
- package/src/widgets/actions/download/exports.test.tsx +275 -0
- package/src/widgets/actions/download/exports.tsx +103 -0
- package/src/widgets/actions/download/types.ts +21 -0
- package/src/widgets/actions/fullscreen/fullscreen.test.tsx +269 -0
- package/src/widgets/actions/fullscreen/fullscreen.tsx +82 -0
- package/src/widgets/actions/fullscreen/styles.ts +17 -0
- package/src/widgets/actions/fullscreen/types.ts +27 -0
- package/src/widgets/actions/index.ts +51 -0
- package/src/widgets/actions/lock-selection/lock-selection.test.tsx +186 -0
- package/src/widgets/actions/lock-selection/lock-selection.tsx +133 -0
- package/src/widgets/actions/lock-selection/types.ts +41 -0
- package/src/widgets/actions/relative-data/relative-data.test.tsx +267 -0
- package/src/widgets/actions/relative-data/relative-data.tsx +133 -0
- package/src/widgets/actions/relative-data/style.ts +9 -0
- package/src/widgets/actions/relative-data/types.ts +31 -0
- package/src/widgets/actions/relative-data/utils.test.ts +223 -0
- package/src/widgets/actions/relative-data/utils.ts +58 -0
- package/src/widgets/actions/searcher/searcher-toggle.test.tsx +354 -0
- package/src/widgets/actions/searcher/searcher-toggle.tsx +73 -0
- package/src/widgets/actions/searcher/searcher.tsx +205 -0
- package/src/widgets/actions/searcher/types.ts +72 -0
- package/src/widgets/actions/shared/styles.ts +12 -0
- package/src/widgets/actions/stack-toggle/grouped-bar-chart-icon.tsx +14 -0
- package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +270 -0
- package/src/widgets/actions/stack-toggle/stack-toggle.tsx +146 -0
- package/src/widgets/actions/stack-toggle/types.ts +29 -0
- package/src/widgets/actions/zoom-toggle/index.ts +2 -0
- package/src/widgets/actions/zoom-toggle/style.ts +14 -0
- package/src/widgets/actions/zoom-toggle/types.ts +44 -0
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +186 -0
- package/src/widgets/bar/config.ts +122 -0
- package/src/widgets/bar/index.ts +9 -0
- package/src/widgets/bar/skeleton.tsx +60 -0
- package/src/widgets/bar/style.ts +33 -0
- package/src/widgets/bar/types.ts +16 -0
- package/src/widgets/category/category-ui.test.tsx +399 -0
- package/src/widgets/category/category-ui.tsx +156 -0
- package/src/widgets/category/components/category-bar.tsx +28 -0
- package/src/widgets/category/components/category-legend.tsx +30 -0
- package/src/widgets/category/components/category-row-multi.tsx +50 -0
- package/src/widgets/category/components/category-row-other.tsx +23 -0
- package/src/widgets/category/components/category-row-single.tsx +47 -0
- package/src/widgets/category/components/index.ts +14 -0
- package/src/widgets/category/config.ts +85 -0
- package/src/widgets/category/index.ts +30 -0
- package/src/widgets/category/skeleton.tsx +24 -0
- package/src/widgets/category/style.ts +133 -0
- package/src/widgets/category/types.ts +40 -0
- package/src/widgets/echart/const.ts +1 -0
- package/src/widgets/echart/echart-ui.test.tsx +537 -0
- package/src/widgets/echart/echart-ui.tsx +92 -0
- package/src/widgets/echart/echart.test.tsx +562 -0
- package/src/widgets/echart/echart.tsx +68 -0
- package/src/widgets/echart/index.ts +16 -0
- package/src/widgets/echart/options.ts +53 -0
- package/src/widgets/echart/types.ts +45 -0
- package/src/widgets/echart/utils.ts +169 -0
- package/src/widgets/error/error.test.tsx +331 -0
- package/src/widgets/error/error.tsx +40 -0
- package/src/widgets/error/index.ts +2 -0
- package/src/widgets/error/types.ts +14 -0
- package/src/widgets/formula/components/item.test.tsx +249 -0
- package/src/widgets/formula/components/item.tsx +18 -0
- package/src/widgets/formula/components/prefix.test.tsx +341 -0
- package/src/widgets/formula/components/prefix.tsx +18 -0
- package/src/widgets/formula/components/row.test.tsx +364 -0
- package/src/widgets/formula/components/row.tsx +21 -0
- package/src/widgets/formula/components/series.tsx +34 -0
- package/src/widgets/formula/components/suffix.test.tsx +383 -0
- package/src/widgets/formula/components/suffix.tsx +28 -0
- package/src/widgets/formula/components/value.test.tsx +329 -0
- package/src/widgets/formula/components/value.tsx +29 -0
- package/src/widgets/formula/config.ts +27 -0
- package/src/widgets/formula/formula-ui.test.tsx +399 -0
- package/src/widgets/formula/formula-ui.tsx +27 -0
- package/src/widgets/formula/index.ts +24 -0
- package/src/widgets/formula/serializer.test.tsx +144 -0
- package/src/widgets/formula/serializer.ts +28 -0
- package/src/widgets/formula/skeleton.tsx +10 -0
- package/src/widgets/formula/style.ts +23 -0
- package/src/widgets/formula/types.ts +50 -0
- package/src/widgets/histogram/config.ts +143 -0
- package/src/widgets/histogram/index.ts +8 -0
- package/src/widgets/histogram/skeleton.tsx +52 -0
- package/src/widgets/histogram/style.ts +8 -0
- package/src/widgets/histogram/types.ts +17 -0
- package/src/widgets/index.ts +25 -0
- package/src/widgets/loader/index.ts +4 -0
- package/src/widgets/loader/loader.tsx +70 -0
- package/src/widgets/loader/types.ts +11 -0
- package/src/widgets/loader/utils.test.ts +112 -0
- package/src/widgets/loader/utils.ts +35 -0
- package/src/widgets/markdown/config.ts +18 -0
- package/src/widgets/markdown/index.ts +14 -0
- package/src/widgets/markdown/markdown-ui.test.tsx +341 -0
- package/src/widgets/markdown/markdown-ui.tsx +52 -0
- package/src/widgets/markdown/markdown.tsx +20 -0
- package/src/widgets/markdown/skeleton.tsx +12 -0
- package/src/widgets/markdown/style.ts +28 -0
- package/src/widgets/markdown/types.ts +28 -0
- package/src/widgets/no-data/index.ts +2 -0
- package/src/widgets/no-data/no-data.test.tsx +447 -0
- package/src/widgets/no-data/no-data.tsx +116 -0
- package/src/widgets/no-data/style.ts +18 -0
- package/src/widgets/no-data/types.ts +72 -0
- package/src/widgets/note/index.ts +2 -0
- package/src/widgets/note/note.test.tsx +391 -0
- package/src/widgets/note/note.tsx +114 -0
- package/src/widgets/note/style.ts +29 -0
- package/src/widgets/note/types.ts +9 -0
- package/src/widgets/pie/config.ts +177 -0
- package/src/widgets/pie/index.ts +8 -0
- package/src/widgets/pie/skeleton.tsx +70 -0
- package/src/widgets/pie/style.ts +8 -0
- package/src/widgets/pie/types.ts +16 -0
- package/src/widgets/range/components/range-item.tsx +213 -0
- package/src/widgets/range/config.ts +10 -0
- package/src/widgets/range/index.ts +16 -0
- package/src/widgets/range/range-ui.test.tsx +203 -0
- package/src/widgets/range/range-ui.tsx +11 -0
- package/src/widgets/range/serializer.test.ts +70 -0
- package/src/widgets/range/serializer.ts +27 -0
- package/src/widgets/range/skeleton.tsx +14 -0
- package/src/widgets/range/style.ts +37 -0
- package/src/widgets/range/types.ts +39 -0
- package/src/widgets/scatterplot/config.ts +138 -0
- package/src/widgets/scatterplot/index.ts +8 -0
- package/src/widgets/scatterplot/skeleton.tsx +59 -0
- package/src/widgets/scatterplot/style.ts +21 -0
- package/src/widgets/scatterplot/types.ts +17 -0
- package/src/widgets/selection-summary/index.ts +6 -0
- package/src/widgets/selection-summary/selection-summary.tsx +46 -0
- package/src/widgets/selection-summary/style.ts +10 -0
- package/src/widgets/selection-summary/types.ts +14 -0
- package/src/widgets/skeleton-loader/index.ts +2 -0
- package/src/widgets/skeleton-loader/skeleton-loader.test.tsx +139 -0
- package/src/widgets/skeleton-loader/skeleton-loader.tsx +28 -0
- package/src/widgets/skeleton-loader/types.ts +8 -0
- package/src/widgets/spread/components/max-value.tsx +29 -0
- package/src/widgets/spread/components/min-value.tsx +29 -0
- package/src/widgets/spread/components/separator.tsx +6 -0
- package/src/widgets/spread/config.ts +34 -0
- package/src/widgets/spread/index.ts +23 -0
- package/src/widgets/spread/skeleton.tsx +10 -0
- package/src/widgets/spread/spread-ui.test.tsx +368 -0
- package/src/widgets/spread/spread-ui.tsx +29 -0
- package/src/widgets/spread/style.ts +22 -0
- package/src/widgets/spread/types.ts +47 -0
- package/src/widgets/stores/index.ts +9 -0
- package/src/widgets/stores/types.ts +192 -0
- package/src/widgets/stores/widget-store.test.ts +601 -0
- package/src/widgets/stores/widget-store.ts +239 -0
- package/src/widgets/subheader/index.ts +3 -0
- package/src/widgets/subheader/style.ts +20 -0
- package/src/widgets/subheader/subheader.test.tsx +45 -0
- package/src/widgets/subheader/subheader.tsx +16 -0
- package/src/widgets/subheader/types.ts +11 -0
- package/src/widgets/table/components/cell-header.tsx +58 -0
- package/src/widgets/table/components/cell.tsx +80 -0
- package/src/widgets/table/components/index.ts +4 -0
- package/src/widgets/table/components/pagination-actions.tsx +67 -0
- package/src/widgets/table/components/pagination.tsx +41 -0
- package/src/widgets/table/components/row.tsx +60 -0
- package/src/widgets/table/config.ts +71 -0
- package/src/widgets/table/helpers.test.ts +244 -0
- package/src/widgets/table/helpers.ts +107 -0
- package/src/widgets/table/hooks/index.ts +7 -0
- package/src/widgets/table/hooks/use-pagination.test.ts +294 -0
- package/src/widgets/table/hooks/use-pagination.ts +155 -0
- package/src/widgets/table/hooks/use-selection.test.ts +504 -0
- package/src/widgets/table/hooks/use-selection.ts +189 -0
- package/src/widgets/table/hooks/use-sort.test.ts +296 -0
- package/src/widgets/table/hooks/use-sort.ts +138 -0
- package/src/widgets/table/index.ts +53 -0
- package/src/widgets/table/serializer.ts +54 -0
- package/src/widgets/table/skeleton.tsx +48 -0
- package/src/widgets/table/style.ts +34 -0
- package/src/widgets/table/table-ui.tsx +64 -0
- package/src/widgets/table/types.ts +223 -0
- package/src/widgets/timeseries/config.ts +135 -0
- package/src/widgets/timeseries/index.ts +8 -0
- package/src/widgets/timeseries/skeleton.tsx +55 -0
- package/src/widgets/timeseries/style.ts +36 -0
- package/src/widgets/timeseries/types.ts +17 -0
- package/src/widgets/toolbar-actions/index.ts +6 -0
- package/src/widgets/toolbar-actions/styles.ts +38 -0
- package/src/widgets/toolbar-actions/toolbar-actions.test.tsx +691 -0
- package/src/widgets/toolbar-actions/toolbar-actions.tsx +145 -0
- package/src/widgets/toolbar-actions/types.ts +60 -0
- package/src/widgets/wrapper/components/actions.test.tsx +101 -0
- package/src/widgets/wrapper/components/actions.tsx +30 -0
- package/src/widgets/wrapper/components/options.test.tsx +323 -0
- package/src/widgets/wrapper/components/options.tsx +73 -0
- package/src/widgets/wrapper/components/title.test.tsx +126 -0
- package/src/widgets/wrapper/components/title.tsx +32 -0
- package/src/widgets/wrapper/index.ts +16 -0
- package/src/widgets/wrapper/styles.ts +98 -0
- package/src/widgets/wrapper/types.ts +55 -0
- package/src/widgets/wrapper/wrapper-ui.test.tsx +232 -0
- package/src/widgets/wrapper/wrapper-ui.tsx +57 -0
- package/src/widgets/wrapper/wrapper.test.tsx +365 -0
- package/src/widgets/wrapper/wrapper.tsx +50 -0
- package/dist/lasso-tool-BwRzEW7k.js.map +0 -1
- package/dist/types/common/common.d.ts +0 -3
- package/dist/types/common/index.d.ts +0 -26
- package/dist/types/common/lasso-tools.d.ts +0 -36
- package/dist/types/common/measurement-tools.d.ts +0 -65
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { SpreadUI } from './spread-ui'
|
|
4
|
+
import { useWidgetStore } from '../stores/widget-store'
|
|
5
|
+
import type { SpreadWidgetState } from './types'
|
|
6
|
+
|
|
7
|
+
describe('SpreadUI', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
useWidgetStore.getState().clearWidgets()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('renders empty container when widget does not exist', () => {
|
|
13
|
+
const { container } = render(<SpreadUI id='non-existent' />)
|
|
14
|
+
// Box wrapper is rendered but Row returns null for missing widget
|
|
15
|
+
expect(container.firstChild?.childNodes.length).toBe(0)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('renders with single spread data item', () => {
|
|
19
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
20
|
+
type: 'spread',
|
|
21
|
+
data: [{ min: 2.1, max: 2.23, suffix: '€' }],
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
render(<SpreadUI id='test-spread' />)
|
|
25
|
+
|
|
26
|
+
expect(screen.getByText('2.1')).toBeTruthy()
|
|
27
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
28
|
+
expect(screen.getByText('2.23')).toBeTruthy()
|
|
29
|
+
expect(screen.getByText('€')).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('renders with multiple spread data items', () => {
|
|
33
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
34
|
+
type: 'spread',
|
|
35
|
+
data: [
|
|
36
|
+
{ min: 1.5, max: 2.5, prefix: '$' },
|
|
37
|
+
{ min: 100, max: 200, suffix: '%' },
|
|
38
|
+
{ min: 0, max: 10 },
|
|
39
|
+
],
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
render(<SpreadUI id='test-spread' />)
|
|
43
|
+
|
|
44
|
+
expect(screen.getByText('$')).toBeTruthy()
|
|
45
|
+
expect(screen.getByText('1.5')).toBeTruthy()
|
|
46
|
+
expect(screen.getByText('2.5')).toBeTruthy()
|
|
47
|
+
expect(screen.getByText('100')).toBeTruthy()
|
|
48
|
+
expect(screen.getByText('200')).toBeTruthy()
|
|
49
|
+
expect(screen.getByText('%')).toBeTruthy()
|
|
50
|
+
expect(screen.getByText('0')).toBeTruthy()
|
|
51
|
+
expect(screen.getByText('10')).toBeTruthy()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('renders prefix for each item', () => {
|
|
55
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
56
|
+
type: 'spread',
|
|
57
|
+
data: [
|
|
58
|
+
{ min: 1, max: 10, prefix: 'Range 1:' },
|
|
59
|
+
{ min: 20, max: 30, prefix: 'Range 2:' },
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
render(<SpreadUI id='test-spread' />)
|
|
64
|
+
|
|
65
|
+
expect(screen.getByText('Range 1:')).toBeTruthy()
|
|
66
|
+
expect(screen.getByText('Range 2:')).toBeTruthy()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('renders separator between min and max values', () => {
|
|
70
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
71
|
+
type: 'spread',
|
|
72
|
+
data: [{ min: 5, max: 15 }],
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
render(<SpreadUI id='test-spread' />)
|
|
76
|
+
|
|
77
|
+
// Check for separator "-"
|
|
78
|
+
const separators = screen.getAllByText('-')
|
|
79
|
+
expect(separators.length).toBe(1)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('renders in correct order: Prefix, MinValue, Separator, MaxValue, Suffix', () => {
|
|
83
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
84
|
+
type: 'spread',
|
|
85
|
+
data: [{ min: 10, max: 20, prefix: '$', suffix: 'USD' }],
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
89
|
+
|
|
90
|
+
const items = container.querySelectorAll('.MuiTypography-root')
|
|
91
|
+
expect(items.length).toBe(5)
|
|
92
|
+
expect(items[0]?.textContent).toBe('$')
|
|
93
|
+
expect(items[1]?.textContent).toBe('10')
|
|
94
|
+
expect(items[2]?.textContent).toBe('-')
|
|
95
|
+
expect(items[3]?.textContent).toBe('20')
|
|
96
|
+
expect(items[4]?.textContent).toBe('USD')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('handles data without prefix', () => {
|
|
100
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
101
|
+
type: 'spread',
|
|
102
|
+
data: [{ min: 5, max: 15, suffix: 'km' }],
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
render(<SpreadUI id='test-spread' />)
|
|
106
|
+
|
|
107
|
+
expect(screen.getByText('5')).toBeTruthy()
|
|
108
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
109
|
+
expect(screen.getByText('15')).toBeTruthy()
|
|
110
|
+
expect(screen.getByText('km')).toBeTruthy()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('handles data without suffix', () => {
|
|
114
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
115
|
+
type: 'spread',
|
|
116
|
+
data: [{ min: 5, max: 15, prefix: '$' }],
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
render(<SpreadUI id='test-spread' />)
|
|
120
|
+
|
|
121
|
+
expect(screen.getByText('$')).toBeTruthy()
|
|
122
|
+
expect(screen.getByText('5')).toBeTruthy()
|
|
123
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
124
|
+
expect(screen.getByText('15')).toBeTruthy()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test('handles data with only min and max values', () => {
|
|
128
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
129
|
+
type: 'spread',
|
|
130
|
+
data: [{ min: 5, max: 15 }],
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
render(<SpreadUI id='test-spread' />)
|
|
134
|
+
|
|
135
|
+
expect(screen.getByText('5')).toBeTruthy()
|
|
136
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
137
|
+
expect(screen.getByText('15')).toBeTruthy()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('handles empty data array', () => {
|
|
141
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
142
|
+
type: 'spread',
|
|
143
|
+
data: [],
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
147
|
+
|
|
148
|
+
// Row component returns null for empty data, Box wrapper still rendered
|
|
149
|
+
expect(container.firstChild?.childNodes.length).toBe(0)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test('handles undefined data', () => {
|
|
153
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
154
|
+
type: 'spread',
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
158
|
+
|
|
159
|
+
// Row component returns null for undefined data, Box wrapper still rendered
|
|
160
|
+
expect(container.firstChild?.childNodes.length).toBe(0)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('renders with data containing colors', () => {
|
|
164
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
165
|
+
type: 'spread',
|
|
166
|
+
data: [
|
|
167
|
+
{ min: 1, max: 10, color: '#FF0000' },
|
|
168
|
+
{ min: 20, max: 30, color: '#00FF00' },
|
|
169
|
+
],
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
render(<SpreadUI id='test-spread' />)
|
|
173
|
+
|
|
174
|
+
expect(screen.getByText('1')).toBeTruthy()
|
|
175
|
+
expect(screen.getByText('10')).toBeTruthy()
|
|
176
|
+
expect(screen.getByText('20')).toBeTruthy()
|
|
177
|
+
expect(screen.getByText('30')).toBeTruthy()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('renders with negative min value', () => {
|
|
181
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
182
|
+
type: 'spread',
|
|
183
|
+
data: [{ min: -10, max: 5 }],
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
render(<SpreadUI id='test-spread' />)
|
|
187
|
+
|
|
188
|
+
expect(screen.getByText('-10')).toBeTruthy()
|
|
189
|
+
expect(screen.getByText('-')).toBeTruthy()
|
|
190
|
+
expect(screen.getByText('5')).toBeTruthy()
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('renders with zero values', () => {
|
|
194
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
195
|
+
type: 'spread',
|
|
196
|
+
data: [{ min: 0, max: 0 }],
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
render(<SpreadUI id='test-spread' />)
|
|
200
|
+
|
|
201
|
+
const zeros = screen.getAllByText('0')
|
|
202
|
+
expect(zeros.length).toBe(2) // min and max both are 0
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test('renders with decimal values', () => {
|
|
206
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
207
|
+
type: 'spread',
|
|
208
|
+
data: [{ min: 2.1, max: 2.23, suffix: '€' }],
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
render(<SpreadUI id='test-spread' />)
|
|
212
|
+
|
|
213
|
+
expect(screen.getByText('2.1')).toBeTruthy()
|
|
214
|
+
expect(screen.getByText('2.23')).toBeTruthy()
|
|
215
|
+
expect(screen.getByText('€')).toBeTruthy()
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('renders with large values', () => {
|
|
219
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
220
|
+
type: 'spread',
|
|
221
|
+
data: [{ min: 1000000, max: 2000000, prefix: '$' }],
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
render(<SpreadUI id='test-spread' />)
|
|
225
|
+
|
|
226
|
+
expect(screen.getByText('$')).toBeTruthy()
|
|
227
|
+
expect(screen.getByText('1000000')).toBeTruthy()
|
|
228
|
+
expect(screen.getByText('2000000')).toBeTruthy()
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('updates when widget data changes', () => {
|
|
232
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
233
|
+
type: 'spread',
|
|
234
|
+
data: [{ min: 1, max: 10 }],
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
const { rerender } = render(<SpreadUI id='test-spread' />)
|
|
238
|
+
expect(screen.getByText('1')).toBeTruthy()
|
|
239
|
+
expect(screen.getByText('10')).toBeTruthy()
|
|
240
|
+
|
|
241
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
242
|
+
data: [{ min: 20, max: 30 }],
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
rerender(<SpreadUI id='test-spread' />)
|
|
246
|
+
expect(screen.queryByText('1')).toBeNull()
|
|
247
|
+
expect(screen.queryByText('10')).toBeNull()
|
|
248
|
+
expect(screen.getByText('20')).toBeTruthy()
|
|
249
|
+
expect(screen.getByText('30')).toBeTruthy()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test('handles widget removal gracefully', () => {
|
|
253
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
254
|
+
type: 'spread',
|
|
255
|
+
data: [{ min: 1, max: 10 }],
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
const { container, rerender } = render(<SpreadUI id='test-spread' />)
|
|
259
|
+
expect(screen.getByText('1')).toBeTruthy()
|
|
260
|
+
|
|
261
|
+
useWidgetStore.getState().removeWidget('test-spread')
|
|
262
|
+
|
|
263
|
+
rerender(<SpreadUI id='test-spread' />)
|
|
264
|
+
// Box wrapper still rendered but Row returns null
|
|
265
|
+
expect(container.firstChild?.childNodes.length).toBe(0)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
test('renders multiple rows for multiple data items', () => {
|
|
269
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
270
|
+
type: 'spread',
|
|
271
|
+
data: [
|
|
272
|
+
{ min: 1, max: 10, prefix: 'A' },
|
|
273
|
+
{ min: 20, max: 30, prefix: 'B' },
|
|
274
|
+
{ min: 40, max: 50, prefix: 'C' },
|
|
275
|
+
],
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
279
|
+
|
|
280
|
+
// Each data item should create a row
|
|
281
|
+
const rows = container.querySelectorAll('[class*="MuiBox-root"]')
|
|
282
|
+
expect(rows.length).toBeGreaterThan(0)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
test('renders with series configuration', () => {
|
|
286
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
287
|
+
type: 'spread',
|
|
288
|
+
data: [{ min: 10, max: 100, suffix: '€' }],
|
|
289
|
+
series: [{ name: 'Revenue', color: '#FF5733' }],
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
render(<SpreadUI id='test-spread' />)
|
|
293
|
+
|
|
294
|
+
// Series should display the first letter of the name
|
|
295
|
+
expect(screen.getByText('R')).toBeTruthy()
|
|
296
|
+
expect(screen.getByText('10')).toBeTruthy()
|
|
297
|
+
expect(screen.getByText('100')).toBeTruthy()
|
|
298
|
+
expect(screen.getByText('€')).toBeTruthy()
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
test('renders without series when not provided', () => {
|
|
302
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
303
|
+
type: 'spread',
|
|
304
|
+
data: [{ min: 5, max: 25 }],
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
308
|
+
|
|
309
|
+
// Should not have any Avatar elements (series)
|
|
310
|
+
const avatars = container.querySelectorAll('.MuiAvatar-root')
|
|
311
|
+
expect(avatars.length).toBe(0)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('renders series with default color when color not provided', () => {
|
|
315
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
316
|
+
type: 'spread',
|
|
317
|
+
data: [{ min: 0, max: 50 }],
|
|
318
|
+
series: [{ name: 'Test' }],
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
render(<SpreadUI id='test-spread' />)
|
|
322
|
+
|
|
323
|
+
// Series should display the first letter
|
|
324
|
+
expect(screen.getByText('T')).toBeTruthy()
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
test('renders with array of series matching data indices', () => {
|
|
328
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
329
|
+
type: 'spread',
|
|
330
|
+
data: [
|
|
331
|
+
{ min: 10, max: 100 },
|
|
332
|
+
{ min: 20, max: 200 },
|
|
333
|
+
{ min: 30, max: 300 },
|
|
334
|
+
],
|
|
335
|
+
series: [
|
|
336
|
+
{ name: 'Alpha', color: '#FF0000' },
|
|
337
|
+
{ name: 'Beta', color: '#00FF00' },
|
|
338
|
+
{ name: 'Gamma', color: '#0000FF' },
|
|
339
|
+
],
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
render(<SpreadUI id='test-spread' />)
|
|
343
|
+
|
|
344
|
+
// Each series should display its first letter
|
|
345
|
+
expect(screen.getByText('A')).toBeTruthy()
|
|
346
|
+
expect(screen.getByText('B')).toBeTruthy()
|
|
347
|
+
expect(screen.getByText('G')).toBeTruthy()
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
test('renders partial series array - only shows series for available indices', () => {
|
|
351
|
+
useWidgetStore.getState().setWidget<SpreadWidgetState>('test-spread', {
|
|
352
|
+
type: 'spread',
|
|
353
|
+
data: [
|
|
354
|
+
{ min: 10, max: 100 },
|
|
355
|
+
{ min: 20, max: 200 },
|
|
356
|
+
{ min: 30, max: 300 },
|
|
357
|
+
],
|
|
358
|
+
series: [{ name: 'Only First', color: '#FF0000' }],
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
const { container } = render(<SpreadUI id='test-spread' />)
|
|
362
|
+
|
|
363
|
+
// Only one series avatar should be rendered
|
|
364
|
+
const avatars = container.querySelectorAll('.MuiAvatar-root')
|
|
365
|
+
expect(avatars.length).toBe(1)
|
|
366
|
+
expect(screen.getByText('O')).toBeTruthy()
|
|
367
|
+
})
|
|
368
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SpreadUIProps } from './types'
|
|
2
|
+
|
|
3
|
+
import { Separator } from './components/separator'
|
|
4
|
+
import { MinValue } from './components/min-value'
|
|
5
|
+
import { MaxValue } from './components/max-value'
|
|
6
|
+
import { Prefix, Row, Suffix, Series } from '../formula'
|
|
7
|
+
import { useWidgetRef } from '../../hooks'
|
|
8
|
+
import { Box } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
export function SpreadUI(props: SpreadUIProps) {
|
|
11
|
+
const ref = useWidgetRef(props.id)
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Box ref={ref}>
|
|
15
|
+
<Row id={props.id}>
|
|
16
|
+
{({ index }) => (
|
|
17
|
+
<>
|
|
18
|
+
<Series id={props.id} index={index} />
|
|
19
|
+
<Prefix id={props.id} index={index} />
|
|
20
|
+
<MinValue id={props.id} index={index} />
|
|
21
|
+
<Separator />
|
|
22
|
+
<MaxValue id={props.id} index={index} />
|
|
23
|
+
<Suffix id={props.id} index={index} />
|
|
24
|
+
</>
|
|
25
|
+
)}
|
|
26
|
+
</Row>
|
|
27
|
+
</Box>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export const styles = {
|
|
4
|
+
root: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'column',
|
|
7
|
+
gap: (theme: Theme) => theme.spacing(2),
|
|
8
|
+
},
|
|
9
|
+
item: {
|
|
10
|
+
'&[data-disabled="true"]': {
|
|
11
|
+
color: (theme: Theme) => theme.palette.text.disabled,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
row: {
|
|
15
|
+
display: 'flex',
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
gap: (theme: Theme) => theme.spacing(0.25),
|
|
18
|
+
'& + &': {
|
|
19
|
+
marginTop: (theme: Theme) => theme.spacing(1),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
} satisfies Record<string, SxProps<Theme>>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { TypographyProps } from '@mui/material'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import type { BaseWidgetState, WidgetsStoreProps } from '../stores/types'
|
|
4
|
+
import type { WrapperState } from '../wrapper/types'
|
|
5
|
+
import type { DownloadItem } from '../actions/download/types'
|
|
6
|
+
import type { SeriesConfig } from '../formula/types'
|
|
7
|
+
|
|
8
|
+
export interface SpreadUIProps {
|
|
9
|
+
id: WidgetsStoreProps['id']
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SpreadDataItem {
|
|
13
|
+
min: number
|
|
14
|
+
max: number
|
|
15
|
+
prefix?: string | ReactNode
|
|
16
|
+
suffix?: string | ReactNode
|
|
17
|
+
color?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RowProps {
|
|
21
|
+
id: WidgetsStoreProps['id']
|
|
22
|
+
children: ReactNode | ((props: { index: number }) => ReactNode)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ValueProps extends Omit<ItemProps, 'children'> {
|
|
26
|
+
id: WidgetsStoreProps['id']
|
|
27
|
+
index?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ItemProps {
|
|
31
|
+
children: ReactNode
|
|
32
|
+
disabled?: boolean
|
|
33
|
+
TypographyProps?: TypographyProps
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type SpreadWidgetData = SpreadDataItem[]
|
|
37
|
+
|
|
38
|
+
export type SpreadWidgetState = BaseWidgetState<
|
|
39
|
+
WrapperState<SpreadWidgetConfig> & { data: SpreadWidgetData }
|
|
40
|
+
>
|
|
41
|
+
|
|
42
|
+
export interface SpreadWidgetConfig {
|
|
43
|
+
formatter?: (value: number) => string
|
|
44
|
+
series?: SeriesConfig[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type SpreadDownloadConfig = DownloadItem<SpreadWidgetData>[]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { RefObject } from 'react'
|
|
2
|
+
|
|
3
|
+
export interface WidgetsStoreProps {
|
|
4
|
+
/** Unique identifier for the widget */
|
|
5
|
+
id: string
|
|
6
|
+
/** Type of widget */
|
|
7
|
+
type: string
|
|
8
|
+
/** Widget data - flexible to accommodate different widget types */
|
|
9
|
+
data: unknown
|
|
10
|
+
/** Loading state */
|
|
11
|
+
isLoading: boolean
|
|
12
|
+
/** Fetching state (e.g., for async data) */
|
|
13
|
+
isFetching: boolean
|
|
14
|
+
/** Error message if any */
|
|
15
|
+
error?: {
|
|
16
|
+
title?: string
|
|
17
|
+
message?: string
|
|
18
|
+
}
|
|
19
|
+
/** Whether widget is visible */
|
|
20
|
+
visible?: boolean
|
|
21
|
+
/** Reference to the widget ui instance */
|
|
22
|
+
refUI?: RefObject<HTMLElement | null>
|
|
23
|
+
/** Registered tools for the widget's transformation pipeline */
|
|
24
|
+
registeredTools?: ToolRegistration[]
|
|
25
|
+
/** Formatter function for widget values */
|
|
26
|
+
formatter?: (value: number) => string
|
|
27
|
+
/** Locale for number formatting (e.g., 'en-US', 'es-ES', 'fr-FR') */
|
|
28
|
+
locale?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Tool transformation function type
|
|
33
|
+
* Can be synchronous or asynchronous to support remote operations
|
|
34
|
+
*/
|
|
35
|
+
export type ToolTransformFunction = (
|
|
36
|
+
data: unknown,
|
|
37
|
+
config?: Record<string, unknown>,
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
39
|
+
) => Promise<unknown> | unknown
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Tool registration for widget pipeline
|
|
43
|
+
*
|
|
44
|
+
* @example Basic tool registration
|
|
45
|
+
* ```typescript
|
|
46
|
+
* registerTool(widgetId, {
|
|
47
|
+
* id: 'searcher',
|
|
48
|
+
* order: 10,
|
|
49
|
+
* enabled: true,
|
|
50
|
+
* fn: (data) => filterData(data),
|
|
51
|
+
* })
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Tool with dependencies
|
|
55
|
+
* ```typescript
|
|
56
|
+
* registerTool(widgetId, {
|
|
57
|
+
* id: 'lock-selection',
|
|
58
|
+
* order: 20,
|
|
59
|
+
* enabled: true,
|
|
60
|
+
* fn: (data) => lockData(data),
|
|
61
|
+
* disables: ['searcher', 'relative-data'], // Disable these when active
|
|
62
|
+
* })
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Dependency Management:
|
|
66
|
+
* - Multiple tools can disable the same target (reference counting)
|
|
67
|
+
* - Target is only re-enabled when ALL disabling tools are inactive
|
|
68
|
+
* - Original enabled state is preserved and restored
|
|
69
|
+
* - Circular dependencies are detected and throw errors
|
|
70
|
+
*/
|
|
71
|
+
export interface ToolRegistration {
|
|
72
|
+
/** Unique tool identifier (e.g., 'searcher', 'relative-data') */
|
|
73
|
+
id: string
|
|
74
|
+
/** Execution priority - lower numbers execute first */
|
|
75
|
+
order: number
|
|
76
|
+
/** Transformation function */
|
|
77
|
+
fn: ToolTransformFunction
|
|
78
|
+
/** Whether tool is currently enabled */
|
|
79
|
+
enabled: boolean
|
|
80
|
+
/** Tool-specific configuration */
|
|
81
|
+
config?: Record<string, unknown>
|
|
82
|
+
/**
|
|
83
|
+
* Array of tool IDs to disable when this tool is active.
|
|
84
|
+
* During pipeline execution, if this tool is enabled, any tools listed
|
|
85
|
+
* in this array will be excluded from the pipeline.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* disables: ['searcher', 'relative-data']
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
disables?: string[]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Base widget state interface
|
|
97
|
+
*/
|
|
98
|
+
export type BaseWidgetState<T> = WidgetsStoreProps & T
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Union type for all widget states
|
|
102
|
+
*/
|
|
103
|
+
export type WidgetState = BaseWidgetState<unknown>
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Widget store state with tool registration
|
|
107
|
+
*/
|
|
108
|
+
export interface WidgetStoreState {
|
|
109
|
+
/** Map of widget id to widget state */
|
|
110
|
+
widgets: Record<string, WidgetState>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Widget store actions
|
|
115
|
+
*/
|
|
116
|
+
export interface WidgetStoreActions {
|
|
117
|
+
/**
|
|
118
|
+
* Add or update a widget in the store
|
|
119
|
+
* @param id - Widget ID
|
|
120
|
+
* @param widget - Widget state properties to merge (accepts any object structure)
|
|
121
|
+
* @template T - Type of the widget state for type-safe access
|
|
122
|
+
*/
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
+
setWidget: <T = any>(id: WidgetState['id'], widget: Partial<T>) => void
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove a widget from the store
|
|
128
|
+
* @param id - Widget ID to remove
|
|
129
|
+
*/
|
|
130
|
+
removeWidget: (id: WidgetState['id']) => void
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Clear all widgets
|
|
134
|
+
*/
|
|
135
|
+
clearWidgets: () => void
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get widget by ID
|
|
139
|
+
* @param id - Widget ID
|
|
140
|
+
* @returns Widget state or undefined if not found
|
|
141
|
+
*/
|
|
142
|
+
getWidget: <T extends WidgetState = WidgetState>(
|
|
143
|
+
id: WidgetState['id'],
|
|
144
|
+
) => T | undefined
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Register a tool with the widget's transformation pipeline
|
|
148
|
+
* @param widgetId - Widget ID
|
|
149
|
+
* @param tool - Tool registration object
|
|
150
|
+
*/
|
|
151
|
+
registerTool: (widgetId: string, tool: ToolRegistration) => void
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Unregister a tool from the widget's transformation pipeline
|
|
155
|
+
* @param widgetId - Widget ID
|
|
156
|
+
* @param toolId - Tool ID to remove
|
|
157
|
+
*/
|
|
158
|
+
unregisterTool: (widgetId: string, toolId: string) => void
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Update tool configuration
|
|
162
|
+
* @param widgetId - Widget ID
|
|
163
|
+
* @param toolId - Tool ID
|
|
164
|
+
* @param config - New configuration to merge
|
|
165
|
+
*/
|
|
166
|
+
updateToolConfig: (
|
|
167
|
+
widgetId: string,
|
|
168
|
+
toolId: string,
|
|
169
|
+
config: Record<string, unknown>,
|
|
170
|
+
) => void
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Set tool enabled state
|
|
174
|
+
* @param widgetId - Widget ID
|
|
175
|
+
* @param toolId - Tool ID
|
|
176
|
+
* @param enabled - Whether tool should be enabled
|
|
177
|
+
*/
|
|
178
|
+
setToolEnabled: (widgetId: string, toolId: string, enabled: boolean) => void
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Execute the tool transformation pipeline
|
|
182
|
+
* Supports both synchronous and asynchronous tools
|
|
183
|
+
* @param widgetId - Widget ID
|
|
184
|
+
* @param sourceData - Original data to transform
|
|
185
|
+
*/
|
|
186
|
+
executeToolPipeline: (widgetId: string, sourceData: unknown) => Promise<void>
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Complete widget store interface
|
|
191
|
+
*/
|
|
192
|
+
export type WidgetStore = WidgetStoreState & WidgetStoreActions
|