Allow a 'data' prop to be passed inplace to the useTable hook for mode 'complete'
Signed-off-by: Gustaf Räntilä <g.rantila@gmail.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Allow data to be passed inplace to the `useTable` hook using the property `data` instead of `getData()` for mode `"complete"`.
|
||||
|
||||
This simplifies usage as data changes, rather than having to perform a `useEffect` when data changes, and then reloading the data. It also happens immediately, so stale data won't remain until a rerender (with an internal async state change), so less flickering.
|
||||
|
||||
Affected components: Table
|
||||
@@ -36,7 +36,13 @@ export const useTableOptionsPropDefs: Record<string, PropDef> = {
|
||||
type: 'enum',
|
||||
values: ['function'],
|
||||
description:
|
||||
'Function that returns or fetches data (required). Signature varies by mode.',
|
||||
'Function that returns or fetches data (required for "offset" and "cursor" modes). For the "complete" mode, either this <b>or</b> `data` must be provided. Signature varies by mode.',
|
||||
},
|
||||
data: {
|
||||
type: 'enum',
|
||||
values: ['T[]'],
|
||||
description:
|
||||
'The data for the table. Only applicable for "complete" mode, and either this <b>or</b> `getData` must be provided.',
|
||||
},
|
||||
paginationOptions: {
|
||||
type: 'enum',
|
||||
|
||||
@@ -12,7 +12,7 @@ const columns: ColumnConfig<DataType>[] = [
|
||||
function MyTable() {
|
||||
const { tableProps } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
});
|
||||
|
||||
return <Table columnConfig={columns} {...tableProps} />;
|
||||
@@ -39,7 +39,7 @@ const columns: ColumnConfig<typeof data[0]>[] = [
|
||||
function MyTable() {
|
||||
const { tableProps } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
});
|
||||
|
||||
return <Table columnConfig={columns} {...tableProps} />;
|
||||
@@ -69,7 +69,7 @@ export const tableSortingSnippet = `const columns: ColumnConfig<Item>[] = [
|
||||
|
||||
const { tableProps } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
initialSort: { column: 'name', direction: 'ascending' },
|
||||
sortFn: (items, { column, direction }) => {
|
||||
return [...items].sort((a, b) => {
|
||||
@@ -85,7 +85,7 @@ return <Table columnConfig={columns} {...tableProps} />;`;
|
||||
|
||||
export const tablePaginationSnippet = `const { tableProps } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
paginationOptions: {
|
||||
pageSize: 10,
|
||||
pageSizeOptions: [10, 25, 50],
|
||||
@@ -94,7 +94,7 @@ export const tablePaginationSnippet = `const { tableProps } = useTable({
|
||||
|
||||
export const tableSearchSnippet = `const { tableProps, search } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
searchFn: (items, query) => {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return items.filter(item =>
|
||||
@@ -119,7 +119,7 @@ export const tableSelectionSnippet = `const [selected, setSelected] = useState<S
|
||||
|
||||
const { tableProps } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -162,7 +162,7 @@ export const tableRowActionsDisabledSnippet = `<Table
|
||||
|
||||
export const tableEmptyStateSnippet = `const { tableProps, search } = useTable({
|
||||
mode: 'complete',
|
||||
getData: () => data,
|
||||
data,
|
||||
searchFn: (items, query) => { /* ... */ },
|
||||
});
|
||||
|
||||
|
||||
+16
-12
@@ -2093,21 +2093,25 @@ export function useTable<T extends TableItem, TFilter = unknown>(
|
||||
): UseTableResult<T, TFilter>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface UseTableCompleteOptions<T extends TableItem, TFilter = unknown>
|
||||
extends QueryOptions<TFilter> {
|
||||
// (undocumented)
|
||||
filterFn?: (data: T[], filter: TFilter) => T[];
|
||||
// (undocumented)
|
||||
getData: () => T[] | Promise<T[]>;
|
||||
// (undocumented)
|
||||
export type UseTableCompleteOptions<
|
||||
T extends TableItem,
|
||||
TFilter = unknown,
|
||||
> = QueryOptions<TFilter> & {
|
||||
mode: 'complete';
|
||||
// (undocumented)
|
||||
paginationOptions?: PaginationOptions;
|
||||
// (undocumented)
|
||||
searchFn?: (data: T[], search: string) => T[];
|
||||
// (undocumented)
|
||||
sortFn?: (data: T[], sort: SortDescriptor) => T[];
|
||||
}
|
||||
filterFn?: (data: T[], filter: TFilter) => T[];
|
||||
searchFn?: (data: T[], search: string) => T[];
|
||||
} & (
|
||||
| {
|
||||
data: T[] | undefined;
|
||||
getData?: never;
|
||||
}
|
||||
| {
|
||||
data?: never;
|
||||
getData: () => T[] | Promise<T[]>;
|
||||
}
|
||||
);
|
||||
|
||||
// @public (undocumented)
|
||||
export interface UseTableCursorOptions<T extends TableItem, TFilter = unknown>
|
||||
|
||||
@@ -96,15 +96,25 @@ export interface CursorResponse<T> {
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface UseTableCompleteOptions<T extends TableItem, TFilter = unknown>
|
||||
extends QueryOptions<TFilter> {
|
||||
export type UseTableCompleteOptions<
|
||||
T extends TableItem,
|
||||
TFilter = unknown,
|
||||
> = QueryOptions<TFilter> & {
|
||||
mode: 'complete';
|
||||
getData: () => T[] | Promise<T[]>;
|
||||
paginationOptions?: PaginationOptions;
|
||||
sortFn?: (data: T[], sort: SortDescriptor) => T[];
|
||||
filterFn?: (data: T[], filter: TFilter) => T[];
|
||||
searchFn?: (data: T[], search: string) => T[];
|
||||
}
|
||||
} & (
|
||||
| {
|
||||
data: T[] | undefined;
|
||||
getData?: never;
|
||||
}
|
||||
| {
|
||||
data?: never;
|
||||
getData: () => T[] | Promise<T[]>;
|
||||
}
|
||||
);
|
||||
|
||||
/** @public */
|
||||
export interface UseTableOffsetOptions<T extends TableItem, TFilter = unknown>
|
||||
|
||||
@@ -30,7 +30,8 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
query: QueryState<TFilter>,
|
||||
): PaginationResult<T> & { reload: () => void } {
|
||||
const {
|
||||
getData: getDataProp,
|
||||
data,
|
||||
getData: getDataProp = () => [],
|
||||
paginationOptions = {},
|
||||
sortFn,
|
||||
filterFn,
|
||||
@@ -43,7 +44,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
const { sort, filter, search } = query;
|
||||
|
||||
const [items, setItems] = useState<T[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(data ? false : true);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [loadCount, setLoadCount] = useState(0);
|
||||
|
||||
@@ -52,6 +53,10 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
|
||||
// Load data on mount and when loadCount changes (reload trigger)
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
@@ -75,7 +80,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [getData, loadCount]);
|
||||
}, [data, getData, loadCount]);
|
||||
|
||||
// Reset offset when query changes (query object is memoized)
|
||||
const prevQueryRef = useRef(query);
|
||||
@@ -86,9 +91,11 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
const resolvedItems = useMemo(() => data ?? items, [data, items]);
|
||||
|
||||
// Process data client-side (filter, search, sort)
|
||||
const processedData = useMemo(() => {
|
||||
let result = [...items];
|
||||
let result = [...resolvedItems];
|
||||
if (filter !== undefined && filterFn) {
|
||||
result = filterFn(result, filter);
|
||||
}
|
||||
@@ -99,7 +106,7 @@ export function useCompletePagination<T extends TableItem, TFilter>(
|
||||
result = sortFn(result, sort);
|
||||
}
|
||||
return result;
|
||||
}, [items, sort, filter, search, filterFn, searchFn, sortFn]);
|
||||
}, [resolvedItems, sort, filter, search, filterFn, searchFn, sortFn]);
|
||||
|
||||
const totalCount = processedData.length;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user