@tsed/react-formio 3.0.0-rc.2 → 3.0.0-rc.21
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/all.js +2 -0
- package/dist/all.js.map +1 -1
- package/dist/chunks/_baseGet.js +102 -0
- package/dist/chunks/_baseGet.js.map +1 -0
- package/dist/chunks/_baseSlice.js +12 -65
- package/dist/chunks/_baseSlice.js.map +1 -1
- package/dist/chunks/camelCase.js +1 -1
- package/dist/chunks/get.js +16 -0
- package/dist/chunks/get.js.map +1 -0
- package/dist/chunks/index2.js +19746 -22285
- package/dist/chunks/index2.js.map +1 -1
- package/dist/chunks/moment.js +2535 -0
- package/dist/chunks/moment.js.map +1 -0
- package/dist/chunks/omit.js +173 -268
- package/dist/chunks/omit.js.map +1 -1
- package/dist/chunks/toString.js +56 -0
- package/dist/chunks/toString.js.map +1 -0
- package/dist/interfaces/JSONRecord.d.ts +4 -0
- package/dist/interfaces/JSONRecord.js +2 -0
- package/dist/interfaces/JSONRecord.js.map +1 -0
- package/dist/interfaces/Operation.d.ts +3 -12
- package/dist/interfaces/SubmissionType.d.ts +4 -14
- package/dist/interfaces/index.d.ts +0 -1
- package/dist/molecules/forms/select/Select.interface.d.ts +0 -4
- package/dist/molecules/table/Table.d.ts +4 -12
- package/dist/molecules/table/Table.js +34 -33
- package/dist/molecules/table/Table.js.map +1 -1
- package/dist/molecules/table/all.js +2 -0
- package/dist/molecules/table/all.js.map +1 -1
- package/dist/molecules/table/components/DefaultBooleanCell.d.ts +2 -0
- package/dist/molecules/table/components/DefaultBooleanCell.js +12 -0
- package/dist/molecules/table/components/DefaultBooleanCell.js.map +1 -0
- package/dist/molecules/table/components/DefaultCell.d.ts +2 -5
- package/dist/molecules/table/components/DefaultCell.js +8 -6
- package/dist/molecules/table/components/DefaultCell.js.map +1 -1
- package/dist/molecules/table/components/DefaultCellOperations.d.ts +4 -11
- package/dist/molecules/table/components/DefaultCellOperations.js.map +1 -1
- package/dist/molecules/table/components/DefaultDateCell.d.ts +2 -0
- package/dist/molecules/table/components/DefaultDateCell.js +16 -0
- package/dist/molecules/table/components/DefaultDateCell.js.map +1 -0
- package/dist/molecules/table/components/DefaultFilter.d.ts +5 -7
- package/dist/molecules/table/components/DefaultFilter.js +8 -8
- package/dist/molecules/table/components/DefaultFilter.js.map +1 -1
- package/dist/molecules/table/components/DefaultOperationButton.d.ts +4 -11
- package/dist/molecules/table/components/DefaultOperationButton.js.map +1 -1
- package/dist/molecules/table/filters/Filters.d.ts +27 -0
- package/dist/molecules/table/filters/Filters.js +2 -0
- package/dist/molecules/table/filters/Filters.js.map +1 -0
- package/dist/molecules/table/filters/SelectFilter.js +22 -20
- package/dist/molecules/table/filters/SelectFilter.js.map +1 -1
- package/dist/molecules/table/filters/TextFieldFilter.js +16 -16
- package/dist/molecules/table/filters/TextFieldFilter.js.map +1 -1
- package/dist/molecules/table/hooks/useTable.d.ts +4 -11
- package/dist/molecules/table/hooks/useTable.js +14 -14
- package/dist/molecules/table/hooks/useTable.js.map +1 -1
- package/dist/molecules/table/hooks/useUniqValues.d.ts +4 -5
- package/dist/molecules/table/hooks/useUniqValues.js +23 -7
- package/dist/molecules/table/hooks/useUniqValues.js.map +1 -1
- package/dist/molecules/table/interfaces/extends.d.ts +3 -0
- package/dist/molecules/table/utils/mapFormToColumns.d.ts +5 -1
- package/dist/molecules/table/utils/mapFormToColumns.js +53 -26
- package/dist/molecules/table/utils/mapFormToColumns.js.map +1 -1
- package/dist/organisms/form/Form.d.ts +3 -11
- package/dist/organisms/form/Form.js.map +1 -1
- package/dist/organisms/form/actions/FormAction.js +5 -5
- package/dist/organisms/form/types.d.ts +1 -9
- package/dist/organisms/form/useForm.d.ts +4 -11
- package/dist/organisms/form/useForm.js.map +1 -1
- package/dist/organisms/table/forms/components/FormsCell.js +1 -1
- package/dist/organisms/table/submissions/SubmissionsTable.d.ts +4 -11
- package/dist/organisms/table/submissions/SubmissionsTable.js +2 -5
- package/dist/organisms/table/submissions/SubmissionsTable.js.map +1 -1
- package/dist/organisms/views/FormViews.d.ts +4 -11
- package/dist/organisms/views/FormViews.js.map +1 -1
- package/package.json +3 -3
- package/src/all.ts +2 -0
- package/src/interfaces/JSONRecord.ts +2 -0
- package/src/interfaces/Operation.ts +3 -6
- package/src/interfaces/SubmissionType.ts +4 -8
- package/src/interfaces/index.ts +0 -1
- package/src/molecules/forms/select/Select.interface.ts +0 -4
- package/src/molecules/table/Table.stories.tsx +101 -66
- package/src/molecules/table/Table.tsx +57 -56
- package/src/molecules/table/all.ts +2 -0
- package/src/molecules/table/components/DefaultBooleanCell.spec.tsx +42 -0
- package/src/molecules/table/components/DefaultBooleanCell.tsx +11 -0
- package/src/molecules/table/components/DefaultCell.spec.tsx +32 -0
- package/src/molecules/table/components/DefaultCell.tsx +8 -9
- package/src/molecules/table/components/DefaultCellOperations.tsx +4 -3
- package/src/molecules/table/components/DefaultDateCell.spec.tsx +43 -0
- package/src/molecules/table/components/DefaultDateCell.tsx +23 -0
- package/src/molecules/table/components/DefaultFilter.tsx +10 -7
- package/src/molecules/table/components/DefaultOperationButton.tsx +4 -4
- package/src/molecules/table/filters/{Filters.d.ts → Filters.ts} +7 -3
- package/src/molecules/table/filters/SelectFilter.tsx +5 -3
- package/src/molecules/table/filters/TextFieldFilter.tsx +5 -3
- package/src/molecules/table/hooks/useTable.tsx +8 -13
- package/src/molecules/table/hooks/useUniqValues.spec.tsx +82 -0
- package/src/molecules/table/hooks/useUniqValues.tsx +38 -6
- package/src/molecules/table/interfaces/extends.ts +3 -0
- package/src/molecules/table/utils/mapFormToColumns.spec.tsx +116 -0
- package/src/molecules/table/utils/mapFormToColumns.tsx +67 -27
- package/src/organisms/form/Form.stories.tsx +7 -2
- package/src/organisms/form/Form.tsx +3 -3
- package/src/organisms/form/types.ts +1 -6
- package/src/organisms/form/useForm.ts +6 -5
- package/src/organisms/table/submissions/SubmissionsTable.tsx +5 -10
- package/src/organisms/views/FormViews.tsx +6 -9
- package/dist/interfaces/QueryOptions.d.ts +0 -23
- package/dist/interfaces/QueryOptions.js +0 -2
- package/dist/interfaces/QueryOptions.js.map +0 -1
- package/dist/molecules/table/filters/Filters.d.js +0 -2
- package/dist/molecules/table/filters/Filters.d.js.map +0 -1
- package/src/interfaces/QueryOptions.ts +0 -24
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
1
|
import type { OperationButtonProps } from "../molecules/table/components/DefaultOperationButton.js";
|
|
4
|
-
import type {
|
|
2
|
+
import type { JSONRecord } from "./JSONRecord.js";
|
|
5
3
|
|
|
6
4
|
export type CellMetadata = Record<string, unknown>;
|
|
7
|
-
|
|
8
|
-
export type PermissionsResolver<Data extends { [key: string]: JSON } = { [key: string]: JSON }> = (
|
|
5
|
+
export type PermissionsResolver<Data extends object = JSONRecord> = (
|
|
9
6
|
data: Data,
|
|
10
7
|
metadata: CellMetadata,
|
|
11
8
|
operation: Operation<Data>
|
|
12
9
|
) => void;
|
|
13
10
|
|
|
14
|
-
export interface Operation<Data extends
|
|
11
|
+
export interface Operation<Data extends object = JSONRecord> {
|
|
15
12
|
/**
|
|
16
13
|
* Action identifier
|
|
17
14
|
*/
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import type { Component, Submission } from "@formio/core";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type { JSONRecord } from "./JSONRecord.js";
|
|
4
4
|
|
|
5
|
-
export type SubmissionType<Data extends
|
|
5
|
+
export type SubmissionType<Data extends object = JSONRecord> = Omit<Partial<Submission>, "data"> & {
|
|
6
6
|
data: Data;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export interface ChangedSubmission<
|
|
10
|
-
Data extends { [key: string]: JSON } = {
|
|
11
|
-
[key: string]: JSON;
|
|
12
|
-
}
|
|
13
|
-
> extends SubmissionType<Data> {
|
|
9
|
+
export interface ChangedSubmission<Data extends object = JSONRecord> extends SubmissionType<Data> {
|
|
14
10
|
data: Data;
|
|
15
11
|
changed?: {
|
|
16
12
|
component: Component;
|
|
17
13
|
instance: any;
|
|
18
14
|
value: unknown;
|
|
19
|
-
} &
|
|
15
|
+
} & JSONRecord;
|
|
20
16
|
isValid: boolean;
|
|
21
17
|
}
|
package/src/interfaces/index.ts
CHANGED
|
@@ -28,10 +28,6 @@ export interface SelectProps<Data = string> extends FormControlProps<Data, Selec
|
|
|
28
28
|
searchEnabled?: boolean;
|
|
29
29
|
customProperties?: Record<string, any>;
|
|
30
30
|
options: (SelectOptionBaseProps<Data> | Omit<SelectOptionProps<Data>, "value">)[];
|
|
31
|
-
/**
|
|
32
|
-
* @deprecated
|
|
33
|
-
*/
|
|
34
|
-
choices?: (SelectOptionBaseProps<Data> | Omit<SelectOptionProps<Data>, "value">)[];
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
export interface SelectSingle<Data = string> extends SelectProps<Data> {
|
|
@@ -74,7 +74,7 @@ type Story = StoryObj<typeof Table<ProductSubmission>>;
|
|
|
74
74
|
export const Usage: Story = {
|
|
75
75
|
args: {
|
|
76
76
|
data: formSubmissions as unknown as ProductSubmission[],
|
|
77
|
-
columns: mapFormToColumns(FormType as any),
|
|
77
|
+
columns: mapFormToColumns({ form: FormType as any }),
|
|
78
78
|
operations: [
|
|
79
79
|
{
|
|
80
80
|
title: "Edit",
|
|
@@ -172,7 +172,10 @@ export const Usage: Story = {
|
|
|
172
172
|
return expect(canvas.queryByText("MPEC")).not.toBeInTheDocument();
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
const tableBody = canvasElement.querySelector("tbody");
|
|
176
|
+
|
|
177
|
+
await expect(tableBody).toBeInTheDocument();
|
|
178
|
+
await expect(within(tableBody as HTMLElement).getByText("La Caravelle")).toBeInTheDocument();
|
|
176
179
|
|
|
177
180
|
await delay(300);
|
|
178
181
|
|
|
@@ -183,17 +186,20 @@ export const Usage: Story = {
|
|
|
183
186
|
export const WithFilters: Story = {
|
|
184
187
|
args: {
|
|
185
188
|
data: formSubmissions as unknown as ProductSubmission[],
|
|
186
|
-
columns: mapFormToColumns(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
columns: mapFormToColumns({
|
|
190
|
+
form: FormType as any,
|
|
191
|
+
columns: [
|
|
192
|
+
{
|
|
193
|
+
accessorKey: "data.id",
|
|
194
|
+
meta: {
|
|
195
|
+
filter: {
|
|
196
|
+
variant: "select",
|
|
197
|
+
layout: "react"
|
|
198
|
+
}
|
|
193
199
|
}
|
|
194
200
|
}
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
]
|
|
202
|
+
}),
|
|
197
203
|
operations: [
|
|
198
204
|
{
|
|
199
205
|
title: "Edit",
|
|
@@ -217,70 +223,99 @@ export const WithFilters: Story = {
|
|
|
217
223
|
}
|
|
218
224
|
};
|
|
219
225
|
|
|
220
|
-
export const
|
|
226
|
+
export const WithPaginationOptions: Story = {
|
|
221
227
|
args: {
|
|
222
228
|
data: formSubmissions as unknown as ProductSubmission[],
|
|
223
|
-
columns: mapFormToColumns
|
|
229
|
+
columns: mapFormToColumns({ form: FormType as any }),
|
|
230
|
+
operations: [
|
|
224
231
|
{
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
title: "Edit",
|
|
233
|
+
action: "edit",
|
|
234
|
+
alias: "row",
|
|
235
|
+
path: "/resources/:resourceId/submissions/:submissionId",
|
|
236
|
+
icon: "edit"
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
i18n: {},
|
|
240
|
+
enablePagination: true,
|
|
241
|
+
pageSizes: [5, 10, 25],
|
|
242
|
+
rowCount: formSubmissions.length,
|
|
243
|
+
initialState: {
|
|
244
|
+
pagination: {
|
|
245
|
+
pageIndex: 0,
|
|
246
|
+
pageSize: 5
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const WithCustomCell: Story = {
|
|
253
|
+
args: {
|
|
254
|
+
data: formSubmissions as unknown as ProductSubmission[],
|
|
255
|
+
columns: mapFormToColumns<ProductSubmission>({
|
|
256
|
+
form: FormType as any,
|
|
257
|
+
columns: [
|
|
258
|
+
{
|
|
259
|
+
accessorKey: "data.id",
|
|
260
|
+
meta: {
|
|
261
|
+
filter: {
|
|
262
|
+
variant: "select",
|
|
263
|
+
layout: "react"
|
|
264
|
+
},
|
|
265
|
+
cellProps: {
|
|
266
|
+
colSpan: 2
|
|
267
|
+
}
|
|
230
268
|
},
|
|
231
|
-
|
|
232
|
-
|
|
269
|
+
cell: (context) => {
|
|
270
|
+
return (
|
|
271
|
+
<div className='flex space-x-4 align-items-center'>
|
|
272
|
+
<div className='max-w-[80px]'>
|
|
273
|
+
<img className='max-w-[80px] rounded-md' src={context.row.original.data.image} alt={context.row.original.data.label} />
|
|
274
|
+
</div>
|
|
275
|
+
<div>
|
|
276
|
+
<div className='text-lg text-primary'>{context.row.original.data.label}</div>
|
|
277
|
+
<div className='text-xs'>{context.getValue()}</div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
233
281
|
}
|
|
234
282
|
},
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
<img className='max-w-[80px] rounded-md' src={context.row.original.data.image} alt={context.row.original.data.label} />
|
|
240
|
-
</div>
|
|
241
|
-
<div>
|
|
242
|
-
<div className='text-lg text-primary'>{context.row.original.data.label}</div>
|
|
243
|
-
<div className='text-xs'>{context.getValue()}</div>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
accessorKey: "data.label",
|
|
251
|
-
meta: {
|
|
252
|
-
hidden: true
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
accessorKey: "data.description",
|
|
257
|
-
meta: {
|
|
258
|
-
filter: {
|
|
259
|
-
variant: "text",
|
|
260
|
-
disableDatalist: true
|
|
283
|
+
{
|
|
284
|
+
accessorKey: "data.label",
|
|
285
|
+
meta: {
|
|
286
|
+
hidden: true
|
|
261
287
|
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
accessorKey: "data.description",
|
|
291
|
+
meta: {
|
|
292
|
+
filter: {
|
|
293
|
+
variant: "text",
|
|
294
|
+
disableDatalist: true
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
accessorKey: "data.price",
|
|
300
|
+
cell: (context) => {
|
|
301
|
+
const value = context.getValue();
|
|
302
|
+
|
|
303
|
+
if (value === undefined) {
|
|
304
|
+
return "-";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div className='text-right'>
|
|
309
|
+
{Intl.NumberFormat("fr-FR", {
|
|
310
|
+
style: "currency",
|
|
311
|
+
currency: context.row.original.data.currency
|
|
312
|
+
}).format(context.getValue())}
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
271
315
|
}
|
|
272
|
-
|
|
273
|
-
return (
|
|
274
|
-
<div className='text-right'>
|
|
275
|
-
{Intl.NumberFormat("fr-FR", {
|
|
276
|
-
style: "currency",
|
|
277
|
-
currency: context.row.original.data.currency
|
|
278
|
-
}).format(context.getValue())}
|
|
279
|
-
</div>
|
|
280
|
-
);
|
|
281
316
|
}
|
|
282
|
-
|
|
283
|
-
|
|
317
|
+
]
|
|
318
|
+
}),
|
|
284
319
|
operations: [
|
|
285
320
|
{
|
|
286
321
|
title: "Edit",
|
|
@@ -2,24 +2,23 @@ import { flexRender } from "@tanstack/react-table";
|
|
|
2
2
|
import cx from "classnames";
|
|
3
3
|
import { PropsWithChildren } from "react";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { JSONRecord } from "../../interfaces/JSONRecord.js";
|
|
6
6
|
import { getComponent } from "../../registries/components";
|
|
7
7
|
import type { Pagination as DefaultPagination } from "../pagination/Pagination";
|
|
8
8
|
import type { DefaultCellFooter } from "./components/DefaultCellFooter";
|
|
9
9
|
import type { DefaultCellHeader } from "./components/DefaultCellHeader";
|
|
10
10
|
import { useTable, UseTableProps } from "./hooks/useTable";
|
|
11
11
|
|
|
12
|
-
export interface TableProps<Data extends
|
|
12
|
+
export interface TableProps<Data extends object = JSONRecord> extends UseTableProps<Data> {
|
|
13
13
|
className?: string;
|
|
14
|
-
|
|
15
14
|
enableFooter?: boolean;
|
|
16
|
-
|
|
17
|
-
pageSizes?: number[];
|
|
15
|
+
enablePagination?: boolean;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
export function Table<Data extends
|
|
18
|
+
export function Table<Data extends object = JSONRecord>({
|
|
21
19
|
className,
|
|
22
20
|
enableFooter,
|
|
21
|
+
enablePagination = true,
|
|
23
22
|
children,
|
|
24
23
|
...props
|
|
25
24
|
}: PropsWithChildren<TableProps<Data>>) {
|
|
@@ -31,58 +30,60 @@ export function Table<Data extends { [key: string]: JSON } = { [key: string]: JS
|
|
|
31
30
|
const { pagination } = tableInstance.getState();
|
|
32
31
|
|
|
33
32
|
return (
|
|
34
|
-
<div className={cx("table-group
|
|
35
|
-
<
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{headerGroup.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
))}
|
|
53
|
-
</thead>
|
|
54
|
-
<tbody>
|
|
55
|
-
{tableInstance.getRowModel().rows.map((row) => {
|
|
56
|
-
return (
|
|
57
|
-
<tr key={row.id} data-testid={`body-row-${row.id}`}>
|
|
58
|
-
{row
|
|
59
|
-
.getVisibleCells()
|
|
60
|
-
.filter((cell) => !cell.column.columnDef.meta?.hidden)
|
|
61
|
-
.map((cell) => {
|
|
62
|
-
return (
|
|
63
|
-
<td {...cell.column.columnDef?.meta?.cellProps} key={cell.id} data-testid={`body-cell-${cell.id}`}>
|
|
64
|
-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
65
|
-
</td>
|
|
66
|
-
);
|
|
67
|
-
})}
|
|
68
|
-
</tr>
|
|
69
|
-
);
|
|
70
|
-
})}
|
|
71
|
-
</tbody>
|
|
72
|
-
{enableFooter && (
|
|
73
|
-
<tfoot>
|
|
74
|
-
{tableInstance.getFooterGroups().map((footerGroup) => (
|
|
75
|
-
<tr key={footerGroup.id}>
|
|
76
|
-
{footerGroup.headers.map((header) => (
|
|
77
|
-
<th key={header.id}>{header.isPlaceholder ? null : <CellFooter header={header} i18n={i18n} />}</th>
|
|
78
|
-
))}
|
|
33
|
+
<div className={cx("table-group", className)}>
|
|
34
|
+
<div className='table-group-body'>
|
|
35
|
+
<table className='table table-striped table-hover'>
|
|
36
|
+
<thead>
|
|
37
|
+
{tableInstance.getHeaderGroups().map((headerGroup) => (
|
|
38
|
+
<tr key={headerGroup.id}>
|
|
39
|
+
{headerGroup.headers.map((header) => {
|
|
40
|
+
const sort = header.column.getIsSorted();
|
|
41
|
+
return (
|
|
42
|
+
<th
|
|
43
|
+
data-testid={`head-cell-${header.id}`}
|
|
44
|
+
key={header.id}
|
|
45
|
+
aria-sort={sort ? (sort === "asc" ? "ascending" : "descending") : "none"}
|
|
46
|
+
>
|
|
47
|
+
{header.isPlaceholder ? null : <CellHeader header={header} i18n={i18n} />}
|
|
48
|
+
</th>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
79
51
|
</tr>
|
|
80
52
|
))}
|
|
81
|
-
</
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
53
|
+
</thead>
|
|
54
|
+
<tbody>
|
|
55
|
+
{tableInstance.getRowModel().rows.map((row) => {
|
|
56
|
+
return (
|
|
57
|
+
<tr key={row.id} data-testid={`body-row-${row.id}`}>
|
|
58
|
+
{row
|
|
59
|
+
.getVisibleCells()
|
|
60
|
+
.filter((cell) => !cell.column.columnDef.meta?.hidden)
|
|
61
|
+
.map((cell) => {
|
|
62
|
+
return (
|
|
63
|
+
<td {...cell.column.columnDef?.meta?.cellProps} key={cell.id} data-testid={`body-cell-${cell.id}`}>
|
|
64
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
65
|
+
</td>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</tr>
|
|
69
|
+
);
|
|
70
|
+
})}
|
|
71
|
+
</tbody>
|
|
72
|
+
{enableFooter && (
|
|
73
|
+
<tfoot>
|
|
74
|
+
{tableInstance.getFooterGroups().map((footerGroup) => (
|
|
75
|
+
<tr key={footerGroup.id}>
|
|
76
|
+
{footerGroup.headers.map((header) => (
|
|
77
|
+
<th key={header.id}>{header.isPlaceholder ? null : <CellFooter header={header} i18n={i18n} />}</th>
|
|
78
|
+
))}
|
|
79
|
+
</tr>
|
|
80
|
+
))}
|
|
81
|
+
</tfoot>
|
|
82
|
+
)}
|
|
83
|
+
</table>
|
|
84
|
+
</div>
|
|
85
|
+
<div className='table-group-footer'>
|
|
86
|
+
{props.data.length && pagination && enablePagination ? (
|
|
86
87
|
<Pagination
|
|
87
88
|
className={"flex-1"}
|
|
88
89
|
canNextPage={tableInstance.getCanNextPage()}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "./components/DefaultFilter";
|
|
2
2
|
import "./components/DefaultArrowSort";
|
|
3
3
|
import "./components/DefaultCell";
|
|
4
|
+
import "./components/DefaultBooleanCell";
|
|
5
|
+
import "./components/DefaultDateCell";
|
|
4
6
|
import "./components/DefaultCellOperations";
|
|
5
7
|
import "./components/DefaultOperationButton";
|
|
6
8
|
import "./components/DefaultCellHeader";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { DefaultCellBoolean } from "./DefaultBooleanCell";
|
|
4
|
+
|
|
5
|
+
function createCellContext(value: boolean, labels?: Record<string, string>) {
|
|
6
|
+
return {
|
|
7
|
+
getValue: () => value,
|
|
8
|
+
column: {
|
|
9
|
+
columnDef: {
|
|
10
|
+
meta: {
|
|
11
|
+
labels
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
} as any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("DefaultCellBoolean", () => {
|
|
19
|
+
it("should render the default yes label for true values", () => {
|
|
20
|
+
render(<DefaultCellBoolean {...createCellContext(true)} />);
|
|
21
|
+
|
|
22
|
+
expect(screen.getByText("Yes", { selector: "span" })).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should render the default no label for false values", () => {
|
|
26
|
+
render(<DefaultCellBoolean {...createCellContext(false)} />);
|
|
27
|
+
|
|
28
|
+
expect(screen.getByText("No", { selector: "span" })).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should render custom labels from column metadata", () => {
|
|
32
|
+
render(<DefaultCellBoolean {...createCellContext(true, { yes: "Enabled", no: "Disabled" })} />);
|
|
33
|
+
|
|
34
|
+
expect(screen.getByText("Enabled", { selector: "span" })).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should render the custom false label from column metadata", () => {
|
|
38
|
+
render(<DefaultCellBoolean {...createCellContext(false, { yes: "Enabled", no: "Disabled" })} />);
|
|
39
|
+
|
|
40
|
+
expect(screen.getByText("Disabled", { selector: "span" })).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CellContext } from "@tanstack/react-table";
|
|
2
|
+
|
|
3
|
+
import { registerComponent } from "../../../registries/components";
|
|
4
|
+
|
|
5
|
+
export function DefaultCellBoolean<Data extends object>({ getValue, column: { columnDef } }: CellContext<Data, boolean>) {
|
|
6
|
+
const value = getValue();
|
|
7
|
+
return <span>{String(value ? columnDef.meta?.labels?.["yes"] || "Yes" : columnDef.meta?.labels?.["no"] || "No")}</span>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
registerComponent("Cell.boolean", DefaultCellBoolean);
|
|
11
|
+
registerComponent("Cell.checkbox", DefaultCellBoolean);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { DefaultCell } from "./DefaultCell";
|
|
4
|
+
|
|
5
|
+
function createCellContext(value: unknown, rendered: unknown) {
|
|
6
|
+
return {
|
|
7
|
+
getValue: () => value,
|
|
8
|
+
renderValue: () => rendered
|
|
9
|
+
} as any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("DefaultCell", () => {
|
|
13
|
+
it("should render an empty span when value is undefined", () => {
|
|
14
|
+
render(<DefaultCell {...createCellContext(undefined, undefined)} />);
|
|
15
|
+
|
|
16
|
+
expect(screen.getByText("", { selector: "span" })).toBeInTheDocument();
|
|
17
|
+
expect(screen.queryByText(/.+/)).not.toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should render html when rendered value differs from raw value", () => {
|
|
21
|
+
render(<DefaultCell {...createCellContext("hello", "<strong>hello</strong>")} />);
|
|
22
|
+
|
|
23
|
+
expect(screen.getByText("hello", { selector: "strong" })).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should render text when rendered value matches raw value", () => {
|
|
27
|
+
render(<DefaultCell {...createCellContext("hello", "hello")} />);
|
|
28
|
+
|
|
29
|
+
expect(screen.getByText("hello", { selector: "span" })).toBeInTheDocument();
|
|
30
|
+
expect(screen.queryByText("hello", { selector: "strong" })).not.toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CellContext } from "@tanstack/react-table";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
value: Data;
|
|
5
|
-
render?: (value: Data) => any;
|
|
6
|
-
}
|
|
3
|
+
import { registerComponent } from "../../../registries/components";
|
|
7
4
|
|
|
8
|
-
export function DefaultCell<Data = any>({
|
|
5
|
+
export function DefaultCell<Data = any>({ getValue, renderValue }: CellContext<Data, any>): JSX.Element {
|
|
6
|
+
const value = getValue();
|
|
9
7
|
if (value === undefined) {
|
|
10
8
|
return <span></span>;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
const rendered =
|
|
11
|
+
const rendered = renderValue();
|
|
14
12
|
|
|
15
|
-
if (value !== rendered) {
|
|
16
|
-
return <div dangerouslySetInnerHTML={{ __html: rendered }} />;
|
|
13
|
+
if (rendered != null && value !== rendered) {
|
|
14
|
+
return <div dangerouslySetInnerHTML={{ __html: String(rendered) }} />;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
return <span>{String(value)}</span>;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
registerComponent("Cell", DefaultCell);
|
|
21
|
+
registerComponent("Cell.string", DefaultCell);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { CellContext } from "@tanstack/react-table";
|
|
2
2
|
|
|
3
|
-
import type { CellMetadata,
|
|
3
|
+
import type { CellMetadata, Operation } from "../../../interfaces";
|
|
4
|
+
import type { JSONRecord } from "../../../interfaces/JSONRecord.js";
|
|
4
5
|
import { getComponent, registerComponent } from "../../../registries/components";
|
|
5
6
|
import type { DefaultOperationButton } from "./DefaultOperationButton";
|
|
6
7
|
|
|
7
|
-
export interface DefaultCellOperationsProps<Data extends
|
|
8
|
+
export interface DefaultCellOperationsProps<Data extends object = JSONRecord> {
|
|
8
9
|
info: CellContext<Data, unknown>;
|
|
9
10
|
operations: Operation<Data>[];
|
|
10
11
|
metadata?: CellMetadata;
|
|
@@ -12,7 +13,7 @@ export interface DefaultCellOperationsProps<Data extends { [key: string]: JSON }
|
|
|
12
13
|
onClick?: (data: any, operation: Operation<Data>) => void;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export function DefaultCellOperations<Data extends
|
|
16
|
+
export function DefaultCellOperations<Data extends object = JSONRecord>({
|
|
16
17
|
info,
|
|
17
18
|
metadata,
|
|
18
19
|
operations,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
|
|
3
|
+
import { DefaultDateCell } from "./DefaultDateCell";
|
|
4
|
+
|
|
5
|
+
function createCellContext(value: string | undefined, format?: string) {
|
|
6
|
+
return {
|
|
7
|
+
getValue: () => value,
|
|
8
|
+
column: {
|
|
9
|
+
columnDef: {
|
|
10
|
+
meta: {
|
|
11
|
+
format
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
} as any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("DefaultDateCell", () => {
|
|
19
|
+
it("should render an empty span when value is undefined", () => {
|
|
20
|
+
render(<DefaultDateCell {...createCellContext(undefined)} />);
|
|
21
|
+
|
|
22
|
+
expect(screen.getByText("", { selector: "span" })).toBeInTheDocument();
|
|
23
|
+
expect(screen.queryByText(/.+/)).not.toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should render the formatted date with the default format", () => {
|
|
27
|
+
render(<DefaultDateCell {...createCellContext("2026-03-12T10:30:00.000Z")} />);
|
|
28
|
+
|
|
29
|
+
expect(screen.getByText("03/12/2026", { selector: "span" })).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should render the formatted date with a custom format", () => {
|
|
33
|
+
render(<DefaultDateCell {...createCellContext("2026-03-12T10:30:00.000Z", "YYYY-MM-DD HH:mm")} />);
|
|
34
|
+
|
|
35
|
+
expect(screen.getByText("2026-03-12 10:30", { selector: "span" })).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should fallback to the raw value when the date is invalid", () => {
|
|
39
|
+
render(<DefaultDateCell {...createCellContext("not-a-date")} />);
|
|
40
|
+
|
|
41
|
+
expect(screen.getByText("not-a-date", { selector: "span" })).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CellContext } from "@tanstack/react-table";
|
|
2
|
+
import moment from "moment";
|
|
3
|
+
|
|
4
|
+
import { registerComponent } from "../../../registries/components";
|
|
5
|
+
|
|
6
|
+
export function DefaultDateCell<Data extends object>({ getValue, column: { columnDef } }: CellContext<Data, string>) {
|
|
7
|
+
const value = getValue();
|
|
8
|
+
|
|
9
|
+
if (!value) {
|
|
10
|
+
return <span />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const date = moment.parseZone(value, moment.ISO_8601, true);
|
|
14
|
+
|
|
15
|
+
if (!date.isValid()) {
|
|
16
|
+
return <span>{String(value)}</span>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return <span>{date.format(columnDef.meta?.format || "L")}</span>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
registerComponent("Cell.date", DefaultDateCell);
|
|
23
|
+
registerComponent("Cell.datetime", DefaultDateCell);
|