@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,447 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import { beforeEach, describe, expect, test } from 'vitest'
|
|
3
|
+
import { WidgetNoData } from './no-data'
|
|
4
|
+
import { useWidgetStore } from '../stores/widget-store'
|
|
5
|
+
|
|
6
|
+
describe('WidgetNoData', () => {
|
|
7
|
+
const widgetId = 'test-no-data-widget'
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
useWidgetStore.getState().clearWidgets()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('when data is empty', () => {
|
|
14
|
+
test('renders NoData UI with empty array', () => {
|
|
15
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
16
|
+
isLoading: false,
|
|
17
|
+
isFetching: false,
|
|
18
|
+
data: [],
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
render(
|
|
22
|
+
<WidgetNoData id={widgetId}>
|
|
23
|
+
<div>Widget Content</div>
|
|
24
|
+
</WidgetNoData>,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
28
|
+
expect(
|
|
29
|
+
screen.getByText(/There are no results for the combination of filters/),
|
|
30
|
+
).toBeTruthy()
|
|
31
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('renders NoData UI with null data', () => {
|
|
35
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
36
|
+
isLoading: false,
|
|
37
|
+
isFetching: false,
|
|
38
|
+
data: null,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
render(
|
|
42
|
+
<WidgetNoData id={widgetId}>
|
|
43
|
+
<div>Widget Content</div>
|
|
44
|
+
</WidgetNoData>,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
48
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('renders NoData UI with undefined data', () => {
|
|
52
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
53
|
+
isLoading: false,
|
|
54
|
+
isFetching: false,
|
|
55
|
+
data: undefined,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
render(
|
|
59
|
+
<WidgetNoData id={widgetId}>
|
|
60
|
+
<div>Widget Content</div>
|
|
61
|
+
</WidgetNoData>,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
65
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('renders NoData UI with array of empty arrays (CategoryWidget pattern)', () => {
|
|
69
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
70
|
+
isLoading: false,
|
|
71
|
+
isFetching: false,
|
|
72
|
+
data: [[], []],
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
render(
|
|
76
|
+
<WidgetNoData id={widgetId}>
|
|
77
|
+
<div>Widget Content</div>
|
|
78
|
+
</WidgetNoData>,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
82
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('renders NoData UI with empty object', () => {
|
|
86
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
87
|
+
isLoading: false,
|
|
88
|
+
isFetching: false,
|
|
89
|
+
data: {},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
render(
|
|
93
|
+
<WidgetNoData id={widgetId}>
|
|
94
|
+
<div>Widget Content</div>
|
|
95
|
+
</WidgetNoData>,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
99
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('when data exists', () => {
|
|
104
|
+
test('renders children with array data', () => {
|
|
105
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
106
|
+
isLoading: false,
|
|
107
|
+
isFetching: false,
|
|
108
|
+
data: [{ value: 1 }],
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
render(
|
|
112
|
+
<WidgetNoData id={widgetId}>
|
|
113
|
+
<div>Widget Content</div>
|
|
114
|
+
</WidgetNoData>,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
118
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('renders children with object data', () => {
|
|
122
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
123
|
+
isLoading: false,
|
|
124
|
+
isFetching: false,
|
|
125
|
+
data: { content: 'Hello' },
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
render(
|
|
129
|
+
<WidgetNoData id={widgetId}>
|
|
130
|
+
<div>Widget Content</div>
|
|
131
|
+
</WidgetNoData>,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
135
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('renders children with nested array data', () => {
|
|
139
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
140
|
+
isLoading: false,
|
|
141
|
+
isFetching: false,
|
|
142
|
+
data: [[{ name: 'A', value: 1 }]],
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
render(
|
|
146
|
+
<WidgetNoData id={widgetId}>
|
|
147
|
+
<div>Widget Content</div>
|
|
148
|
+
</WidgetNoData>,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
152
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('renders children with primitive data (number)', () => {
|
|
156
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
157
|
+
isLoading: false,
|
|
158
|
+
isFetching: false,
|
|
159
|
+
data: 0,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
render(
|
|
163
|
+
<WidgetNoData id={widgetId}>
|
|
164
|
+
<div>Widget Content</div>
|
|
165
|
+
</WidgetNoData>,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
169
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('renders children with primitive data (string)', () => {
|
|
173
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
174
|
+
isLoading: false,
|
|
175
|
+
isFetching: false,
|
|
176
|
+
data: 'test',
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
render(
|
|
180
|
+
<WidgetNoData id={widgetId}>
|
|
181
|
+
<div>Widget Content</div>
|
|
182
|
+
</WidgetNoData>,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
186
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('renders children with primitive data (boolean)', () => {
|
|
190
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
191
|
+
isLoading: false,
|
|
192
|
+
isFetching: false,
|
|
193
|
+
data: false,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
render(
|
|
197
|
+
<WidgetNoData id={widgetId}>
|
|
198
|
+
<div>Widget Content</div>
|
|
199
|
+
</WidgetNoData>,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
203
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
describe('when loading or fetching', () => {
|
|
208
|
+
test('renders children when isLoading=true even if data is empty', () => {
|
|
209
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
210
|
+
isLoading: true,
|
|
211
|
+
isFetching: false,
|
|
212
|
+
data: [],
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
render(
|
|
216
|
+
<WidgetNoData id={widgetId}>
|
|
217
|
+
<div>Widget Content</div>
|
|
218
|
+
</WidgetNoData>,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
222
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('renders children when isFetching=true even if data is empty', () => {
|
|
226
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
227
|
+
isLoading: false,
|
|
228
|
+
isFetching: true,
|
|
229
|
+
data: [],
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
render(
|
|
233
|
+
<WidgetNoData id={widgetId}>
|
|
234
|
+
<div>Widget Content</div>
|
|
235
|
+
</WidgetNoData>,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
239
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('renders children when both isLoading and isFetching are true', () => {
|
|
243
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
244
|
+
isLoading: true,
|
|
245
|
+
isFetching: true,
|
|
246
|
+
data: [],
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
render(
|
|
250
|
+
<WidgetNoData id={widgetId}>
|
|
251
|
+
<div>Widget Content</div>
|
|
252
|
+
</WidgetNoData>,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
256
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
describe('customization', () => {
|
|
261
|
+
test('renders custom title', () => {
|
|
262
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
263
|
+
isLoading: false,
|
|
264
|
+
isFetching: false,
|
|
265
|
+
data: [],
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
render(
|
|
269
|
+
<WidgetNoData id={widgetId} title='No results found'>
|
|
270
|
+
<div>Widget Content</div>
|
|
271
|
+
</WidgetNoData>,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
expect(screen.getByText('No results found')).toBeTruthy()
|
|
275
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('renders custom description', () => {
|
|
279
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
280
|
+
isLoading: false,
|
|
281
|
+
isFetching: false,
|
|
282
|
+
data: [],
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
render(
|
|
286
|
+
<WidgetNoData id={widgetId} description='Try adjusting your filters'>
|
|
287
|
+
<div>Widget Content</div>
|
|
288
|
+
</WidgetNoData>,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
expect(screen.getByText('Try adjusting your filters')).toBeTruthy()
|
|
292
|
+
expect(
|
|
293
|
+
screen.queryByText(/There are no results for the combination/),
|
|
294
|
+
).toBeNull()
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
test('uses custom isEmpty function', () => {
|
|
298
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
299
|
+
isLoading: false,
|
|
300
|
+
isFetching: false,
|
|
301
|
+
data: { customField: 'value' },
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
// Custom isEmpty that treats this object as empty
|
|
305
|
+
const customIsEmpty = (data: unknown) => {
|
|
306
|
+
const d = data as { customField: string }
|
|
307
|
+
return d?.customField === 'value'
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
render(
|
|
311
|
+
<WidgetNoData id={widgetId} isEmpty={customIsEmpty}>
|
|
312
|
+
<div>Widget Content</div>
|
|
313
|
+
</WidgetNoData>,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
317
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
test('custom isEmpty returning false renders children', () => {
|
|
321
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
322
|
+
isLoading: false,
|
|
323
|
+
isFetching: false,
|
|
324
|
+
data: [],
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
// Custom isEmpty that treats empty array as having data
|
|
328
|
+
const customIsEmpty = () => false
|
|
329
|
+
|
|
330
|
+
render(
|
|
331
|
+
<WidgetNoData id={widgetId} isEmpty={customIsEmpty}>
|
|
332
|
+
<div>Widget Content</div>
|
|
333
|
+
</WidgetNoData>,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
337
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
describe('when widget does not exist in store', () => {
|
|
342
|
+
test('treats as empty and shows NoData UI', () => {
|
|
343
|
+
// Widget not initialized in store
|
|
344
|
+
render(
|
|
345
|
+
<WidgetNoData id='non-existent-widget'>
|
|
346
|
+
<div>Widget Content</div>
|
|
347
|
+
</WidgetNoData>,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
351
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
describe('reactivity to store changes', () => {
|
|
356
|
+
test('updates from NoData to content when data arrives', () => {
|
|
357
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
358
|
+
isLoading: false,
|
|
359
|
+
isFetching: false,
|
|
360
|
+
data: [],
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
const { rerender } = render(
|
|
364
|
+
<WidgetNoData id={widgetId}>
|
|
365
|
+
<div>Widget Content</div>
|
|
366
|
+
</WidgetNoData>,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
370
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
371
|
+
|
|
372
|
+
// Update with data
|
|
373
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
374
|
+
data: [{ value: 1 }],
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
// Force rerender to pick up store changes
|
|
378
|
+
rerender(
|
|
379
|
+
<WidgetNoData id={widgetId}>
|
|
380
|
+
<div>Widget Content</div>
|
|
381
|
+
</WidgetNoData>,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
385
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
test('updates from content to NoData when data becomes empty', () => {
|
|
389
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
390
|
+
isLoading: false,
|
|
391
|
+
isFetching: false,
|
|
392
|
+
data: [{ value: 1 }],
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
const { rerender } = render(
|
|
396
|
+
<WidgetNoData id={widgetId}>
|
|
397
|
+
<div>Widget Content</div>
|
|
398
|
+
</WidgetNoData>,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
402
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
403
|
+
|
|
404
|
+
// Clear data
|
|
405
|
+
useWidgetStore.getState().setWidget(widgetId, { data: [] })
|
|
406
|
+
|
|
407
|
+
// Force rerender to pick up store changes
|
|
408
|
+
rerender(
|
|
409
|
+
<WidgetNoData id={widgetId}>
|
|
410
|
+
<div>Widget Content</div>
|
|
411
|
+
</WidgetNoData>,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
415
|
+
expect(screen.queryByText('Widget Content')).toBeNull()
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
test('updates from NoData to content when loading starts', () => {
|
|
419
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
420
|
+
isLoading: false,
|
|
421
|
+
isFetching: false,
|
|
422
|
+
data: [],
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
const { rerender } = render(
|
|
426
|
+
<WidgetNoData id={widgetId}>
|
|
427
|
+
<div>Widget Content</div>
|
|
428
|
+
</WidgetNoData>,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
432
|
+
|
|
433
|
+
// Start loading
|
|
434
|
+
useWidgetStore.getState().setWidget(widgetId, { isLoading: true })
|
|
435
|
+
|
|
436
|
+
// Force rerender to pick up store changes
|
|
437
|
+
rerender(
|
|
438
|
+
<WidgetNoData id={widgetId}>
|
|
439
|
+
<div>Widget Content</div>
|
|
440
|
+
</WidgetNoData>,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
expect(screen.getByText('Widget Content')).toBeTruthy()
|
|
444
|
+
expect(screen.queryByText('No data available')).toBeNull()
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Box, Typography } from '@mui/material'
|
|
2
|
+
import { useShallow } from 'zustand/shallow'
|
|
3
|
+
import { useWidgetStore } from '../stores/widget-store'
|
|
4
|
+
import type { WidgetNoDataProps } from './types'
|
|
5
|
+
import { styles } from './style'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* NoData wrapper component that displays empty state UI when widget has no data
|
|
9
|
+
*
|
|
10
|
+
* Integrates with widget store to check loading/fetching state and data availability.
|
|
11
|
+
* Works in conjunction with SkeletonLoader for complete loading/empty state handling.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic usage
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <NoData id="my-widget">
|
|
16
|
+
* <WidgetContent id="my-widget" />
|
|
17
|
+
* </NoData>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example With SkeletonLoader
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <SkeletonLoader id="my-widget" Skeleton={MySkeleton}>
|
|
23
|
+
* <NoData id="my-widget">
|
|
24
|
+
* <WidgetContent id="my-widget" />
|
|
25
|
+
* </NoData>
|
|
26
|
+
* </SkeletonLoader>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example With custom messages
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <NoData
|
|
32
|
+
* id="my-widget"
|
|
33
|
+
* title="No results found"
|
|
34
|
+
* description="Try adjusting your filters"
|
|
35
|
+
* >
|
|
36
|
+
* <WidgetContent id="my-widget" />
|
|
37
|
+
* </NoData>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function WidgetNoData({
|
|
41
|
+
id,
|
|
42
|
+
children,
|
|
43
|
+
title = 'No data available',
|
|
44
|
+
description = 'There are no results for the combination of filters applied to your data. Try tweaking your filters, or zoom and pan the map to adjust filters',
|
|
45
|
+
isEmpty = defaultIsEmpty,
|
|
46
|
+
}: WidgetNoDataProps) {
|
|
47
|
+
// Subscribe to widget store with selective subscription for optimal performance
|
|
48
|
+
const widget = useWidgetStore(
|
|
49
|
+
useShallow((state) => {
|
|
50
|
+
const w = state.widgets[id]
|
|
51
|
+
return {
|
|
52
|
+
isLoading: w?.isLoading,
|
|
53
|
+
isFetching: w?.isFetching,
|
|
54
|
+
data: w?.data,
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
// If loading or fetching, show children
|
|
60
|
+
// SkeletonLoader handles loading state, this allows proper composition
|
|
61
|
+
if (widget?.isLoading || widget?.isFetching) {
|
|
62
|
+
return children
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check if data is empty
|
|
66
|
+
if (isEmpty(widget?.data)) {
|
|
67
|
+
return (
|
|
68
|
+
<Box sx={styles.root}>
|
|
69
|
+
<Typography variant='body2' color='text.primary'>
|
|
70
|
+
{title}
|
|
71
|
+
</Typography>
|
|
72
|
+
<Typography variant='caption' color='text.secondary'>
|
|
73
|
+
{description}
|
|
74
|
+
</Typography>
|
|
75
|
+
</Box>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Data exists, render children
|
|
80
|
+
return children
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Default function to determine if data is empty
|
|
85
|
+
* Handles various data structures commonly used in widgets
|
|
86
|
+
*/
|
|
87
|
+
function defaultIsEmpty(data: unknown): boolean {
|
|
88
|
+
// Null or undefined
|
|
89
|
+
if (data == null) {
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Arrays (most common case)
|
|
94
|
+
if (Array.isArray(data)) {
|
|
95
|
+
// Empty array
|
|
96
|
+
if (data.length === 0) {
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Array of arrays (CategoryWidget pattern: [[],[]])
|
|
101
|
+
// Check if all inner arrays are empty
|
|
102
|
+
if (data.every((item) => Array.isArray(item) && item.length === 0)) {
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Objects
|
|
110
|
+
if (typeof data === 'object') {
|
|
111
|
+
return Object.keys(data).length === 0
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Primitives (numbers, strings, booleans) are considered valid data
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Styles for NoData component matching Figma design specifications
|
|
5
|
+
* Design reference: node 5781-11028
|
|
6
|
+
*/
|
|
7
|
+
export const styles: Record<string, SxProps<Theme>> = {
|
|
8
|
+
root: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
flexDirection: 'column',
|
|
11
|
+
gap: 1, // 8px
|
|
12
|
+
paddingTop: 1, // 8px
|
|
13
|
+
paddingBottom: 2, // 16px
|
|
14
|
+
paddingX: 2, // 16px
|
|
15
|
+
width: '100%',
|
|
16
|
+
minHeight: '100%',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import type { WidgetState } from '../stores/types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props for the WidgetNoData wrapper component
|
|
6
|
+
*
|
|
7
|
+
* @example Basic usage
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <WidgetNoData id="my-widget">
|
|
10
|
+
* <WidgetContent id="my-widget" />
|
|
11
|
+
* </WidgetNoData>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @example With custom messages
|
|
15
|
+
* ```tsx
|
|
16
|
+
* <WidgetNoData
|
|
17
|
+
* id="my-widget"
|
|
18
|
+
* title="No results found"
|
|
19
|
+
* description="Try adjusting your filters"
|
|
20
|
+
* >
|
|
21
|
+
* <WidgetContent id="my-widget" />
|
|
22
|
+
* </WidgetNoData>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example With custom isEmpty logic
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <WidgetNoData
|
|
28
|
+
* id="my-widget"
|
|
29
|
+
* isEmpty={(data) => {
|
|
30
|
+
* const d = data as CustomData
|
|
31
|
+
* return !d?.items?.length
|
|
32
|
+
* }}
|
|
33
|
+
* >
|
|
34
|
+
* <WidgetContent id="my-widget" />
|
|
35
|
+
* </WidgetNoData>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface WidgetNoDataProps {
|
|
39
|
+
/**
|
|
40
|
+
* Widget ID to fetch state from store
|
|
41
|
+
*/
|
|
42
|
+
id: WidgetState['id']
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Children to render when data is available
|
|
46
|
+
*/
|
|
47
|
+
children: ReactNode
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional custom title for empty state
|
|
51
|
+
* @default "No data available"
|
|
52
|
+
*/
|
|
53
|
+
title?: string
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Optional custom description for empty state
|
|
57
|
+
* @default "There are no results for the combination of filters applied to your data. Try tweaking your filters, or zoom and pan the map to adjust filters"
|
|
58
|
+
*/
|
|
59
|
+
description?: string
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Optional custom function to determine if data is empty
|
|
63
|
+
* If not provided, uses default isEmpty logic that handles:
|
|
64
|
+
* - null/undefined → empty
|
|
65
|
+
* - [] (empty array) → empty
|
|
66
|
+
* - [[]] (nested empty arrays) → empty
|
|
67
|
+
* - {} (empty object) → empty
|
|
68
|
+
* - Primitives → not empty
|
|
69
|
+
* - Arrays/objects with content → not empty
|
|
70
|
+
*/
|
|
71
|
+
isEmpty?: (data: unknown) => boolean
|
|
72
|
+
}
|