@sio-group/ui-datatable 0.1.0 → 0.1.2
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/CHANGELOG.md +14 -0
- package/dist/index.cjs +2 -3
- package/dist/index.js +2 -3
- package/package.json +2 -2
- package/src/components/DataTableControls.tsx +1 -7
- package/src/components/cell-types/BooleanCell.tsx +2 -8
- package/src/components/cell-types/InlineInputCell.tsx +2 -11
- package/src/tests/renderValue.test.tsx +304 -0
- package/src/tests/setup.ts +1 -0
- package/src/tests/useDataTable.test.ts +464 -0
- package/src/types/data-table-props.d.ts +20 -0
- package/src/types/render-value-props.d.ts +7 -0
- package/src/utils/render-value.tsx +3 -11
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @sio-group/ui-datatable
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @sio-group/ui-modal@0.4.2
|
|
9
|
+
|
|
10
|
+
## 0.1.1
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @sio-group/ui-modal@0.4.1
|
|
16
|
+
|
|
3
17
|
## 0.1.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -284,6 +284,7 @@ var BooleanCell = ({
|
|
|
284
284
|
}) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
285
285
|
import_ui_core2.Button,
|
|
286
286
|
{
|
|
287
|
+
className: "boolean",
|
|
287
288
|
color: value ? "success" : "error",
|
|
288
289
|
variant: column.format === "button" ? "primary" : "link",
|
|
289
290
|
onClick: () => updateData?.(item.id, {
|
|
@@ -336,7 +337,6 @@ var import_react3 = require("react");
|
|
|
336
337
|
var import_ui_core3 = require("@sio-group/ui-core");
|
|
337
338
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
338
339
|
var InlineInputCell = ({
|
|
339
|
-
column,
|
|
340
340
|
formField,
|
|
341
341
|
item,
|
|
342
342
|
value,
|
|
@@ -459,7 +459,6 @@ var renderValue = ({ value, column, item, formFields, updateData }) => {
|
|
|
459
459
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
460
460
|
InlineInputCell,
|
|
461
461
|
{
|
|
462
|
-
column,
|
|
463
462
|
value,
|
|
464
463
|
item,
|
|
465
464
|
formField,
|
|
@@ -485,7 +484,7 @@ var renderValue = ({ value, column, item, formFields, updateData }) => {
|
|
|
485
484
|
DateCell,
|
|
486
485
|
{
|
|
487
486
|
column,
|
|
488
|
-
value
|
|
487
|
+
value: String(value)
|
|
489
488
|
}
|
|
490
489
|
);
|
|
491
490
|
}
|
package/dist/index.js
CHANGED
|
@@ -258,6 +258,7 @@ var BooleanCell = ({
|
|
|
258
258
|
}) => /* @__PURE__ */ jsx5(
|
|
259
259
|
Button2,
|
|
260
260
|
{
|
|
261
|
+
className: "boolean",
|
|
261
262
|
color: value ? "success" : "error",
|
|
262
263
|
variant: column.format === "button" ? "primary" : "link",
|
|
263
264
|
onClick: () => updateData?.(item.id, {
|
|
@@ -310,7 +311,6 @@ import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
|
310
311
|
import { Button as Button3 } from "@sio-group/ui-core";
|
|
311
312
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
312
313
|
var InlineInputCell = ({
|
|
313
|
-
column,
|
|
314
314
|
formField,
|
|
315
315
|
item,
|
|
316
316
|
value,
|
|
@@ -433,7 +433,6 @@ var renderValue = ({ value, column, item, formFields, updateData }) => {
|
|
|
433
433
|
return /* @__PURE__ */ jsx8(
|
|
434
434
|
InlineInputCell,
|
|
435
435
|
{
|
|
436
|
-
column,
|
|
437
436
|
value,
|
|
438
437
|
item,
|
|
439
438
|
formField,
|
|
@@ -459,7 +458,7 @@ var renderValue = ({ value, column, item, formFields, updateData }) => {
|
|
|
459
458
|
DateCell,
|
|
460
459
|
{
|
|
461
460
|
column,
|
|
462
|
-
value
|
|
461
|
+
value: String(value)
|
|
463
462
|
}
|
|
464
463
|
);
|
|
465
464
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sio-group/ui-datatable",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@sio-group/ui-core": "0.4.0",
|
|
35
35
|
"@sio-group/ui-pagination": "0.1.1",
|
|
36
|
-
"@sio-group/ui-modal": "0.4.
|
|
36
|
+
"@sio-group/ui-modal": "0.4.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@vitejs/plugin-react": "^4",
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import {Entity} from "../types";
|
|
2
1
|
import {useState} from "react";
|
|
3
|
-
|
|
4
|
-
interface DataTableControlsProps {
|
|
5
|
-
currentSearch?: string | null;
|
|
6
|
-
handleSearch: (query: string) => void;
|
|
7
|
-
entity?: Entity
|
|
8
|
-
}
|
|
2
|
+
import {DataTableControlsProps} from "../types/data-table-props";
|
|
9
3
|
|
|
10
4
|
export const DataTableControls = ({
|
|
11
5
|
currentSearch,
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {Column} from "../../types";
|
|
2
1
|
import {Button} from "@sio-group/ui-core";
|
|
3
|
-
|
|
4
|
-
interface BooleanCellProps<T extends { id: number | string }> {
|
|
5
|
-
item: T;
|
|
6
|
-
column: Column<T>;
|
|
7
|
-
value: T[keyof T];
|
|
8
|
-
updateData?: (id: string | number, values: Partial<T>) => void;
|
|
9
|
-
}
|
|
2
|
+
import {BooleanCellProps} from "../../types/data-table-props";
|
|
10
3
|
|
|
11
4
|
export const BooleanCell = <T extends { id: string | number }> ({
|
|
12
5
|
column,
|
|
@@ -15,6 +8,7 @@ export const BooleanCell = <T extends { id: string | number }> ({
|
|
|
15
8
|
updateData,
|
|
16
9
|
}: BooleanCellProps<T>) => (
|
|
17
10
|
<Button
|
|
11
|
+
className="boolean"
|
|
18
12
|
color={value ? "success" : "error"}
|
|
19
13
|
variant={column.format === "button" ? "primary" : "link"}
|
|
20
14
|
onClick={() =>
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {Column, FormField} from "../../types";
|
|
1
|
+
import {useEffect, useState} from "react";
|
|
3
2
|
import {Button} from "@sio-group/ui-core";
|
|
4
|
-
|
|
5
|
-
interface InlineInputCellProps<T extends { id: string | number }> {
|
|
6
|
-
column: Column<T>;
|
|
7
|
-
formField: FormField;
|
|
8
|
-
item: T;
|
|
9
|
-
value: T[keyof T];
|
|
10
|
-
updateData?: (id: string | number, values: Partial<T>) => void;
|
|
11
|
-
}
|
|
3
|
+
import {InlineInputCellProps} from "../../types/data-table-props";
|
|
12
4
|
|
|
13
5
|
export const InlineInputCell = <T extends { id: string | number }>({
|
|
14
|
-
column,
|
|
15
6
|
formField,
|
|
16
7
|
item,
|
|
17
8
|
value,
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import {renderValue} from "../utils/render-value";
|
|
5
|
+
|
|
6
|
+
interface TestItem {
|
|
7
|
+
id: number;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
status: string;
|
|
11
|
+
active: boolean;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
tags: string[];
|
|
14
|
+
role: { name: string };
|
|
15
|
+
meta: { key: string; value: string };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const baseColumn = { name: 'name' as keyof TestItem, label: 'Naam' };
|
|
19
|
+
const baseItem: TestItem = {
|
|
20
|
+
id: 1,
|
|
21
|
+
name: 'Alice',
|
|
22
|
+
email: 'alice@example.com',
|
|
23
|
+
status: 'active',
|
|
24
|
+
active: true,
|
|
25
|
+
createdAt: '2024-01-15T10:00:00.000Z',
|
|
26
|
+
tags: ['admin', 'user'],
|
|
27
|
+
role: { name: 'Administrator' },
|
|
28
|
+
meta: { key: 'foo', value: 'bar' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const renderCell = (props: Parameters<typeof renderValue>[0]) => {
|
|
32
|
+
const result = renderValue(props);
|
|
33
|
+
const { container } = render(<>{result}</>);
|
|
34
|
+
return container;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ─────────────────────────────────────────────
|
|
38
|
+
// Null / undefined / empty
|
|
39
|
+
// ─────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
describe('empty values', () => {
|
|
42
|
+
it('renders EmptyCell for null', () => {
|
|
43
|
+
const container = renderCell({ value: null, column: baseColumn, item: baseItem });
|
|
44
|
+
expect(container.querySelector('.empty')).toBeTruthy();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('renders EmptyCell for undefined', () => {
|
|
48
|
+
const container = renderCell({ value: undefined, column: baseColumn, item: baseItem });
|
|
49
|
+
expect(container.querySelector('.empty')).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renders EmptyCell for empty array', () => {
|
|
53
|
+
const container = renderCell({ value: [], column: baseColumn, item: baseItem });
|
|
54
|
+
expect(container.querySelector('.empty')).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// ─────────────────────────────────────────────
|
|
59
|
+
// String / number fallback
|
|
60
|
+
// ─────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
describe('string and number values', () => {
|
|
63
|
+
it('renders a string value', () => {
|
|
64
|
+
const container = renderCell({ value: 'Alice', column: baseColumn, item: baseItem });
|
|
65
|
+
expect(container.textContent).toBe('Alice');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('renders a number value as string', () => {
|
|
69
|
+
const container = renderCell({ value: 42, column: baseColumn, item: baseItem });
|
|
70
|
+
expect(container.textContent).toBe('42');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────
|
|
75
|
+
// Format: email
|
|
76
|
+
// ─────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
describe('format: email', () => {
|
|
79
|
+
it('renders a mailto link', () => {
|
|
80
|
+
const container = renderCell({
|
|
81
|
+
value: 'alice@example.com',
|
|
82
|
+
column: { ...baseColumn, format: 'email' },
|
|
83
|
+
item: baseItem,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const link = container.querySelector('a');
|
|
87
|
+
expect(link).toBeTruthy();
|
|
88
|
+
expect(link?.getAttribute('href')).toBe('mailto:alice@example.com');
|
|
89
|
+
expect(link?.textContent).toBe('alice@example.com');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ─────────────────────────────────────────────
|
|
94
|
+
// Format: date / datetime
|
|
95
|
+
// ─────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
describe('format: date and datetime', () => {
|
|
98
|
+
it('renders a localized date string for format: date', () => {
|
|
99
|
+
const container = renderCell({
|
|
100
|
+
value: '2024-01-15T10:00:00.000Z',
|
|
101
|
+
column: { ...baseColumn, format: 'date' },
|
|
102
|
+
item: baseItem,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Should contain year and month — exact format depends on locale
|
|
106
|
+
expect(container.textContent).toMatch(/2024/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('renders a localized datetime string for format: datetime', () => {
|
|
110
|
+
const container = renderCell({
|
|
111
|
+
value: '2024-01-15T10:00:00.000Z',
|
|
112
|
+
column: { ...baseColumn, format: 'datetime' },
|
|
113
|
+
item: baseItem,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(container.textContent).toMatch(/2024/);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ─────────────────────────────────────────────
|
|
121
|
+
// Format: boolean / button
|
|
122
|
+
// ─────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
describe('format: boolean and button', () => {
|
|
125
|
+
it('renders a BooleanCell for format: boolean', () => {
|
|
126
|
+
const container = renderCell({
|
|
127
|
+
value: true,
|
|
128
|
+
column: { ...baseColumn, format: 'boolean' },
|
|
129
|
+
item: baseItem,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(container.querySelector('.boolean')).toBeTruthy();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('renders a BooleanCell for format: button', () => {
|
|
136
|
+
const container = renderCell({
|
|
137
|
+
value: false,
|
|
138
|
+
column: { ...baseColumn, format: 'button' },
|
|
139
|
+
item: baseItem,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(container.querySelector('.boolean')).toBeTruthy();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('calls updateData when BooleanCell is clicked', async () => {
|
|
146
|
+
const updateData = vi.fn();
|
|
147
|
+
const container = renderCell({
|
|
148
|
+
value: true,
|
|
149
|
+
column: { ...baseColumn, format: 'boolean' },
|
|
150
|
+
item: baseItem,
|
|
151
|
+
updateData,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const button = container.querySelector('button');
|
|
155
|
+
button?.click();
|
|
156
|
+
|
|
157
|
+
expect(updateData).toHaveBeenCalledWith(baseItem.id, { name: !true });
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// ─────────────────────────────────────────────
|
|
162
|
+
// Format: pill
|
|
163
|
+
// ─────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
describe('format: pill', () => {
|
|
166
|
+
it('renders a Pill with status and label from value', () => {
|
|
167
|
+
const container = renderCell({
|
|
168
|
+
value: { status: 'success', label: 'Actief' },
|
|
169
|
+
column: { ...baseColumn, format: 'pill' },
|
|
170
|
+
item: baseItem,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const pill = container.querySelector('.pill');
|
|
174
|
+
expect(pill).toBeTruthy();
|
|
175
|
+
expect(pill?.textContent).toBe('Actief');
|
|
176
|
+
expect(pill?.className).toContain('pill--success');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ─────────────────────────────────────────────
|
|
181
|
+
// Arrays
|
|
182
|
+
// ─────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
describe('array values', () => {
|
|
185
|
+
it('renders each string item in its own div', () => {
|
|
186
|
+
const container = renderCell({
|
|
187
|
+
value: ['admin', 'user'],
|
|
188
|
+
column: baseColumn,
|
|
189
|
+
item: baseItem,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const divs = container.querySelectorAll('div');
|
|
193
|
+
expect(divs).toHaveLength(2);
|
|
194
|
+
expect(divs[0].textContent).toBe('admin');
|
|
195
|
+
expect(divs[1].textContent).toBe('user');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('renders each object item using key-value pairs', () => {
|
|
199
|
+
const container = renderCell({
|
|
200
|
+
value: [{ name: 'Admin' }, { name: 'User' }],
|
|
201
|
+
column: baseColumn,
|
|
202
|
+
item: baseItem,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(container.textContent).toContain('Admin');
|
|
206
|
+
expect(container.textContent).toContain('User');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('renders only the specified key when format.key is set', () => {
|
|
210
|
+
const container = renderCell({
|
|
211
|
+
value: [{ name: 'Admin', id: 1 }, { name: 'User', id: 2 }],
|
|
212
|
+
column: { ...baseColumn, format: { key: 'name' } },
|
|
213
|
+
item: baseItem,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(container.textContent).toContain('Admin');
|
|
217
|
+
expect(container.textContent).toContain('User');
|
|
218
|
+
expect(container.textContent).not.toContain('1');
|
|
219
|
+
expect(container.textContent).not.toContain('2');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// ─────────────────────────────────────────────
|
|
224
|
+
// Objects
|
|
225
|
+
// ─────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
describe('object values', () => {
|
|
228
|
+
it('renders all key-value pairs for a plain object', () => {
|
|
229
|
+
const container = renderCell({
|
|
230
|
+
value: { key: 'foo', value: 'bar' },
|
|
231
|
+
column: baseColumn,
|
|
232
|
+
item: baseItem,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(container.textContent).toContain('key');
|
|
236
|
+
expect(container.textContent).toContain('foo');
|
|
237
|
+
expect(container.textContent).toContain('value');
|
|
238
|
+
expect(container.textContent).toContain('bar');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('renders only the specified key when format.key is set', () => {
|
|
242
|
+
const container = renderCell({
|
|
243
|
+
value: { name: 'Administrator', id: 5 },
|
|
244
|
+
column: { ...baseColumn, format: { key: 'name' } },
|
|
245
|
+
item: baseItem,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(container.textContent).toBe('Administrator');
|
|
249
|
+
expect(container.textContent).not.toContain('5');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('renders EmptyCell for empty object', () => {
|
|
253
|
+
const container = renderCell({
|
|
254
|
+
value: {},
|
|
255
|
+
column: baseColumn,
|
|
256
|
+
item: baseItem,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
expect(container.querySelector('.empty')).toBeTruthy();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('falls through to String(value) when format.key does not exist on object', () => {
|
|
263
|
+
const container = renderCell({
|
|
264
|
+
value: { name: 'Alice' },
|
|
265
|
+
column: { ...baseColumn, format: { key: 'nonexistent' } },
|
|
266
|
+
item: baseItem,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Returns empty string for missing key
|
|
270
|
+
expect(container.textContent).toBe('');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// ─────────────────────────────────────────────
|
|
275
|
+
// Inline editing (formFields)
|
|
276
|
+
// ─────────────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
describe('inline editing', () => {
|
|
279
|
+
it('renders InlineInputCell when a matching formField is found', () => {
|
|
280
|
+
const formFields = [{ name: 'name', type: 'text' as const }];
|
|
281
|
+
const container = renderCell({
|
|
282
|
+
value: 'Alice',
|
|
283
|
+
column: baseColumn,
|
|
284
|
+
item: baseItem,
|
|
285
|
+
formFields,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// InlineInputCell should be rendered — check for edit button or input
|
|
289
|
+
expect(container.querySelector('button[aria-label="inline edit field"]')).toBeTruthy();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('does not render InlineInputCell when no matching formField', () => {
|
|
293
|
+
const formFields = [{ name: 'email', type: 'text' as const }];
|
|
294
|
+
const container = renderCell({
|
|
295
|
+
value: 'Alice',
|
|
296
|
+
column: baseColumn, // name: 'name', no matching formField for 'name'
|
|
297
|
+
item: baseItem,
|
|
298
|
+
formFields,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
expect(container.querySelector('button[aria-label="inline edit field"]')).toBeFalsy();
|
|
302
|
+
expect(container.textContent).toBe('Alice');
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { renderHook, act } from '@testing-library/react';
|
|
3
|
+
import {useDataTable} from "../hooks/useDataTable";
|
|
4
|
+
|
|
5
|
+
interface TestItem {
|
|
6
|
+
id: number;
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
status: string;
|
|
10
|
+
age: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const mockData: TestItem[] = [
|
|
14
|
+
{ id: 1, name: 'Alice', email: 'alice@example.com', status: 'active', age: 30 },
|
|
15
|
+
{ id: 2, name: 'Bob', email: 'bob@example.com', status: 'inactive', age: 25 },
|
|
16
|
+
{ id: 3, name: 'Charlie', email: 'charlie@example.com', status: 'active', age: 35 },
|
|
17
|
+
{ id: 4, name: 'Diana', email: 'diana@example.com', status: 'inactive', age: 28 },
|
|
18
|
+
{ id: 5, name: 'Eve', email: 'eve@example.com', status: 'active', age: 22 },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const mockPagination = {
|
|
22
|
+
currentPage: 1,
|
|
23
|
+
pageCount: 3,
|
|
24
|
+
total: 15,
|
|
25
|
+
from: 1,
|
|
26
|
+
to: 5,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ─────────────────────────────────────────────
|
|
30
|
+
// Mode detection
|
|
31
|
+
// ─────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
describe('mode detection', () => {
|
|
34
|
+
it('is client-side when no pagination is provided', () => {
|
|
35
|
+
const { result } = renderHook(() =>
|
|
36
|
+
useDataTable({ data: mockData })
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(result.current.pagedData).toEqual(mockData);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('is server-side when pagination object is provided', () => {
|
|
43
|
+
const { result } = renderHook(() =>
|
|
44
|
+
useDataTable({ data: mockData, pagination: mockPagination })
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Server-side returns data as-is
|
|
48
|
+
expect(result.current.pagedData).toEqual(mockData);
|
|
49
|
+
expect(result.current.paginationMeta).toEqual(mockPagination);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ─────────────────────────────────────────────
|
|
54
|
+
// showSearch
|
|
55
|
+
// ─────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
describe('showSearch', () => {
|
|
58
|
+
it('is false by default (client-side)', () => {
|
|
59
|
+
const { result } = renderHook(() =>
|
|
60
|
+
useDataTable({ data: mockData })
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(result.current.showSearch).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('is true when clientSearchKeys is provided (client-side)', () => {
|
|
67
|
+
const { result } = renderHook(() =>
|
|
68
|
+
useDataTable({ data: mockData, clientSearchKeys: ['name'] })
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(result.current.showSearch).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('is false when clientSearchKeys is empty array (client-side)', () => {
|
|
75
|
+
const { result } = renderHook(() =>
|
|
76
|
+
useDataTable({ data: mockData, clientSearchKeys: [] })
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(result.current.showSearch).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('is false by default (server-side)', () => {
|
|
83
|
+
const { result } = renderHook(() =>
|
|
84
|
+
useDataTable({ data: mockData, pagination: mockPagination })
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(result.current.showSearch).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('is true when onSearch is provided (server-side)', () => {
|
|
91
|
+
const { result } = renderHook(() =>
|
|
92
|
+
useDataTable({
|
|
93
|
+
data: mockData,
|
|
94
|
+
pagination: mockPagination,
|
|
95
|
+
onSearch: vi.fn(),
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(result.current.showSearch).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ─────────────────────────────────────────────
|
|
104
|
+
// showPagination
|
|
105
|
+
// ─────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
describe('showPagination', () => {
|
|
108
|
+
it('is false by default (client-side)', () => {
|
|
109
|
+
const { result } = renderHook(() =>
|
|
110
|
+
useDataTable({ data: mockData })
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(result.current.showPagination).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('is true when clientPageSize is provided (client-side)', () => {
|
|
117
|
+
const { result } = renderHook(() =>
|
|
118
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
expect(result.current.showPagination).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('is false by default (server-side)', () => {
|
|
125
|
+
const { result } = renderHook(() =>
|
|
126
|
+
useDataTable({ data: mockData, pagination: mockPagination })
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(result.current.showPagination).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('is true when onPaginate is provided (server-side)', () => {
|
|
133
|
+
const { result } = renderHook(() =>
|
|
134
|
+
useDataTable({
|
|
135
|
+
data: mockData,
|
|
136
|
+
pagination: mockPagination,
|
|
137
|
+
onPaginate: vi.fn(),
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(result.current.showPagination).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────────────────────────
|
|
146
|
+
// Client-side search
|
|
147
|
+
// ─────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe('client-side search', () => {
|
|
150
|
+
it('returns all data when search is empty', () => {
|
|
151
|
+
const { result } = renderHook(() =>
|
|
152
|
+
useDataTable({ data: mockData, clientSearchKeys: ['name'] })
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(result.current.pagedData).toHaveLength(5);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('filters data across multiple search keys', () => {
|
|
159
|
+
const { result } = renderHook(() =>
|
|
160
|
+
useDataTable({ data: mockData, clientSearchKeys: ['name', 'email'] })
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
act(() => result.current.handleSearch('example.com'));
|
|
164
|
+
|
|
165
|
+
expect(result.current.pagedData).toHaveLength(5);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('resets to page 1 when search changes', () => {
|
|
169
|
+
const { result } = renderHook(() =>
|
|
170
|
+
useDataTable({ data: mockData, clientSearchKeys: ['name'], clientPageSize: 2 })
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Go to page 2
|
|
174
|
+
act(() => result.current.handlePaginate(2));
|
|
175
|
+
expect(result.current.paginationMeta?.currentPage).toBe(2);
|
|
176
|
+
|
|
177
|
+
// Search — should reset to page 1
|
|
178
|
+
act(() => result.current.handleSearch('alice'));
|
|
179
|
+
expect(result.current.paginationMeta?.currentPage).toBe(1);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('updates currentSearch', () => {
|
|
183
|
+
const { result } = renderHook(() =>
|
|
184
|
+
useDataTable({ data: mockData, clientSearchKeys: ['name'] })
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
act(() => result.current.handleSearch('bob'));
|
|
188
|
+
|
|
189
|
+
expect(result.current.currentSearch).toBe('bob');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ─────────────────────────────────────────────
|
|
194
|
+
// Client-side sort
|
|
195
|
+
// ─────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
describe('client-side sort', () => {
|
|
198
|
+
it('sorts ascending by string field', () => {
|
|
199
|
+
const { result } = renderHook(() =>
|
|
200
|
+
useDataTable({ data: mockData })
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
act(() => result.current.handleSort({ name: 'name', direction: 'asc' }));
|
|
204
|
+
|
|
205
|
+
const names = result.current.pagedData.map((i) => i.name);
|
|
206
|
+
expect(names).toEqual([...names].sort());
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('resets sort when null is passed', () => {
|
|
210
|
+
const { result } = renderHook(() =>
|
|
211
|
+
useDataTable({ data: mockData })
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
act(() => result.current.handleSort({ name: 'name', direction: 'asc' }));
|
|
215
|
+
act(() => result.current.handleSort(null));
|
|
216
|
+
|
|
217
|
+
expect(result.current.currentSort).toBeNull();
|
|
218
|
+
expect(result.current.pagedData).toEqual(mockData);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('updates currentSort', () => {
|
|
222
|
+
const { result } = renderHook(() =>
|
|
223
|
+
useDataTable({ data: mockData })
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const sort = { name: 'name' as keyof TestItem, direction: 'asc' as const };
|
|
227
|
+
act(() => result.current.handleSort(sort));
|
|
228
|
+
|
|
229
|
+
expect(result.current.currentSort).toEqual(sort);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// ─────────────────────────────────────────────
|
|
234
|
+
// Client-side pagination
|
|
235
|
+
// ─────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
describe('client-side pagination', () => {
|
|
238
|
+
it('slices data for the first page', () => {
|
|
239
|
+
const { result } = renderHook(() =>
|
|
240
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(result.current.pagedData).toHaveLength(2);
|
|
244
|
+
expect(result.current.pagedData[0].id).toBe(1);
|
|
245
|
+
expect(result.current.pagedData[1].id).toBe(2);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('slices data for subsequent pages', () => {
|
|
249
|
+
const { result } = renderHook(() =>
|
|
250
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
act(() => result.current.handlePaginate(2));
|
|
254
|
+
|
|
255
|
+
expect(result.current.pagedData).toHaveLength(2);
|
|
256
|
+
expect(result.current.pagedData[0].id).toBe(3);
|
|
257
|
+
expect(result.current.pagedData[1].id).toBe(4);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('handles last page with fewer items', () => {
|
|
261
|
+
const { result } = renderHook(() =>
|
|
262
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
act(() => result.current.handlePaginate(3));
|
|
266
|
+
|
|
267
|
+
expect(result.current.pagedData).toHaveLength(1);
|
|
268
|
+
expect(result.current.pagedData[0].id).toBe(5);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('calculates paginationMeta correctly', () => {
|
|
272
|
+
const { result } = renderHook(() =>
|
|
273
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(result.current.paginationMeta).toEqual({
|
|
277
|
+
currentPage: 1,
|
|
278
|
+
pageCount: 3,
|
|
279
|
+
total: 5,
|
|
280
|
+
from: 1,
|
|
281
|
+
to: 2,
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('updates paginationMeta on page change', () => {
|
|
286
|
+
const { result } = renderHook(() =>
|
|
287
|
+
useDataTable({ data: mockData, clientPageSize: 2 })
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
act(() => result.current.handlePaginate(2));
|
|
291
|
+
|
|
292
|
+
expect(result.current.paginationMeta?.currentPage).toBe(2);
|
|
293
|
+
expect(result.current.paginationMeta?.from).toBe(3);
|
|
294
|
+
expect(result.current.paginationMeta?.to).toBe(4);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('returns all data when clientPageSize is not set', () => {
|
|
298
|
+
const { result } = renderHook(() =>
|
|
299
|
+
useDataTable({ data: mockData })
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
expect(result.current.pagedData).toHaveLength(5);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ─────────────────────────────────────────────
|
|
307
|
+
// Server-side delegation
|
|
308
|
+
// ─────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
describe('server-side delegation', () => {
|
|
311
|
+
it('delegates search to onSearch callback', () => {
|
|
312
|
+
const onSearch = vi.fn();
|
|
313
|
+
const { result } = renderHook(() =>
|
|
314
|
+
useDataTable({ data: mockData, pagination: mockPagination, onSearch })
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
act(() => result.current.handleSearch('alice'));
|
|
318
|
+
|
|
319
|
+
expect(onSearch).toHaveBeenCalledWith('alice');
|
|
320
|
+
expect(onSearch).toHaveBeenCalledTimes(1);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('delegates sort to onSort callback', () => {
|
|
324
|
+
const onSort = vi.fn();
|
|
325
|
+
const { result } = renderHook(() =>
|
|
326
|
+
useDataTable({ data: mockData, pagination: mockPagination, onSort })
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const sort = { name: 'name' as keyof TestItem, direction: 'asc' as const };
|
|
330
|
+
act(() => result.current.handleSort(sort));
|
|
331
|
+
|
|
332
|
+
expect(onSort).toHaveBeenCalledWith(sort);
|
|
333
|
+
expect(onSort).toHaveBeenCalledTimes(1);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('delegates null sort to onSort callback', () => {
|
|
337
|
+
const onSort = vi.fn();
|
|
338
|
+
const { result } = renderHook(() =>
|
|
339
|
+
useDataTable({ data: mockData, pagination: mockPagination, onSort })
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
act(() => result.current.handleSort(null));
|
|
343
|
+
|
|
344
|
+
expect(onSort).toHaveBeenCalledWith(null);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('delegates pagination to onPaginate callback', () => {
|
|
348
|
+
const onPaginate = vi.fn();
|
|
349
|
+
const { result } = renderHook(() =>
|
|
350
|
+
useDataTable({ data: mockData, pagination: mockPagination, onPaginate })
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
act(() => result.current.handlePaginate(2));
|
|
354
|
+
|
|
355
|
+
expect(onPaginate).toHaveBeenCalledWith(2);
|
|
356
|
+
expect(onPaginate).toHaveBeenCalledTimes(1);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('returns data as-is without filtering', () => {
|
|
360
|
+
const { result } = renderHook(() =>
|
|
361
|
+
useDataTable({ data: mockData, pagination: mockPagination })
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
expect(result.current.pagedData).toEqual(mockData);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('returns server pagination meta as-is', () => {
|
|
368
|
+
const { result } = renderHook(() =>
|
|
369
|
+
useDataTable({ data: mockData, pagination: mockPagination })
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
expect(result.current.paginationMeta).toEqual(mockPagination);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('uses controlled searchValue for currentSearch', () => {
|
|
376
|
+
const { result } = renderHook(() =>
|
|
377
|
+
useDataTable({
|
|
378
|
+
data: mockData,
|
|
379
|
+
pagination: mockPagination,
|
|
380
|
+
onSearch: vi.fn(),
|
|
381
|
+
searchValue: 'alice',
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
expect(result.current.currentSearch).toBe('alice');
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('uses controlled sortValue for currentSort', () => {
|
|
389
|
+
const sort = { name: 'name' as keyof TestItem, direction: 'asc' as const };
|
|
390
|
+
const { result } = renderHook(() =>
|
|
391
|
+
useDataTable({
|
|
392
|
+
data: mockData,
|
|
393
|
+
pagination: mockPagination,
|
|
394
|
+
onSort: vi.fn(),
|
|
395
|
+
sortValue: sort,
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
expect(result.current.currentSort).toEqual(sort);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('does not filter data client-side even when clientSearchKeys is set', () => {
|
|
403
|
+
const onSearch = vi.fn();
|
|
404
|
+
const { result } = renderHook(() =>
|
|
405
|
+
useDataTable({
|
|
406
|
+
data: mockData,
|
|
407
|
+
pagination: mockPagination,
|
|
408
|
+
onSearch,
|
|
409
|
+
clientSearchKeys: ['name'],
|
|
410
|
+
})
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
act(() => result.current.handleSearch('alice'));
|
|
414
|
+
|
|
415
|
+
// Data should not be filtered locally
|
|
416
|
+
expect(result.current.pagedData).toHaveLength(5);
|
|
417
|
+
// Callback should be called instead
|
|
418
|
+
expect(onSearch).toHaveBeenCalledWith('alice');
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// ─────────────────────────────────────────────
|
|
423
|
+
// Edge cases
|
|
424
|
+
// ─────────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
describe('edge cases', () => {
|
|
427
|
+
it('handles empty data array', () => {
|
|
428
|
+
const { result } = renderHook(() =>
|
|
429
|
+
useDataTable({ data: [] })
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
expect(result.current.pagedData).toEqual([]);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('handles empty data with pagination', () => {
|
|
436
|
+
const { result } = renderHook(() =>
|
|
437
|
+
useDataTable({ data: [], clientPageSize: 20 })
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
expect(result.current.pagedData).toHaveLength(0);
|
|
441
|
+
expect(result.current.paginationMeta?.total).toBe(0);
|
|
442
|
+
expect(result.current.paginationMeta?.pageCount).toBe(0);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('handles clientPageSize larger than dataset', () => {
|
|
446
|
+
const { result } = renderHook(() =>
|
|
447
|
+
useDataTable({ data: mockData, clientPageSize: 100 })
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
expect(result.current.pagedData).toHaveLength(5);
|
|
451
|
+
expect(result.current.paginationMeta?.pageCount).toBe(1);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('does not mutate original data when sorting', () => {
|
|
455
|
+
const originalData = [...mockData];
|
|
456
|
+
const { result } = renderHook(() =>
|
|
457
|
+
useDataTable({ data: mockData })
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
act(() => result.current.handleSort({ name: 'name', direction: 'desc' }));
|
|
461
|
+
|
|
462
|
+
expect(mockData).toEqual(originalData);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -29,4 +29,24 @@ export interface DataTableProps<T extends { id: string | number }> {
|
|
|
29
29
|
striped?: boolean,
|
|
30
30
|
hover?: boolean,
|
|
31
31
|
style?: CSSProperties,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface DataTableControlsProps {
|
|
35
|
+
currentSearch?: string | null;
|
|
36
|
+
handleSearch: (query: string) => void;
|
|
37
|
+
entity?: Entity
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface BooleanCellProps<T extends { id: number | string }> {
|
|
41
|
+
item: T;
|
|
42
|
+
column: Column<T>;
|
|
43
|
+
value: T[keyof T];
|
|
44
|
+
updateData?: (id: string | number, values: Partial<T>) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface InlineInputCellProps<T extends { id: string | number }> {
|
|
48
|
+
formField: FormField;
|
|
49
|
+
item: T;
|
|
50
|
+
value: T[keyof T];
|
|
51
|
+
updateData?: (id: string | number, values: Partial<T>) => void;
|
|
32
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {FormField} from "../types";
|
|
2
2
|
import {EmptyCell} from "../components/cell-types/EmptyCell";
|
|
3
3
|
import {BooleanCell} from "../components/cell-types/BooleanCell";
|
|
4
4
|
import {Link, Pill} from "@sio-group/ui-core";
|
|
@@ -6,14 +6,7 @@ import {renderObject} from "./render-object";
|
|
|
6
6
|
import {DateCell} from "../components/cell-types/DateCell";
|
|
7
7
|
import {isPillValue} from "./is-pill-value";
|
|
8
8
|
import {InlineInputCell} from "../components/cell-types/InlineInputCell";
|
|
9
|
-
|
|
10
|
-
interface RenderValueProps <T extends { id: string | number }> {
|
|
11
|
-
value: T[keyof T];
|
|
12
|
-
column: Column<T>;
|
|
13
|
-
item: T;
|
|
14
|
-
formFields?: FormField[];
|
|
15
|
-
updateData?: (id: (string | number), values: Partial<T>) => void;
|
|
16
|
-
}
|
|
9
|
+
import {RenderValueProps} from "../types/render-value-props";
|
|
17
10
|
|
|
18
11
|
export const renderValue = <T extends { id: string | number }> ({value, column, item, formFields, updateData}: RenderValueProps<T>) => {
|
|
19
12
|
const formatKey: string | undefined = typeof column.format === 'object' ? column.format.key : undefined;
|
|
@@ -22,7 +15,6 @@ export const renderValue = <T extends { id: string | number }> ({value, column,
|
|
|
22
15
|
if (formField) {
|
|
23
16
|
return (
|
|
24
17
|
<InlineInputCell
|
|
25
|
-
column={column}
|
|
26
18
|
value={value}
|
|
27
19
|
item={item}
|
|
28
20
|
formField={formField}
|
|
@@ -49,7 +41,7 @@ export const renderValue = <T extends { id: string | number }> ({value, column,
|
|
|
49
41
|
return (
|
|
50
42
|
<DateCell
|
|
51
43
|
column={column}
|
|
52
|
-
value={value}
|
|
44
|
+
value={String(value)}
|
|
53
45
|
/>
|
|
54
46
|
);
|
|
55
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/hooks/usedatatable.ts","./src/types/action-cell-props.d.ts","./src/types/action-menu.d.ts","./src/types/column.d.ts","./src/types/data-table-body-props.d.ts","./src/types/data-table-header-props.d.ts","./src/types/data-table-props.d.ts","./src/types/entity.d.ts","./src/types/form-field.d.ts","./src/types/index.ts","./src/types/pagination-meta.d.ts","./src/types/render-value-props.d.ts","./src/types/sort-state.d.ts","./src/types/table-cell-props.d.ts","./src/types/use-data-table-props.d.ts","./src/types/use-data-table-return.d.ts","./src/utils/is-pill-value.ts","./src/components/actioncell.tsx","./src/components/datatable.tsx","./src/components/datatablebody.tsx","./src/components/datatablecontrols.tsx","./src/components/datatableheader.tsx","./src/components/defaultsorticon.tsx","./src/components/tablecell.tsx","./src/components/cell-types/booleancell.tsx","./src/components/cell-types/datecell.tsx","./src/components/cell-types/emptycell.tsx","./src/components/cell-types/inlineinputcell.tsx","./src/utils/render-object.tsx","./src/utils/render-value.tsx"],"errors":true,"version":"5.9.3"}
|