@izumisy-tailor/tailor-data-viewer 0.1.41 → 0.1.42
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/README.md +1 -1
- package/docs/README.md +1 -0
- package/docs/columns.md +276 -0
- package/docs/compositional-api.md +12 -8
- package/docs/custom-renderers.md +77 -0
- package/docs/labels.md +77 -0
- package/package.json +1 -1
- package/src/component/column-definition.test.ts +244 -0
- package/src/component/column-definition.ts +247 -0
- package/src/component/column-selector.test.tsx +16 -20
- package/src/component/contexts/data-viewer-context.test.tsx +60 -60
- package/src/component/contexts/data-viewer-context.tsx +48 -47
- package/src/component/contexts/table-data-context.tsx +8 -61
- package/src/component/create-data-viewer.tsx +24 -7
- package/src/component/data-table-toolbar.test.tsx +7 -10
- package/src/component/data-table.test.tsx +33 -29
- package/src/component/data-table.tsx +120 -52
- package/src/component/data-view-tab-content.tsx +26 -12
- package/src/component/index.ts +8 -0
- package/src/component/label-resolver.test.ts +70 -0
- package/src/component/label-resolver.ts +36 -3
- package/src/component/saved-view-context.tsx +1 -0
- package/src/component/search-filter.test.tsx +13 -37
- package/src/component/single-record-tab-content.tsx +7 -8
- package/src/component/types.test-d.ts +314 -0
- package/src/component/types.ts +185 -0
- package/src/component/variation-filter.test.tsx +1 -1
package/README.md
CHANGED
|
@@ -183,8 +183,8 @@ function CustomDataViewer() {
|
|
|
183
183
|
return (
|
|
184
184
|
<DataViewer.TableDataProvider
|
|
185
185
|
tableName="User"
|
|
186
|
+
columns={["id", "name", "email"]}
|
|
186
187
|
initialData={{
|
|
187
|
-
selectedFields: ["id", "name", "email"],
|
|
188
188
|
sort: { field: "name", direction: "Asc" },
|
|
189
189
|
}}
|
|
190
190
|
>
|
package/docs/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Documentation for `@izumisy-tailor/tailor-data-viewer`.
|
|
|
12
12
|
|
|
13
13
|
## Configuration
|
|
14
14
|
|
|
15
|
+
- [Column Definition API](columns.md) - Flexible column definition with width, renderers, and relation fields
|
|
15
16
|
- [GraphQL Fetcher](fetcher.md) - GraphQL fetcher configuration and custom implementation
|
|
16
17
|
- [Custom Labels](labels.md) - Customizing field names, table names, and UI text (i18n support)
|
|
17
18
|
- [Custom Renderers](custom-renderers.md) - Custom cell renderers and built-in renderers
|
package/docs/columns.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Column Definition API
|
|
2
|
+
|
|
3
|
+
The Column Definition API provides a flexible way to define columns displayed in DataViewer. It supports various levels of definition, from simple field name arrays to detailed configurations including width and renderers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Use the `columns` property to specify which columns to display and their settings.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
const { TableDataProvider, ToolbarProvider, DataTable } = createDataViewer({
|
|
11
|
+
metadata,
|
|
12
|
+
fetcher,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Simple usage
|
|
16
|
+
<TableDataProvider tableName="task" columns={["title", "status", "createdAt"]}>
|
|
17
|
+
...
|
|
18
|
+
</TableDataProvider>
|
|
19
|
+
|
|
20
|
+
// Detailed configuration
|
|
21
|
+
<TableDataProvider
|
|
22
|
+
tableName="task"
|
|
23
|
+
columns={[
|
|
24
|
+
"title",
|
|
25
|
+
["status", { width: 100 }],
|
|
26
|
+
["assignee.name", { label: "Assignee" }],
|
|
27
|
+
{ field: "description", width: 300, renderer: MyCustomRenderer },
|
|
28
|
+
]}
|
|
29
|
+
>
|
|
30
|
+
...
|
|
31
|
+
</TableDataProvider>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## ColumnDef Type
|
|
35
|
+
|
|
36
|
+
Column definitions support four levels:
|
|
37
|
+
|
|
38
|
+
### Level 1: Field Path (String)
|
|
39
|
+
|
|
40
|
+
The simplest form. Specify the field name as a string.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
type ColumnDef = string;
|
|
44
|
+
|
|
45
|
+
// Example
|
|
46
|
+
const columns = ["id", "title", "status"];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Level 2a: Tuple (Field Path + Renderer)
|
|
50
|
+
|
|
51
|
+
Use when specifying a custom renderer.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
type ColumnDef = [FieldPath, CellRenderer];
|
|
55
|
+
|
|
56
|
+
// Example
|
|
57
|
+
const columns = [
|
|
58
|
+
["status", StatusBadgeRenderer],
|
|
59
|
+
["priority", PriorityRenderer],
|
|
60
|
+
];
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Level 2b: Tuple (Field Path + Options)
|
|
64
|
+
|
|
65
|
+
Use when specifying options like width or label.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
type ColumnDef = [FieldPath, ColumnOptions];
|
|
69
|
+
|
|
70
|
+
// Example
|
|
71
|
+
const columns = [
|
|
72
|
+
["title", { width: 200, label: "Title" }],
|
|
73
|
+
["description", { minWidth: 100, maxWidth: 400 }],
|
|
74
|
+
];
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Level 3: Object Form
|
|
78
|
+
|
|
79
|
+
The complete form that allows explicit specification of all options.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
type ColumnDef = ColumnDefinition;
|
|
83
|
+
|
|
84
|
+
interface ColumnDefinition {
|
|
85
|
+
/** Field path (can specify relation fields using dot notation) */
|
|
86
|
+
field: FieldPath;
|
|
87
|
+
/** Column width (number or string) */
|
|
88
|
+
width?: number | string;
|
|
89
|
+
/** Minimum width */
|
|
90
|
+
minWidth?: number | string;
|
|
91
|
+
/** Maximum width */
|
|
92
|
+
maxWidth?: number | string;
|
|
93
|
+
/** Custom cell renderer */
|
|
94
|
+
renderer?: CellRenderer;
|
|
95
|
+
/** Column header label */
|
|
96
|
+
label?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Example
|
|
100
|
+
const columns = [
|
|
101
|
+
{ field: "title", width: 200, label: "Title" },
|
|
102
|
+
{ field: "status", renderer: StatusBadgeRenderer, width: 100 },
|
|
103
|
+
];
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Relation Fields
|
|
107
|
+
|
|
108
|
+
Use dot notation to directly specify fields from relations.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const columns = [
|
|
112
|
+
"title",
|
|
113
|
+
"assignee.name", // name field from assignee relation
|
|
114
|
+
"assignee.email", // email field from assignee relation
|
|
115
|
+
"project.name", // name field from project relation
|
|
116
|
+
];
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This displays the following columns in the table:
|
|
120
|
+
- title
|
|
121
|
+
- assignee.name (label is "assignee.name" or can be overridden with labels)
|
|
122
|
+
- assignee.email
|
|
123
|
+
- project.name
|
|
124
|
+
|
|
125
|
+
## Options Reference
|
|
126
|
+
|
|
127
|
+
### width
|
|
128
|
+
|
|
129
|
+
Specifies the column width. Can be a number (pixels) or string (CSS width value).
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
{ width: 200 } // 200px
|
|
133
|
+
{ width: "20%" } // 20%
|
|
134
|
+
{ width: "auto" } // auto
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### minWidth / maxWidth
|
|
138
|
+
|
|
139
|
+
Specifies the minimum and maximum width of the column.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
{ minWidth: 100, maxWidth: 400 }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### renderer
|
|
146
|
+
|
|
147
|
+
Specifies a custom cell renderer.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const StatusRenderer: CellRenderer = ({ value, row }) => (
|
|
151
|
+
<Badge variant={value === "active" ? "success" : "secondary"}>
|
|
152
|
+
{value}
|
|
153
|
+
</Badge>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const columns = [
|
|
157
|
+
["status", StatusRenderer],
|
|
158
|
+
// or
|
|
159
|
+
{ field: "status", renderer: StatusRenderer },
|
|
160
|
+
];
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### label
|
|
164
|
+
|
|
165
|
+
Overrides the column header label.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const columns = [
|
|
169
|
+
["assignee.name", { label: "Assignee" }],
|
|
170
|
+
{ field: "createdAt", label: "Created At" },
|
|
171
|
+
];
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Type Definitions
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
/** Field path (can specify relation fields using dot notation) */
|
|
178
|
+
type FieldPath = string;
|
|
179
|
+
|
|
180
|
+
/** Column options */
|
|
181
|
+
interface ColumnOptions {
|
|
182
|
+
width?: number | string;
|
|
183
|
+
minWidth?: number | string;
|
|
184
|
+
maxWidth?: number | string;
|
|
185
|
+
renderer?: CellRenderer;
|
|
186
|
+
label?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Column definition (Level 3) */
|
|
190
|
+
interface ColumnDefinition extends ColumnOptions {
|
|
191
|
+
field: FieldPath;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Column definition (all levels) */
|
|
195
|
+
type ColumnDef =
|
|
196
|
+
| FieldPath // Level 1
|
|
197
|
+
| [FieldPath, CellRenderer] // Level 2a
|
|
198
|
+
| [FieldPath, ColumnOptions] // Level 2b
|
|
199
|
+
| ColumnDefinition; // Level 3
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Normalized Form
|
|
203
|
+
|
|
204
|
+
Internally, all ColumnDef values are normalized to the following form:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface NormalizedColumnDefinition {
|
|
208
|
+
/** Original field path */
|
|
209
|
+
field: FieldPath;
|
|
210
|
+
/** Column width */
|
|
211
|
+
width?: number | string;
|
|
212
|
+
/** Minimum width */
|
|
213
|
+
minWidth?: number | string;
|
|
214
|
+
/** Maximum width */
|
|
215
|
+
maxWidth?: number | string;
|
|
216
|
+
/** Custom renderer */
|
|
217
|
+
renderer?: CellRenderer;
|
|
218
|
+
/** Column label */
|
|
219
|
+
label?: string;
|
|
220
|
+
/** Whether this is a relation field */
|
|
221
|
+
isRelationField: boolean;
|
|
222
|
+
/** Base field name (before the dot) */
|
|
223
|
+
baseField: string;
|
|
224
|
+
/** Nested field name (after the dot, if any) */
|
|
225
|
+
nestedField?: string;
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Example: Complete Configuration
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { createDataViewer, builtInRenderers } from "@tailor/data-viewer";
|
|
233
|
+
|
|
234
|
+
const { TableDataProvider, DataTable, ColumnSelector } = createDataViewer({
|
|
235
|
+
metadata,
|
|
236
|
+
fetcher,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
function TaskList() {
|
|
240
|
+
return (
|
|
241
|
+
<TableDataProvider
|
|
242
|
+
tableName="task"
|
|
243
|
+
columns={[
|
|
244
|
+
// Simple field
|
|
245
|
+
"title",
|
|
246
|
+
|
|
247
|
+
// Width specification
|
|
248
|
+
["status", { width: 120 }],
|
|
249
|
+
|
|
250
|
+
// Custom renderer
|
|
251
|
+
["priority", builtInRenderers.statusBadge({
|
|
252
|
+
mapping: {
|
|
253
|
+
high: { label: "High", color: "red" },
|
|
254
|
+
medium: { label: "Medium", color: "yellow" },
|
|
255
|
+
low: { label: "Low", color: "gray" },
|
|
256
|
+
},
|
|
257
|
+
})],
|
|
258
|
+
|
|
259
|
+
// Relation field + label
|
|
260
|
+
["assignee.name", { label: "Assignee", width: 150 }],
|
|
261
|
+
|
|
262
|
+
// Complete configuration
|
|
263
|
+
{
|
|
264
|
+
field: "description",
|
|
265
|
+
width: 300,
|
|
266
|
+
minWidth: 200,
|
|
267
|
+
maxWidth: 500,
|
|
268
|
+
label: "Description",
|
|
269
|
+
},
|
|
270
|
+
]}
|
|
271
|
+
>
|
|
272
|
+
<DataTable />
|
|
273
|
+
</TableDataProvider>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
@@ -53,8 +53,8 @@ function MyDataViewer() {
|
|
|
53
53
|
return (
|
|
54
54
|
<DataViewer.TableDataProvider
|
|
55
55
|
tableName="User"
|
|
56
|
+
columns={["id", "name", "email"]}
|
|
56
57
|
initialData={{
|
|
57
|
-
selectedFields: ["id", "name", "email"],
|
|
58
58
|
sort: { field: "name", direction: "Asc" },
|
|
59
59
|
}}
|
|
60
60
|
>
|
|
@@ -112,15 +112,14 @@ interface TableDataProviderProps {
|
|
|
112
112
|
children: ReactNode;
|
|
113
113
|
/** Table name to display (type-safe when metadata is `as const`) */
|
|
114
114
|
tableName: string;
|
|
115
|
-
/**
|
|
115
|
+
/** Column definitions for the table (see columns.md for details) */
|
|
116
|
+
columns?: ColumnDef[];
|
|
117
|
+
/** Initial data for filters and sort */
|
|
116
118
|
initialData?: DataViewerInitialData;
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
interface DataViewerInitialData {
|
|
120
122
|
filters?: SearchFilters;
|
|
121
|
-
selectedFields?: string[];
|
|
122
|
-
selectedRelations?: string[];
|
|
123
|
-
expandedRelationFields?: ExpandedRelationFields;
|
|
124
123
|
sort?: { field: string; direction: "Asc" | "Desc" } | { field: string; direction: "Asc" | "Desc" }[];
|
|
125
124
|
}
|
|
126
125
|
```
|
|
@@ -130,11 +129,16 @@ interface DataViewerInitialData {
|
|
|
130
129
|
```tsx
|
|
131
130
|
<DataViewer.TableDataProvider
|
|
132
131
|
tableName="Task"
|
|
132
|
+
columns={[
|
|
133
|
+
"id",
|
|
134
|
+
"title",
|
|
135
|
+
["status", { width: 120 }],
|
|
136
|
+
"assignee.name",
|
|
137
|
+
"assignee.email",
|
|
138
|
+
"createdAt",
|
|
139
|
+
]}
|
|
133
140
|
initialData={{
|
|
134
141
|
filters: [{ field: "status", fieldType: "enum", operator: "eq", value: "active" }],
|
|
135
|
-
selectedFields: ["id", "title", "status", "createdAt"],
|
|
136
|
-
selectedRelations: ["assignee"],
|
|
137
|
-
expandedRelationFields: { assignee: ["name", "email"] },
|
|
138
142
|
sort: { field: "createdAt", direction: "Desc" },
|
|
139
143
|
}}
|
|
140
144
|
>
|
package/docs/custom-renderers.md
CHANGED
|
@@ -51,6 +51,83 @@ The `byFieldName` object uses a `tableName:fieldName` key format:
|
|
|
51
51
|
5. `byFieldType["string"]` — type-based fallback
|
|
52
52
|
6. Default formatting (lowest)
|
|
53
53
|
|
|
54
|
+
## Renderers vs Column API Renderer Option
|
|
55
|
+
|
|
56
|
+
There are two ways to define cell renderers: centralized management via `renderers` in `createDataViewer`, or ad-hoc specification using the `renderer` option in the [Column API](./columns.md).
|
|
57
|
+
|
|
58
|
+
### Ad-hoc Renderer Specification with Column API
|
|
59
|
+
|
|
60
|
+
The Column API allows you to specify renderers on-the-fly using the `renderer` option in each column definition:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<TableDataProvider
|
|
64
|
+
tableName="task"
|
|
65
|
+
columns={[
|
|
66
|
+
["status", StatusBadgeRenderer],
|
|
67
|
+
{ field: "priority", renderer: PriorityRenderer },
|
|
68
|
+
]}
|
|
69
|
+
>
|
|
70
|
+
...
|
|
71
|
+
</TableDataProvider>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Benefits of Centralized Management with renderers
|
|
75
|
+
|
|
76
|
+
Using `renderers` in `createDataViewer` for centralized renderer management provides the following benefits:
|
|
77
|
+
|
|
78
|
+
1. **Eliminate Duplication**: When the same field is rendered across multiple views, you don't need to specify the renderer individually in each view.
|
|
79
|
+
|
|
80
|
+
2. **Consistency for Relation Fields**: When expanding relation fields like `assignee.email` across multiple views, defining renderers once in `renderers` applies to all views automatically.
|
|
81
|
+
|
|
82
|
+
3. **Improved Maintainability**: When a renderer change is needed, modifying it in one place reflects the change everywhere.
|
|
83
|
+
|
|
84
|
+
4. **Efficiency with Wildcards**: Wildcard patterns like `*:*Email` or `*:id` allow you to set common field renderers across all tables at once.
|
|
85
|
+
|
|
86
|
+
5. **Type-based Fallbacks**: The `byFieldType` option enables automatic rendering based on field types (e.g., all boolean fields rendered as icons).
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
// Example of centralized management with renderers
|
|
90
|
+
const DataViewer = createDataViewer({
|
|
91
|
+
metadata,
|
|
92
|
+
fetcher,
|
|
93
|
+
renderers: {
|
|
94
|
+
cell: {
|
|
95
|
+
byFieldName: {
|
|
96
|
+
// Define relation field renderers once
|
|
97
|
+
"*:*.email": EmailLink,
|
|
98
|
+
"*:id": TruncatedId,
|
|
99
|
+
|
|
100
|
+
// Table-specific renderers
|
|
101
|
+
"task:status": StatusBadge({
|
|
102
|
+
colorMap: { todo: "gray", "in-progress": "blue", done: "green" },
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
105
|
+
byFieldType: {
|
|
106
|
+
boolean: BooleanIcon,
|
|
107
|
+
array: ArrayBadges,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// No need to specify renderer in columns for each view
|
|
114
|
+
<TableDataProvider tableName="task" columns={["title", "status", "assignee.email"]}>
|
|
115
|
+
...
|
|
116
|
+
</TableDataProvider>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Guidelines for Choosing an Approach
|
|
120
|
+
|
|
121
|
+
| Case | Recommended Approach |
|
|
122
|
+
|------|----------------------|
|
|
123
|
+
| Same field rendered across multiple views | Centralized management with `renderers` |
|
|
124
|
+
| Different renderer needed only in a specific view | Column API `renderer` option |
|
|
125
|
+
| Expanding relation fields | Centralized management with `renderers` |
|
|
126
|
+
| Common fields across all tables | `renderers` wildcard (`*:fieldName`, `*:*suffix`) |
|
|
127
|
+
| Type-based default rendering | `renderers.cell.byFieldType` |
|
|
128
|
+
|
|
129
|
+
> **Note**: Renderers specified via the Column API `renderer` option take precedence over centralized `renderers`. Use this when you need a different renderer only in a specific view.
|
|
130
|
+
|
|
54
131
|
## Usage with createDataViewer (Recommended)
|
|
55
132
|
|
|
56
133
|
Set renderers at the `createDataViewer` level to apply them across all tables:
|
package/docs/labels.md
CHANGED
|
@@ -30,10 +30,87 @@ For field labels (`getLabel("orders:status")`):
|
|
|
30
30
|
2. `labels["*:status"]` — wildcard match
|
|
31
31
|
3. `"status"` — fallback to field name
|
|
32
32
|
|
|
33
|
+
For nested relation fields (`getLabel("task:assignee.name")`):
|
|
34
|
+
1. `labels["task:assignee.name"]` — exact match (highest)
|
|
35
|
+
2. `labels["*:assignee.name"]` — wildcard match for full path
|
|
36
|
+
3. `labels["assignee:name"]` — target table format fallback
|
|
37
|
+
4. `labels["*:name"]` — target field wildcard
|
|
38
|
+
5. `"name"` — fallback to target field name
|
|
39
|
+
|
|
33
40
|
For UI labels (`getLabel("$:refresh")`):
|
|
34
41
|
1. `labels["$:refresh"]` — custom override (highest)
|
|
35
42
|
2. `DEFAULT_UI_LABELS["$:refresh"]` — built-in default
|
|
36
43
|
|
|
44
|
+
## Labels vs Column API Label Option
|
|
45
|
+
|
|
46
|
+
There are two ways to define field labels: centralized management via `labels`, or ad-hoc specification using the `label` option in the [Column API](./columns.md).
|
|
47
|
+
|
|
48
|
+
### Ad-hoc Label Specification with Column API
|
|
49
|
+
|
|
50
|
+
The Column API allows you to specify labels on-the-fly using the `label` option in each column definition:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<TableDataProvider
|
|
54
|
+
tableName="task"
|
|
55
|
+
columns={[
|
|
56
|
+
["assignee.name", { label: "Assignee" }],
|
|
57
|
+
{ field: "createdAt", label: "Created Date" },
|
|
58
|
+
]}
|
|
59
|
+
>
|
|
60
|
+
...
|
|
61
|
+
</TableDataProvider>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Benefits of Centralized Management with labels
|
|
65
|
+
|
|
66
|
+
Using `labels` for centralized label management provides the following benefits:
|
|
67
|
+
|
|
68
|
+
1. **Eliminate Duplication**: When the same field is used across multiple views, you don't need to specify the label individually in each view.
|
|
69
|
+
|
|
70
|
+
2. **Consistency for Relation Fields**: When expanding relation fields like `assignee.name` across multiple views, defining them once in `labels` applies to all views automatically.
|
|
71
|
+
|
|
72
|
+
3. **Improved Maintainability**: When a label change is needed, modifying it in one place reflects the change everywhere.
|
|
73
|
+
|
|
74
|
+
4. **Efficiency with Wildcards**: Wildcard patterns like `*:createdAt` allow you to set common field labels across all tables at once.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
// Example of centralized management with labels
|
|
78
|
+
const DataViewer = createDataViewer({
|
|
79
|
+
metadata,
|
|
80
|
+
fetcher,
|
|
81
|
+
labels: {
|
|
82
|
+
// Define relation field labels using target table format
|
|
83
|
+
// These labels apply to all tables referencing the same relation
|
|
84
|
+
"assignee:name": "Assignee",
|
|
85
|
+
"assignee:email": "Assignee Email",
|
|
86
|
+
"project:name": "Project Name",
|
|
87
|
+
|
|
88
|
+
// Or use table-specific format for precise control
|
|
89
|
+
"task:assignee.name": "Task Assignee", // Takes priority over "assignee:name"
|
|
90
|
+
|
|
91
|
+
// Common fields across multiple tables
|
|
92
|
+
"*:createdAt": "Created",
|
|
93
|
+
"*:updatedAt": "Updated",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// No need to specify label in columns for each view
|
|
98
|
+
<TableDataProvider tableName="task" columns={["title", "assignee.name", "createdAt"]}>
|
|
99
|
+
...
|
|
100
|
+
</TableDataProvider>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Guidelines for Choosing an Approach
|
|
104
|
+
|
|
105
|
+
| Case | Recommended Approach |
|
|
106
|
+
|------|----------------------|
|
|
107
|
+
| Same field displayed across multiple views | Centralized management with `labels` |
|
|
108
|
+
| Different label needed only in a specific view | Column API `label` option |
|
|
109
|
+
| Expanding relation fields | Centralized management with `labels` |
|
|
110
|
+
| Common fields across all tables | `labels` wildcard (`*:fieldName`) |
|
|
111
|
+
|
|
112
|
+
> **Note**: Labels specified via the Column API `label` option take precedence over `labels`. Use this when you need to display a different label only in a specific view.
|
|
113
|
+
|
|
37
114
|
## Usage with createDataViewer
|
|
38
115
|
|
|
39
116
|
```tsx
|