@izumisy-tailor/tailor-data-viewer 0.1.20 → 0.1.22

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.
@@ -3,15 +3,18 @@ import { Alert, AlertDescription } from "./ui/alert";
3
3
  import type {
4
4
  TableMetadata,
5
5
  TableMetadataMap,
6
- FieldMetadata,
7
6
  } from "../generator/metadata-generator";
8
- import { formatFieldValue } from "../graphql/query-builder";
9
7
  import { TableSelector } from "./table-selector";
10
8
  import { DataTable } from "./data-table";
11
9
  import { DataTableToolbar } from "./data-table-toolbar";
12
10
  import { Pagination } from "./pagination";
13
- import { useColumnState } from "./hooks/use-column-state";
14
- import { useTableData } from "./hooks/use-table-data";
11
+ import { ColumnSelector } from "./column-selector";
12
+ import { SearchFilterForm } from "./search-filter";
13
+ import { ViewSave } from "./view-save-load";
14
+ import { CsvButton } from "./csv-button";
15
+ import { RefreshButton } from "./refresh-button";
16
+ import { DataViewerProvider } from "./contexts";
17
+ import { TableDataProvider, useTableDataContext } from "./contexts";
15
18
  import type { InitialQuery } from "./data-viewer";
16
19
  import type { SearchFilters } from "./types";
17
20
  import type { SavedView } from "./saved-view-context";
@@ -22,8 +25,8 @@ interface DataViewTabContentProps {
22
25
  initialTable?: TableMetadata;
23
26
  onTableConfirm: (table: TableMetadata) => void;
24
27
  isTableLocked: boolean;
25
- /** All table metadata for relation lookup */
26
- tableMetadataMap: TableMetadataMap;
28
+ /** All table metadata (generated from TailorDB schema) */
29
+ metadata: TableMetadataMap;
27
30
  /** Initial query condition for filtering */
28
31
  initialQuery?: InitialQuery;
29
32
  /** Initial saved view to load */
@@ -51,7 +54,7 @@ export function DataViewTabContent({
51
54
  initialTable,
52
55
  onTableConfirm,
53
56
  isTableLocked,
54
- tableMetadataMap,
57
+ metadata,
55
58
  initialQuery,
56
59
  initialView,
57
60
  onOpenAsSheet,
@@ -86,94 +89,11 @@ export function DataViewTabContent({
86
89
  }, [initialQuery, initialTable, initialView]);
87
90
 
88
91
  // Search filters (includes initialQuery as a regular filter that can be edited/removed)
89
- const [searchFilters, setSearchFilters] =
90
- useState<SearchFilters>(getInitialFilters);
91
-
92
- // Memoize fields to avoid creating new array reference on every render when selectedTable is null
93
- const fields = useMemo(
94
- () => selectedTable?.fields ?? [],
95
- [selectedTable?.fields],
96
- );
97
-
98
- // Column state (includes relation selection)
99
- // If initialView is provided, use its column selections as defaults
100
- const columnState = useColumnState(
101
- fields,
102
- selectedTable?.relations,
103
- initialView?.selectedFields,
104
- initialView?.selectedRelations,
105
- initialView?.expandedRelationFields,
106
- );
107
-
108
- // Table data fetching (pass selectedRelations for FK field inclusion and search filters)
109
- const tableData = useTableData(
110
- appUri,
111
- selectedTable,
112
- columnState.selectedFields,
113
- columnState.selectedRelations,
114
- searchFilters,
115
- tableMetadataMap,
116
- columnState.expandedRelationFields,
92
+ const initialFilters = useMemo(
93
+ () => getInitialFilters(),
94
+ [getInitialFilters],
117
95
  );
118
96
 
119
- // Handle filter changes - reset pagination when filters change
120
- const handleFiltersChange = useCallback(
121
- (newFilters: SearchFilters) => {
122
- setSearchFilters(newFilters);
123
- tableData.resetPagination();
124
- },
125
- [tableData],
126
- );
127
-
128
- // CSV Download handler
129
- const handleDownloadCsv = useCallback(() => {
130
- if (!selectedTable || tableData.data.length === 0) return;
131
-
132
- const selectedFieldsMetadata = selectedTable.fields.filter(
133
- (f: FieldMetadata) => columnState.selectedFields.includes(f.name),
134
- );
135
-
136
- // Create CSV header
137
- const headers = selectedFieldsMetadata.map((f: FieldMetadata) => f.name);
138
-
139
- // Create CSV rows
140
- const rows = tableData.data.map((row: Record<string, unknown>) =>
141
- selectedFieldsMetadata.map((field: FieldMetadata) => {
142
- const value = row[field.name];
143
- const formattedValue = formatFieldValue(value, field);
144
- // Escape double quotes and wrap in quotes if contains comma, newline, or quotes
145
- const stringValue = String(formattedValue ?? "");
146
- if (
147
- stringValue.includes(",") ||
148
- stringValue.includes("\n") ||
149
- stringValue.includes('"')
150
- ) {
151
- return `"${stringValue.replace(/"/g, '""')}"`;
152
- }
153
- return stringValue;
154
- }),
155
- );
156
-
157
- // Combine headers and rows
158
- const csvContent = [
159
- headers.join(","),
160
- ...rows.map((r: string[]) => r.join(",")),
161
- ].join("\n");
162
-
163
- // Create and download file
164
- const blob = new Blob(["\uFEFF" + csvContent], {
165
- type: "text/csv;charset=utf-8;",
166
- });
167
- const url = URL.createObjectURL(blob);
168
- const link = document.createElement("a");
169
- link.href = url;
170
- link.download = `${selectedTable.name}_${new Date().toISOString().slice(0, 10)}.csv`;
171
- document.body.appendChild(link);
172
- link.click();
173
- document.body.removeChild(link);
174
- URL.revokeObjectURL(url);
175
- }, [selectedTable, tableData.data, columnState.selectedFields]);
176
-
177
97
  const handleTableSelect = useCallback(
178
98
  (table: TableMetadata) => {
179
99
  setSelectedTable(table);
@@ -204,6 +124,52 @@ export function DataViewTabContent({
204
124
  );
205
125
  }
206
126
 
127
+ return (
128
+ <DataViewerProvider
129
+ appUri={appUri}
130
+ tableName={selectedTable.name}
131
+ metadata={metadata}
132
+ initialData={{
133
+ filters: initialFilters,
134
+ selectedFields: initialView?.selectedFields,
135
+ selectedRelations: initialView?.selectedRelations,
136
+ expandedRelationFields: initialView?.expandedRelationFields,
137
+ }}
138
+ >
139
+ <TableDataProvider>
140
+ <DataViewTabContentInner
141
+ selectedTable={selectedTable}
142
+ onOpenAsSheet={onOpenAsSheet}
143
+ onOpenSingleRecordAsSheet={onOpenSingleRecordAsSheet}
144
+ />
145
+ </TableDataProvider>
146
+ </DataViewerProvider>
147
+ );
148
+ }
149
+
150
+ interface DataViewTabContentInnerProps {
151
+ selectedTable: TableMetadata;
152
+ onOpenAsSheet?: (
153
+ targetTableName: string,
154
+ filterField: string,
155
+ filterValue: string,
156
+ ) => void;
157
+ onOpenSingleRecordAsSheet?: (
158
+ targetTableName: string,
159
+ recordId: string,
160
+ ) => void;
161
+ }
162
+
163
+ /**
164
+ * Inner component that uses context
165
+ */
166
+ function DataViewTabContentInner({
167
+ selectedTable,
168
+ onOpenAsSheet,
169
+ onOpenSingleRecordAsSheet,
170
+ }: DataViewTabContentInnerProps) {
171
+ const tableData = useTableDataContext();
172
+
207
173
  return (
208
174
  <div className="space-y-4">
209
175
  {/* Table name and description */}
@@ -217,40 +183,14 @@ export function DataViewTabContent({
217
183
  </div>
218
184
 
219
185
  {/* Operations row: column selector, search filter, etc. */}
220
- <DataTableToolbar
221
- columnSelector={{
222
- fields: selectedTable.fields,
223
- selectedFields: columnState.selectedFields,
224
- onToggle: columnState.toggleField,
225
- onSelectAll: columnState.selectAll,
226
- onDeselectAll: columnState.deselectAll,
227
- relations: selectedTable.relations,
228
- selectedRelations: columnState.selectedRelations,
229
- onToggleRelation: columnState.toggleRelation,
230
- tableMetadataMap: tableMetadataMap,
231
- expandedRelationFields: columnState.expandedRelationFields,
232
- onToggleExpandedRelationField:
233
- columnState.toggleExpandedRelationField,
234
- isExpandedRelationFieldSelected:
235
- columnState.isExpandedRelationFieldSelected,
236
- }}
237
- searchFilter={{
238
- fields: selectedTable.fields,
239
- filters: searchFilters,
240
- onFiltersChange: handleFiltersChange,
241
- }}
242
- viewSave={{
243
- tableName: selectedTable.name,
244
- filters: searchFilters,
245
- selectedFields: columnState.selectedFields,
246
- selectedRelations: columnState.selectedRelations,
247
- expandedRelationFields: columnState.expandedRelationFields,
248
- }}
249
- onDownloadCsv={handleDownloadCsv}
250
- onRefresh={() => tableData.refetch()}
251
- loading={tableData.loading}
252
- csvDisabled={tableData.data.length === 0}
253
- />
186
+ <DataTableToolbar>
187
+ <ColumnSelector />
188
+ <SearchFilterForm />
189
+ <ViewSave />
190
+ <div className="flex-1" />
191
+ <CsvButton />
192
+ <RefreshButton />
193
+ </DataTableToolbar>
254
194
 
255
195
  {/* Error Display */}
256
196
  {tableData.error && (
@@ -263,19 +203,8 @@ export function DataViewTabContent({
263
203
 
264
204
  {/* Data Table */}
265
205
  <DataTable
266
- data={tableData.data}
267
- fields={selectedTable.fields}
268
- selectedFields={columnState.selectedFields}
269
- sortState={tableData.sortState}
270
- onSort={tableData.setSort}
271
- loading={tableData.loading}
272
- tableMetadata={selectedTable}
273
- tableMetadataMap={tableMetadataMap}
274
- appUri={appUri}
275
- selectedRelations={columnState.selectedRelations}
276
206
  onOpenAsSheet={onOpenAsSheet}
277
207
  onOpenSingleRecordAsSheet={onOpenSingleRecordAsSheet}
278
- expandedRelationFields={columnState.expandedRelationFields}
279
208
  />
280
209
 
281
210
  {/* Pagination */}
@@ -11,7 +11,7 @@ import { SingleRecordTabContent } from "./single-record-tab-content";
11
11
  import { useSavedViews, type SavedView } from "./saved-view-context";
12
12
 
13
13
  interface DataViewerProps {
14
- tableMetadata: TableMetadataMap;
14
+ metadata: TableMetadataMap;
15
15
  appUri: string;
16
16
  /** Initial view ID to load on mount */
17
17
  initialViewId?: string;
@@ -52,11 +52,11 @@ function generateTabId(): string {
52
52
  * Similar to Excel sheets - each tab has its own table view
53
53
  */
54
54
  export function DataViewer({
55
- tableMetadata,
55
+ metadata,
56
56
  appUri,
57
57
  initialViewId,
58
58
  }: DataViewerProps) {
59
- const allTables = Object.values(tableMetadata);
59
+ const allTables = Object.values(metadata);
60
60
  const { getViewById, isLoading } = useSavedViews();
61
61
 
62
62
  // Track if we've already initialized from the saved view
@@ -84,7 +84,7 @@ export function DataViewer({
84
84
 
85
85
  const view = getViewById(initialViewId);
86
86
  if (view) {
87
- const table = tableMetadata[view.tableName];
87
+ const table = metadata[view.tableName];
88
88
  if (table) {
89
89
  initializedFromViewRef.current = true;
90
90
  const newTab: Tab = {
@@ -98,7 +98,7 @@ export function DataViewer({
98
98
  setActiveTabId(newTab.id);
99
99
  }
100
100
  }
101
- }, [initialViewId, isLoading, getViewById, tableMetadata]);
101
+ }, [initialViewId, isLoading, getViewById, metadata]);
102
102
 
103
103
  const handleAddTab = useCallback(() => {
104
104
  const newTab: Tab = {
@@ -154,7 +154,7 @@ export function DataViewer({
154
154
  */
155
155
  const handleOpenAsSheet = useCallback(
156
156
  (targetTableName: string, filterField: string, filterValue: string) => {
157
- const targetTable = tableMetadata[targetTableName];
157
+ const targetTable = metadata[targetTableName];
158
158
  if (!targetTable) return;
159
159
 
160
160
  const newTab: Tab = {
@@ -170,7 +170,7 @@ export function DataViewer({
170
170
  setTabs((prev) => [...prev, newTab]);
171
171
  setActiveTabId(newTab.id);
172
172
  },
173
- [tableMetadata],
173
+ [metadata],
174
174
  );
175
175
 
176
176
  /**
@@ -178,7 +178,7 @@ export function DataViewer({
178
178
  */
179
179
  const handleOpenSingleRecordAsSheet = useCallback(
180
180
  (targetTableName: string, recordId: string) => {
181
- const targetTable = tableMetadata[targetTableName];
181
+ const targetTable = metadata[targetTableName];
182
182
  if (!targetTable) return;
183
183
 
184
184
  const newTab: Tab = {
@@ -193,7 +193,7 @@ export function DataViewer({
193
193
  setTabs((prev) => [...prev, newTab]);
194
194
  setActiveTabId(newTab.id);
195
195
  },
196
- [tableMetadata],
196
+ [metadata],
197
197
  );
198
198
 
199
199
  return (
@@ -250,7 +250,7 @@ export function DataViewer({
250
250
  {tab.singleRecordMode && tab.table ? (
251
251
  <SingleRecordTabContent
252
252
  tableMetadata={tab.table}
253
- tableMetadataMap={tableMetadata}
253
+ metadata={metadata}
254
254
  appUri={appUri}
255
255
  recordId={tab.singleRecordMode.recordId}
256
256
  onOpenAsSheet={handleOpenAsSheet}
@@ -263,7 +263,7 @@ export function DataViewer({
263
263
  initialTable={tab.table ?? undefined}
264
264
  onTableConfirm={(table) => handleTableConfirm(tab.id, table)}
265
265
  isTableLocked={tab.isLocked}
266
- tableMetadataMap={tableMetadata}
266
+ metadata={metadata}
267
267
  initialQuery={tab.initialQuery}
268
268
  initialView={tab.initialView}
269
269
  onOpenAsSheet={handleOpenAsSheet}
@@ -51,8 +51,8 @@ export interface ColumnState {
51
51
  * Manage column visibility state (fields and relations)
52
52
  */
53
53
  export function useColumnState(
54
- fields: FieldMetadata[],
55
- relations?: RelationMetadata[],
54
+ fields: readonly FieldMetadata[],
55
+ relations?: readonly RelationMetadata[],
56
56
  defaultFields?: string[],
57
57
  defaultRelations?: string[],
58
58
  defaultExpandedRelationFields?: ExpandedRelationFields,
@@ -1,6 +1,38 @@
1
+ // Main DataViewer component
1
2
  export { DataViewer } from "./data-viewer";
2
3
  export type { InitialQuery } from "./data-viewer";
4
+
5
+ // Toolbar component (children-based)
3
6
  export { DataTableToolbar } from "./data-table-toolbar";
4
- export type { ActivePanel } from "./data-table-toolbar";
7
+
8
+ // Saved view context
5
9
  export { useSavedViews, SavedViewProvider } from "./saved-view-context";
6
10
  export type { SavedView, SaveViewInput } from "./saved-view-context";
11
+
12
+ // Context providers and hooks
13
+ export { DataViewerProvider, useDataViewer } from "./contexts";
14
+ export type {
15
+ DataViewerContextValue,
16
+ DataViewerProviderProps,
17
+ DataViewerInitialData,
18
+ } from "./contexts";
19
+ export { TableDataProvider, useTableDataContext } from "./contexts";
20
+ export type { TableDataContextValue, TableDataProviderProps } from "./contexts";
21
+ export { ToolbarProvider, useToolbar } from "./contexts";
22
+ export type {
23
+ ToolbarContextValue,
24
+ ToolbarProviderProps,
25
+ ToolbarActivePanel,
26
+ } from "./contexts";
27
+
28
+ // Individual components (context-only, must be used within DataViewerProvider)
29
+ export { ColumnSelector } from "./column-selector";
30
+ export { SearchFilterForm } from "./search-filter";
31
+ export { ViewSave } from "./view-save-load";
32
+ export { DataTable } from "./data-table";
33
+ export type { DataTableProps } from "./data-table";
34
+ export { CsvButton } from "./csv-button";
35
+ export { RefreshButton } from "./refresh-button";
36
+
37
+ // Types
38
+ export type { SearchFilter, SearchFilters } from "./types";
@@ -0,0 +1,20 @@
1
+ import { RefreshCw } from "lucide-react";
2
+ import { Button } from "./ui/button";
3
+ import { useDataViewer } from "./contexts";
4
+ import { useTableDataContext } from "./contexts";
5
+
6
+ /**
7
+ * Refresh button
8
+ * Must be used within DataViewer.Root and TableDataProvider context.
9
+ */
10
+ export function RefreshButton() {
11
+ const { refetch } = useDataViewer();
12
+ const { loading } = useTableDataContext();
13
+
14
+ return (
15
+ <Button variant="outline" size="sm" onClick={refetch} disabled={loading}>
16
+ <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
17
+ 更新
18
+ </Button>
19
+ );
20
+ }
@@ -18,17 +18,9 @@ import {
18
18
  import { Badge } from "./ui/badge";
19
19
  import { Label } from "./ui/label";
20
20
  import type { FieldMetadata } from "../generator/metadata-generator";
21
- import type { SearchFilter, SearchFilters } from "./types";
22
-
23
- interface SearchFilterProps {
24
- fields: FieldMetadata[];
25
- filters: SearchFilters;
26
- onFiltersChange: (filters: SearchFilters) => void;
27
- /** Controlled open state */
28
- open?: boolean;
29
- /** Callback when open state changes */
30
- onOpenChange?: (open: boolean) => void;
31
- }
21
+ import type { SearchFilter } from "./types";
22
+ import { useDataViewer } from "./contexts";
23
+ import { useToolbar } from "./contexts";
32
24
 
33
25
  // Filterable field types
34
26
  const FILTERABLE_TYPES = [
@@ -49,14 +41,17 @@ function isFilterableField(field: FieldMetadata): boolean {
49
41
  /**
50
42
  * Search filter form component
51
43
  * Allows adding multiple AND filters for string/number/boolean/enum fields
44
+ * Must be used within DataViewer.Root and DataViewer.Toolbar context
52
45
  */
53
- export function SearchFilterForm({
54
- fields,
55
- filters,
56
- onFiltersChange,
57
- open,
58
- onOpenChange,
59
- }: SearchFilterProps) {
46
+ export function SearchFilterForm() {
47
+ const { tableMetadata, filters, setFilters } = useDataViewer();
48
+ const { activePanel, setActivePanel } = useToolbar();
49
+
50
+ const fields = tableMetadata?.fields ?? [];
51
+ const open = activePanel === "search";
52
+ const onOpenChange = (isOpen: boolean) =>
53
+ setActivePanel(isOpen ? "search" : null);
54
+
60
55
  const [selectedField, setSelectedField] = useState<string>("");
61
56
  const [inputValue, setInputValue] = useState<string>("");
62
57
  const [booleanValue, setBooleanValue] = useState<boolean>(false);
@@ -85,7 +80,7 @@ export function SearchFilterForm({
85
80
  enumValues: selectedFieldMetadata.enumValues,
86
81
  };
87
82
 
88
- onFiltersChange([...filters, newFilter]);
83
+ setFilters([...filters, newFilter]);
89
84
  setSelectedField("");
90
85
  setInputValue("");
91
86
  setBooleanValue(false);
@@ -95,19 +90,19 @@ export function SearchFilterForm({
95
90
  inputValue,
96
91
  booleanValue,
97
92
  filters,
98
- onFiltersChange,
93
+ setFilters,
99
94
  ]);
100
95
 
101
96
  const handleRemoveFilter = useCallback(
102
97
  (fieldName: string) => {
103
- onFiltersChange(filters.filter((f) => f.field !== fieldName));
98
+ setFilters(filters.filter((f) => f.field !== fieldName));
104
99
  },
105
- [filters, onFiltersChange],
100
+ [filters, setFilters],
106
101
  );
107
102
 
108
103
  const handleClearAll = useCallback(() => {
109
- onFiltersChange([]);
110
- }, [onFiltersChange]);
104
+ setFilters([]);
105
+ }, [setFilters]);
111
106
 
112
107
  const handleKeyDown = useCallback(
113
108
  (e: React.KeyboardEvent) => {
@@ -127,7 +127,7 @@ describe("SingleRecordTabContent", () => {
127
127
  render(
128
128
  <SingleRecordTabContent
129
129
  tableMetadata={mockTaskTable}
130
- tableMetadataMap={mockTableMetadataMap}
130
+ metadata={mockTableMetadataMap}
131
131
  appUri="https://test.example.com"
132
132
  recordId="task-123-456-789"
133
133
  fetcher={mockFetcher}
@@ -141,7 +141,7 @@ describe("SingleRecordTabContent", () => {
141
141
  render(
142
142
  <SingleRecordTabContent
143
143
  tableMetadata={mockTaskTable}
144
- tableMetadataMap={mockTableMetadataMap}
144
+ metadata={mockTableMetadataMap}
145
145
  appUri="https://test.example.com"
146
146
  recordId="task-123-456-789"
147
147
  fetcher={mockFetcher}
@@ -160,7 +160,7 @@ describe("SingleRecordTabContent", () => {
160
160
  render(
161
161
  <SingleRecordTabContent
162
162
  tableMetadata={mockTaskTable}
163
- tableMetadataMap={mockTableMetadataMap}
163
+ metadata={mockTableMetadataMap}
164
164
  appUri="https://test.example.com"
165
165
  recordId="task-123-456-789"
166
166
  fetcher={mockFetcher}
@@ -193,7 +193,7 @@ describe("SingleRecordTabContent", () => {
193
193
  render(
194
194
  <SingleRecordTabContent
195
195
  tableMetadata={mockTaskTable}
196
- tableMetadataMap={mockTableMetadataMap}
196
+ metadata={mockTableMetadataMap}
197
197
  appUri="https://test.example.com"
198
198
  recordId="task-123-456-789"
199
199
  fetcher={mockFetcher}
@@ -213,7 +213,7 @@ describe("SingleRecordTabContent", () => {
213
213
  render(
214
214
  <SingleRecordTabContent
215
215
  tableMetadata={mockTaskTable}
216
- tableMetadataMap={mockTableMetadataMap}
216
+ metadata={mockTableMetadataMap}
217
217
  appUri="https://test.example.com"
218
218
  recordId="nonexistent-id"
219
219
  fetcher={mockFetcher}
@@ -233,7 +233,7 @@ describe("SingleRecordTabContent", () => {
233
233
  render(
234
234
  <SingleRecordTabContent
235
235
  tableMetadata={mockTaskTable}
236
- tableMetadataMap={mockTableMetadataMap}
236
+ metadata={mockTableMetadataMap}
237
237
  appUri="https://test.example.com"
238
238
  recordId="task-123-456-789"
239
239
  fetcher={mockFetcher}
@@ -257,7 +257,7 @@ describe("SingleRecordTabContent", () => {
257
257
  render(
258
258
  <SingleRecordTabContent
259
259
  tableMetadata={mockTaskTable}
260
- tableMetadataMap={mockTableMetadataMap}
260
+ metadata={mockTableMetadataMap}
261
261
  appUri="https://test.example.com"
262
262
  recordId="task-123-456-789"
263
263
  fetcher={mockFetcher}
@@ -277,7 +277,7 @@ describe("SingleRecordTabContent", () => {
277
277
  render(
278
278
  <SingleRecordTabContent
279
279
  tableMetadata={mockTaskTable}
280
- tableMetadataMap={mockTableMetadataMap}
280
+ metadata={mockTableMetadataMap}
281
281
  appUri="https://test.example.com"
282
282
  recordId="task-123-456-789"
283
283
  fetcher={mockFetcher}
@@ -298,7 +298,7 @@ describe("SingleRecordTabContent", () => {
298
298
  render(
299
299
  <SingleRecordTabContent
300
300
  tableMetadata={mockTaskTable}
301
- tableMetadataMap={mockTableMetadataMap}
301
+ metadata={mockTableMetadataMap}
302
302
  appUri="https://test.example.com"
303
303
  recordId="task-123-456-789"
304
304
  fetcher={mockFetcher}
@@ -326,7 +326,7 @@ describe("SingleRecordTabContent", () => {
326
326
  render(
327
327
  <SingleRecordTabContent
328
328
  tableMetadata={mockTaskTable}
329
- tableMetadataMap={mockTableMetadataMap}
329
+ metadata={mockTableMetadataMap}
330
330
  appUri="https://test.example.com"
331
331
  recordId="task-123-456-789"
332
332
  fetcher={mockFetcher}