@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,71 @@
|
|
|
1
|
+
import { downloadToCSV, downloadToPNG } from '../actions'
|
|
2
|
+
import type { ConfigProps } from '../loader/types'
|
|
3
|
+
import type {
|
|
4
|
+
TableDownloadConfig,
|
|
5
|
+
TableWidgetConfig,
|
|
6
|
+
TableColumn,
|
|
7
|
+
Mode,
|
|
8
|
+
SortDirection,
|
|
9
|
+
} from './types'
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_MODE: Mode = 'local'
|
|
12
|
+
export const DEFAULT_PAGE = 0
|
|
13
|
+
export const DEFAULT_ROWS_PER_PAGE = 10
|
|
14
|
+
export const DEFAULT_ROWS_PER_PAGE_OPTIONS = [5, 10, 25, 50]
|
|
15
|
+
|
|
16
|
+
export const DEFAULT_COLUMN_ID = null
|
|
17
|
+
export const DEFAULT_DIRECTION: SortDirection = 'asc'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Default table download configuration
|
|
21
|
+
* Supports PNG export (screenshot) and CSV export (data)
|
|
22
|
+
*/
|
|
23
|
+
export function tableDownloadConfig({
|
|
24
|
+
refUI,
|
|
25
|
+
}: ConfigProps): TableDownloadConfig {
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
...downloadToPNG,
|
|
29
|
+
modifier: () => downloadToPNG.modifier(refUI),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
...downloadToCSV,
|
|
33
|
+
modifier: async (data) => {
|
|
34
|
+
if (!data?.length) {
|
|
35
|
+
return downloadToCSV.modifier([])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Convert table data to CSV format
|
|
39
|
+
// First row contains headers (column keys)
|
|
40
|
+
const firstRow = data[0]
|
|
41
|
+
if (!firstRow) {
|
|
42
|
+
return downloadToCSV.modifier([])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const headers = Object.keys(firstRow).filter((key) => key !== 'id')
|
|
46
|
+
const rows = data.map((row) =>
|
|
47
|
+
headers.map((header) => {
|
|
48
|
+
const value = row[header]
|
|
49
|
+
if (value === null || value === undefined) return ''
|
|
50
|
+
if (typeof value === 'object') return JSON.stringify(value)
|
|
51
|
+
return value
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return downloadToCSV.modifier([headers, ...rows])
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Default table widget configuration
|
|
63
|
+
*/
|
|
64
|
+
export function tableConfig(columns: TableColumn[] = []): TableWidgetConfig {
|
|
65
|
+
return {
|
|
66
|
+
columns,
|
|
67
|
+
selectable: false,
|
|
68
|
+
selected: [],
|
|
69
|
+
mode: DEFAULT_MODE,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getCellValue, compareValues, sortData, paginateData } from './helpers'
|
|
3
|
+
|
|
4
|
+
describe('helpers', () => {
|
|
5
|
+
describe('getCellValue', () => {
|
|
6
|
+
it('should return empty string for null', () => {
|
|
7
|
+
expect(getCellValue(null)).toBe('')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('should return empty string for undefined', () => {
|
|
11
|
+
expect(getCellValue(undefined)).toBe('')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('should return string as-is', () => {
|
|
15
|
+
expect(getCellValue('hello')).toBe('hello')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should return number as-is', () => {
|
|
19
|
+
expect(getCellValue(42)).toBe(42)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should return boolean as-is', () => {
|
|
23
|
+
expect(getCellValue(true)).toBe(true)
|
|
24
|
+
expect(getCellValue(false)).toBe(false)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should format array of strings with quotes', () => {
|
|
28
|
+
expect(getCellValue(['a', 'b', 'c'])).toBe('["a", "b", "c"]')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should format array of numbers without quotes', () => {
|
|
32
|
+
expect(getCellValue([1, 2, 3])).toBe('[1, 2, 3]')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should format mixed array', () => {
|
|
36
|
+
expect(getCellValue(['hello', 42, true])).toBe('["hello", 42, true]')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should format empty array', () => {
|
|
40
|
+
expect(getCellValue([])).toBe('[]')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should stringify object', () => {
|
|
44
|
+
expect(getCellValue({ key: 'value' })).toBe('{"key":"value"}')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should stringify nested object', () => {
|
|
48
|
+
expect(getCellValue({ a: { b: 'c' } })).toBe('{"a":{"b":"c"}}')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('compareValues', () => {
|
|
53
|
+
describe('null/undefined handling', () => {
|
|
54
|
+
it('should sort null to end (return 1)', () => {
|
|
55
|
+
expect(compareValues(null, 'a', 'asc')).toBe(1)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should sort undefined to end (return 1)', () => {
|
|
59
|
+
expect(compareValues(undefined, 'a', 'asc')).toBe(1)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should sort non-null before null (return -1)', () => {
|
|
63
|
+
expect(compareValues('a', null, 'asc')).toBe(-1)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should sort non-undefined before undefined (return -1)', () => {
|
|
67
|
+
expect(compareValues('a', undefined, 'asc')).toBe(-1)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('string comparison', () => {
|
|
72
|
+
it('should compare strings ascending', () => {
|
|
73
|
+
expect(compareValues('apple', 'banana', 'asc')).toBeLessThan(0)
|
|
74
|
+
expect(compareValues('banana', 'apple', 'asc')).toBeGreaterThan(0)
|
|
75
|
+
expect(compareValues('apple', 'apple', 'asc')).toBe(0)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should compare strings descending', () => {
|
|
79
|
+
expect(compareValues('apple', 'banana', 'desc')).toBeGreaterThan(0)
|
|
80
|
+
expect(compareValues('banana', 'apple', 'desc')).toBeLessThan(0)
|
|
81
|
+
// When values are equal, result is 0 (could be -0 or +0, both equal 0)
|
|
82
|
+
expect(compareValues('apple', 'apple', 'desc') === 0).toBe(true)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe('number comparison', () => {
|
|
87
|
+
it('should compare numbers ascending', () => {
|
|
88
|
+
expect(compareValues(1, 2, 'asc')).toBeLessThan(0)
|
|
89
|
+
expect(compareValues(2, 1, 'asc')).toBeGreaterThan(0)
|
|
90
|
+
expect(compareValues(1, 1, 'asc')).toBe(0)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should compare numbers descending', () => {
|
|
94
|
+
expect(compareValues(1, 2, 'desc')).toBeGreaterThan(0)
|
|
95
|
+
expect(compareValues(2, 1, 'desc')).toBeLessThan(0)
|
|
96
|
+
// When values are equal, result is 0 (could be -0 or +0, both equal 0)
|
|
97
|
+
expect(compareValues(1, 1, 'desc') === 0).toBe(true)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should handle negative numbers', () => {
|
|
101
|
+
expect(compareValues(-5, 5, 'asc')).toBeLessThan(0)
|
|
102
|
+
expect(compareValues(5, -5, 'asc')).toBeGreaterThan(0)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should handle decimal numbers', () => {
|
|
106
|
+
expect(compareValues(1.5, 2.5, 'asc')).toBeLessThan(0)
|
|
107
|
+
expect(compareValues(2.5, 1.5, 'asc')).toBeGreaterThan(0)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('boolean comparison', () => {
|
|
112
|
+
it('should compare booleans ascending (false before true)', () => {
|
|
113
|
+
expect(compareValues(false, true, 'asc')).toBeLessThan(0)
|
|
114
|
+
expect(compareValues(true, false, 'asc')).toBeGreaterThan(0)
|
|
115
|
+
expect(compareValues(true, true, 'asc')).toBe(0)
|
|
116
|
+
expect(compareValues(false, false, 'asc')).toBe(0)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should compare booleans descending (true before false)', () => {
|
|
120
|
+
expect(compareValues(false, true, 'desc')).toBeGreaterThan(0)
|
|
121
|
+
expect(compareValues(true, false, 'desc')).toBeLessThan(0)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe('object comparison', () => {
|
|
126
|
+
it('should compare objects as JSON strings', () => {
|
|
127
|
+
const objA = { a: 1 }
|
|
128
|
+
const objB = { b: 2 }
|
|
129
|
+
// Compares '{"a":1}' vs '{"b":2}'
|
|
130
|
+
expect(compareValues(objA, objB, 'asc')).toBeLessThan(0)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should handle array comparison', () => {
|
|
134
|
+
const arrA = [1, 2]
|
|
135
|
+
const arrB = [3, 4]
|
|
136
|
+
// Compares '[1,2]' vs '[3,4]'
|
|
137
|
+
expect(compareValues(arrA, arrB, 'asc')).toBeLessThan(0)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('sortData', () => {
|
|
143
|
+
const testData = [
|
|
144
|
+
{ id: 1, name: 'Charlie', age: 30 },
|
|
145
|
+
{ id: 2, name: 'Alice', age: 25 },
|
|
146
|
+
{ id: 3, name: 'Bob', age: 35 },
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
it('should sort by string column ascending', () => {
|
|
150
|
+
const sorted = sortData(testData, 'name', 'asc')
|
|
151
|
+
expect(sorted.map((r) => r.name)).toEqual(['Alice', 'Bob', 'Charlie'])
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('should sort by string column descending', () => {
|
|
155
|
+
const sorted = sortData(testData, 'name', 'desc')
|
|
156
|
+
expect(sorted.map((r) => r.name)).toEqual(['Charlie', 'Bob', 'Alice'])
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should sort by number column ascending', () => {
|
|
160
|
+
const sorted = sortData(testData, 'age', 'asc')
|
|
161
|
+
expect(sorted.map((r) => r.age)).toEqual([25, 30, 35])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should sort by number column descending', () => {
|
|
165
|
+
const sorted = sortData(testData, 'age', 'desc')
|
|
166
|
+
expect(sorted.map((r) => r.age)).toEqual([35, 30, 25])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should not mutate original array', () => {
|
|
170
|
+
const original = [...testData]
|
|
171
|
+
sortData(testData, 'name', 'asc')
|
|
172
|
+
expect(testData).toEqual(original)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('should handle empty array', () => {
|
|
176
|
+
expect(sortData([], 'name', 'asc')).toEqual([])
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should handle single item array', () => {
|
|
180
|
+
const single = [{ id: 1, name: 'Alice' }]
|
|
181
|
+
expect(sortData(single, 'name', 'asc')).toEqual(single)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should handle null values in data', () => {
|
|
185
|
+
const dataWithNull = [
|
|
186
|
+
{ id: 1, name: 'Bob' },
|
|
187
|
+
{ id: 2, name: null },
|
|
188
|
+
{ id: 3, name: 'Alice' },
|
|
189
|
+
]
|
|
190
|
+
const sorted = sortData(dataWithNull, 'name', 'asc')
|
|
191
|
+
// Null should be sorted to end
|
|
192
|
+
expect(sorted.map((r) => r.name)).toEqual(['Alice', 'Bob', null])
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('paginateData', () => {
|
|
197
|
+
const testData = [
|
|
198
|
+
{ id: 1 },
|
|
199
|
+
{ id: 2 },
|
|
200
|
+
{ id: 3 },
|
|
201
|
+
{ id: 4 },
|
|
202
|
+
{ id: 5 },
|
|
203
|
+
{ id: 6 },
|
|
204
|
+
{ id: 7 },
|
|
205
|
+
{ id: 8 },
|
|
206
|
+
{ id: 9 },
|
|
207
|
+
{ id: 10 },
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
it('should return first page', () => {
|
|
211
|
+
const result = paginateData(testData, 0, 3)
|
|
212
|
+
expect(result.map((r) => r.id)).toEqual([1, 2, 3])
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should return second page', () => {
|
|
216
|
+
const result = paginateData(testData, 1, 3)
|
|
217
|
+
expect(result.map((r) => r.id)).toEqual([4, 5, 6])
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('should return partial last page', () => {
|
|
221
|
+
const result = paginateData(testData, 3, 3)
|
|
222
|
+
expect(result.map((r) => r.id)).toEqual([10])
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should return empty array for page beyond data', () => {
|
|
226
|
+
const result = paginateData(testData, 10, 3)
|
|
227
|
+
expect(result).toEqual([])
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('should handle page size larger than data', () => {
|
|
231
|
+
const result = paginateData(testData, 0, 100)
|
|
232
|
+
expect(result.map((r) => r.id)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('should handle empty array', () => {
|
|
236
|
+
expect(paginateData([], 0, 10)).toEqual([])
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('should handle single item', () => {
|
|
240
|
+
const result = paginateData([{ id: 1 }], 0, 10)
|
|
241
|
+
expect(result.map((r) => r.id)).toEqual([1])
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get formatted cell value for display
|
|
5
|
+
* Handles primitives, arrays, objects, and custom formatters
|
|
6
|
+
*
|
|
7
|
+
* @param cellValue - Raw cell value
|
|
8
|
+
* @param formatter - Optional formatter function
|
|
9
|
+
* @returns Formatted value for display
|
|
10
|
+
*/
|
|
11
|
+
export function getCellValue<T>(cellValue: T): ReactNode {
|
|
12
|
+
// Handle null/undefined
|
|
13
|
+
if (cellValue === null || cellValue === undefined) {
|
|
14
|
+
return ''
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Handle arrays - format as [item1, item2, ...]
|
|
18
|
+
if (Array.isArray(cellValue)) {
|
|
19
|
+
const formatted = cellValue
|
|
20
|
+
.map((item) => (typeof item === 'string' ? `"${item}"` : String(item)))
|
|
21
|
+
.join(', ')
|
|
22
|
+
return `[${formatted}]`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Handle objects - stringify to JSON
|
|
26
|
+
if (typeof cellValue === 'object') {
|
|
27
|
+
return JSON.stringify(cellValue)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Return primitives as-is (string, number, boolean)
|
|
31
|
+
return cellValue as ReactNode
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compare two values for sorting
|
|
36
|
+
* Handles strings, numbers, and other types
|
|
37
|
+
*
|
|
38
|
+
* @param a - First value
|
|
39
|
+
* @param b - Second value
|
|
40
|
+
* @param direction - Sort direction
|
|
41
|
+
* @returns Comparison result (-1, 0, 1)
|
|
42
|
+
*/
|
|
43
|
+
export function compareValues(
|
|
44
|
+
a: unknown,
|
|
45
|
+
b: unknown,
|
|
46
|
+
direction: 'asc' | 'desc',
|
|
47
|
+
): number {
|
|
48
|
+
// Handle null/undefined - always sort to end
|
|
49
|
+
if (a === null || a === undefined) return 1
|
|
50
|
+
if (b === null || b === undefined) return -1
|
|
51
|
+
|
|
52
|
+
let comparison = 0
|
|
53
|
+
|
|
54
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
55
|
+
comparison = a.localeCompare(b)
|
|
56
|
+
} else if (typeof a === 'number' && typeof b === 'number') {
|
|
57
|
+
comparison = a - b
|
|
58
|
+
} else if (typeof a === 'boolean' && typeof b === 'boolean') {
|
|
59
|
+
comparison = a === b ? 0 : a ? 1 : -1
|
|
60
|
+
} else if (typeof a === 'object' || typeof b === 'object') {
|
|
61
|
+
// Compare objects/arrays as JSON strings
|
|
62
|
+
const strA = JSON.stringify(a)
|
|
63
|
+
const strB = JSON.stringify(b)
|
|
64
|
+
comparison = strA.localeCompare(strB)
|
|
65
|
+
} else {
|
|
66
|
+
// Fallback for other primitives (bigint, symbol, etc.)
|
|
67
|
+
comparison = 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return direction === 'asc' ? comparison : -comparison
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Sort data array by column
|
|
75
|
+
*
|
|
76
|
+
* @param data - Data array to sort
|
|
77
|
+
* @param columnId - Column ID to sort by
|
|
78
|
+
* @param direction - Sort direction
|
|
79
|
+
* @returns Sorted data array (new array, does not mutate original)
|
|
80
|
+
*/
|
|
81
|
+
export function sortData<T extends Record<string, unknown>>(
|
|
82
|
+
data: T[],
|
|
83
|
+
columnId: string,
|
|
84
|
+
direction: 'asc' | 'desc',
|
|
85
|
+
): T[] {
|
|
86
|
+
return [...data].sort((a, b) =>
|
|
87
|
+
compareValues(a[columnId], b[columnId], direction),
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Paginate data array
|
|
93
|
+
*
|
|
94
|
+
* @param data - Data array to paginate
|
|
95
|
+
* @param page - Current page (0-indexed)
|
|
96
|
+
* @param rowsPerPage - Number of rows per page
|
|
97
|
+
* @returns Paginated data slice
|
|
98
|
+
*/
|
|
99
|
+
export function paginateData<T>(
|
|
100
|
+
data: T[],
|
|
101
|
+
page: number,
|
|
102
|
+
rowsPerPage: number,
|
|
103
|
+
): T[] {
|
|
104
|
+
const start = page * rowsPerPage
|
|
105
|
+
const end = start + rowsPerPage
|
|
106
|
+
return data.slice(start, end)
|
|
107
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { usePagination } from './use-pagination'
|
|
2
|
+
export { useSort } from './use-sort'
|
|
3
|
+
export { useSelection } from './use-selection'
|
|
4
|
+
|
|
5
|
+
export type { UsePaginationResult } from './use-pagination'
|
|
6
|
+
export type { UseSortResult } from './use-sort'
|
|
7
|
+
export type { UseSelectionOptions, UseSelectionResult } from './use-selection'
|