From abe29fde0147fdb69dc68c5b9b49bb8453e1611f Mon Sep 17 00:00:00 2001 From: Jordan Koschei Date: Sun, 17 Nov 2024 21:14:11 -0500 Subject: [PATCH 1/4] chore: adding generics to everything --- .../src/DataTable/DataTable.tsx | 91 ++++++++++--------- .../src/DataTable/DataTableRowActions.tsx | 8 +- .../src/DataTable/DataTableSettings.tsx | 24 +++-- .../src/DataTable/reorderDataRowsLocally.tsx | 6 +- .../src/DataTable/useRowReordering.tsx | 43 ++++----- .../src/labs/DataFilters.tsx | 46 ++++++---- .../odyssey-react-mui/src/labs/DataTable.tsx | 72 ++++++++------- .../src/labs/DataView/BulkActionsMenu.tsx | 8 +- .../src/labs/DataView/CardLayoutContent.tsx | 45 +++++---- .../src/labs/DataView/DataCard.tsx | 18 ++-- .../src/labs/DataView/DataTable.tsx | 15 ++- .../src/labs/DataView/DataView.tsx | 26 ++++-- .../src/labs/DataView/DetailPanel.tsx | 8 +- .../src/labs/DataView/RowActions.tsx | 8 +- .../src/labs/DataView/TableLayoutContent.tsx | 52 ++++++----- .../src/labs/DataView/TableSettings.tsx | 18 ++-- .../src/labs/DataView/componentTypes.ts | 32 +++---- .../src/labs/DataView/dataTypes.ts | 38 ++++---- .../src/labs/DataView/fetchData.ts | 18 ++-- .../src/labs/DataView/useFilterConversion.ts | 18 ++-- .../odyssey-labs/DataView/dataFunctions.ts | 8 +- 21 files changed, 331 insertions(+), 271 deletions(-) diff --git a/packages/odyssey-react-mui/src/DataTable/DataTable.tsx b/packages/odyssey-react-mui/src/DataTable/DataTable.tsx index e60e041c7f..1e01ccfdfc 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTable.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTable.tsx @@ -33,6 +33,7 @@ import { MRT_Column, MRT_ColumnDef, MRT_TableInstance, + MRT_RowData, } from "material-react-table"; import { useTranslation } from "react-i18next"; import { @@ -64,30 +65,34 @@ import { EmptyState } from "../EmptyState"; import { Button } from "../Button"; import { Callout } from "../Callout"; -export type DataTableColumn = MRT_ColumnDef & { - /** - * @deprecated use hasTextWrapping instead of enableWrapping - */ - enableWrapping?: boolean; - hasTextWrapping?: boolean; -}; - -type DataTableColumnInstance = Omit< - MRT_Column, +export type DataTableColumn = + MRT_ColumnDef & { + /** + * @deprecated use hasTextWrapping instead of enableWrapping + */ + enableWrapping?: boolean; + hasTextWrapping?: boolean; + }; + +type DataTableColumnInstance = Omit< + MRT_Column, "columnDef" > & { - columnDef: DataTableColumn; + columnDef: DataTableColumn; }; -type DataTableCell = Omit, "column"> & { - column: DataTableColumnInstance; +type DataTableCell = Omit< + MRT_Cell, + "column" +> & { + column: DataTableColumnInstance; }; -export type DataTableGetDataType = { +export type DataTableGetDataType = { page?: number; resultsPerPage?: number; search?: string; - filters?: DataFilter[]; + filters?: DataFilter[]; sort?: MRT_SortingState; }; @@ -101,7 +106,7 @@ export type DataTableRenderDetailPanelType = { table: MRT_TableInstance; }; -export type DataTableProps = { +export type DataTableProps = { /** * An optional action button above the table. */ @@ -119,7 +124,7 @@ export type DataTableProps = { /** * The columns that make up the table */ - columns: DataTableColumn[]; + columns: DataTableColumn[]; /** * The current page number. */ @@ -135,11 +140,11 @@ export type DataTableProps = { /** * An optional set of filters to render in the filters menu */ - filters?: Array | string>; + filters?: Array | DataTableColumn | string>; /** * The function to get the ID of a row */ - getRowId?: MRT_TableOptions["getRowId"]; + getRowId?: MRT_TableOptions["getRowId"]; /** * Callback that fires whenever the table needs to fetch new data, due to changes in * page, results per page, search input, filters, or sorting @@ -150,9 +155,9 @@ export type DataTableProps = { search, filters, sort, - }: DataTableGetDataType) => - | MRT_TableOptions["data"] - | Promise["data"]>; + }: DataTableGetDataType) => + | MRT_TableOptions["data"] + | Promise["data"]>; /** * If true, the end user can resize individual columns. */ @@ -219,15 +224,15 @@ export type DataTableProps = { /** * The optional component to display when expanding a row. */ - renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; + renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; /** * Action buttons to display in each row */ - rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; + rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; /** * Menu items to include in the optional actions menu on each row. */ - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; /** * The debounce time, in milliseconds, for the search input firing * `onChangeSearch` when changed. If `hasSearchSubmitButton` is true, @@ -321,7 +326,7 @@ const ScrollableTableContainer = styled("div", { }), ); -const DataTable = ({ +const DataTable = ({ additionalActionButton, additionalActionMenuItems, bulkActionMenuItems, @@ -357,16 +362,15 @@ const DataTable = ({ rowActionMenuItems, searchDelayTime, totalRows, -}: DataTableProps) => { +}: DataTableProps) => { const { t } = useTranslation(); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [pagination, setPagination] = useState({ pageIndex: currentPage, pageSize: resultsPerPage, }); - const [draggingRow, setDraggingRow] = - useState | null>(); + const [draggingRow, setDraggingRow] = useState | null>(); const [isTableContainerScrolledToStart, setIsTableContainerScrolledToStart] = useState(true); const [isTableContainerScrolledToEnd, setIsTableContainerScrolledToEnd] = @@ -385,8 +389,8 @@ const DataTable = ({ useState(initialDensity); const [rowSelection, setRowSelection] = useState({}); const [search, setSearch] = useState(initialSearchValue); - const [filters, setFilters] = useState(); - const [initialFilters, setInitialFilters] = useState(); + const [filters, setFilters] = useState[]>(); + const [initialFilters, setInitialFilters] = useState[]>(); const [isLoading, setIsLoading] = useState(true); const [isEmpty, setIsEmpty] = useState(); const [errorMessage, setErrorMessage] = useState( @@ -423,9 +427,7 @@ const DataTable = ({ page: pagination.pageIndex, }); - const getRowId = getRowIdProp - ? getRowIdProp - : (row: DataTableRowData) => row.id; + const getRowId = getRowIdProp ? getRowIdProp : (row: TData) => row.id; const rowDensityClassName = useMemo(() => { return rowDensity === "spacious" @@ -436,7 +438,7 @@ const DataTable = ({ }, [rowDensity]); const renderRowActions = useCallback( - ({ row }: { row: MRT_Row }) => { + ({ row }: { row: MRT_Row }) => { const currentIndex = row.index + (pagination.pageIndex - 1) * pagination.pageSize; return ( @@ -468,7 +470,7 @@ const DataTable = ({ * filterOptions format, which allows for strings and { label: string, value: string } */ const convertFilterSelectOptions = useCallback( - (options: DataTableColumn["filterSelectOptions"]) => + (options: DataTableColumn["filterSelectOptions"]) => options?.map((option) => typeof option === "string" ? { @@ -486,15 +488,16 @@ const DataTable = ({ ); const convertColumnToFilter = useCallback( - (column: DataTableColumn) => + (column: DataTableColumn) => column.enableColumnFilter !== false && column.accessorKey - ? ({ + ? { id: column.accessorKey, label: column.header, variant: column.filterVariant, options: convertFilterSelectOptions(column.filterSelectOptions), - } satisfies DataFilter as DataFilter) - : null, + } + : // } satisfies DataFilter as DataFilter) + null, [convertFilterSelectOptions], ); @@ -505,7 +508,7 @@ const DataTable = ({ */ const dataTableFilters = useMemo(() => { const providedFilters = filtersProp || columns; - return providedFilters.reduce((accumulator, item) => { + return providedFilters.reduce[]>((accumulator, item) => { if (typeof item === "string") { const foundColumn = columns.find( (column) => column.accessorKey === item, @@ -532,7 +535,7 @@ const DataTable = ({ }, [columns, filtersProp, convertColumnToFilter]); const defaultCell = useCallback( - ({ cell }: { cell: DataTableCell }) => { + ({ cell }: { cell: DataTableCell }) => { const value = cell.getValue(); const hasTextWrapping = cell.column.columnDef.hasTextWrapping || @@ -745,7 +748,7 @@ const DataTable = ({ // Row actions enableRowActions: shouldDisplayRowActions, positionActionsColumn: - "last" as MRT_TableOptions["positionActionsColumn"], + "last" as MRT_TableOptions["positionActionsColumn"], renderRowActions: ({ row }) => renderRowActions({ row }), // Row selection diff --git a/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx b/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx index 2e80e6407f..9a59d81bc4 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx @@ -26,14 +26,14 @@ import { import { DataTableProps } from "./DataTable"; import { Trans, useTranslation } from "react-i18next"; -export type DataTableRowActionsProps = { +export type DataTableRowActionsProps = { row: MRT_Row | MRT_RowData; rowIndex: number; rowActionButtons?: ( row: MRT_RowData, ) => ReactElement; rowActionMenuItems?: (row: MRT_RowData) => MenuButtonProps["children"]; - totalRows?: DataTableProps["totalRows"]; + totalRows?: DataTableProps["totalRows"]; updateRowOrder?: ({ rowId, newRowIndex, @@ -43,14 +43,14 @@ export type DataTableRowActionsProps = { }) => void; }; -const DataTableRowActions = ({ +const DataTableRowActions = ({ row, rowIndex, rowActionButtons, rowActionMenuItems, totalRows, updateRowOrder, -}: DataTableRowActionsProps) => { +}: DataTableRowActionsProps) => { const { t } = useTranslation(); const handleToFrontClick = useCallback(() => { diff --git a/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx b/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx index 2da2769c6a..31458b2b5e 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx @@ -17,22 +17,28 @@ import { MenuItem } from "../MenuItem"; import { ListIcon, ShowIcon } from "../icons.generated"; import { densityValues } from "./constants"; import { DataTableProps } from "./DataTable"; -import { MRT_VisibilityState } from "material-react-table"; +import { MRT_RowData, MRT_VisibilityState } from "material-react-table"; import { useTranslation } from "react-i18next"; -export type DataTableSettingsProps = { - hasChangeableDensity: DataTableProps["hasChangeableDensity"]; +export type DataTableSettingsProps = { + hasChangeableDensity: DataTableProps["hasChangeableDensity"]; rowDensity: (typeof densityValues)[number]; setRowDensity: Dispatch>; - hasColumnVisibility: DataTableProps["hasColumnVisibility"]; - columns: DataTableProps["columns"]; + hasColumnVisibility: DataTableProps["hasColumnVisibility"]; + columns: DataTableProps["columns"]; columnVisibility?: MRT_VisibilityState; setColumnVisibility: Dispatch< SetStateAction >; }; -const DataTableSettings = ({ +type DataTableSettingsComponent = (( + props: DataTableSettingsProps, +) => JSX.Element) & { + displayName?: string; +}; + +const DataTableSettings = ({ hasChangeableDensity, rowDensity, setRowDensity, @@ -40,7 +46,7 @@ const DataTableSettings = ({ columns, columnVisibility, setColumnVisibility, -}: DataTableSettingsProps) => { +}: DataTableSettingsProps) => { const { t } = useTranslation(); const changeRowDensity = useCallback( @@ -131,7 +137,9 @@ const DataTableSettings = ({ ); }; -const MemoizedDataTableSettings = memo(DataTableSettings); +const MemoizedDataTableSettings = memo( + DataTableSettings, +) as DataTableSettingsComponent; MemoizedDataTableSettings.displayName = "DataTableSettings"; export { MemoizedDataTableSettings as DataTableSettings }; diff --git a/packages/odyssey-react-mui/src/DataTable/reorderDataRowsLocally.tsx b/packages/odyssey-react-mui/src/DataTable/reorderDataRowsLocally.tsx index 393f13325d..3f9d8d7e9c 100644 --- a/packages/odyssey-react-mui/src/DataTable/reorderDataRowsLocally.tsx +++ b/packages/odyssey-react-mui/src/DataTable/reorderDataRowsLocally.tsx @@ -20,15 +20,15 @@ import { MRT_RowData } from "material-react-table"; * @param newIndex - The new index to move the row to. * @returns A new array of data with the row moved to the specified index. */ -export const reorderDataRowsLocally = ({ +export const reorderDataRowsLocally = ({ currentData, rowId, newRowIndex, }: { - currentData: MRT_RowData[]; + currentData: TData[]; rowId: string; newRowIndex: number; -}): MRT_RowData[] => { +}): TData[] => { const updatedData = [...currentData]; const rowIndex = updatedData.findIndex((row) => row.id === rowId); diff --git a/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx b/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx index aca67be713..36a664e44c 100644 --- a/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx +++ b/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx @@ -16,7 +16,7 @@ import { reorderDataRowsLocally } from "./reorderDataRowsLocally"; import { useOdysseyDesignTokens } from "../OdysseyDesignTokensContext"; import { MRT_Row, MRT_RowData, MRT_TableInstance } from "material-react-table"; -export const useRowReordering = ({ +export const useRowReordering = ({ totalRows, onReorderRows, data, @@ -26,14 +26,12 @@ export const useRowReordering = ({ resultsPerPage, page, }: { - totalRows: DataTableProps["totalRows"]; - onReorderRows: DataTableProps["onReorderRows"]; - data: MRT_RowData[]; - setData: Dispatch>; - draggingRow?: MRT_Row | null; - setDraggingRow: Dispatch< - SetStateAction | null | undefined> - >; + totalRows: DataTableProps["totalRows"]; + onReorderRows: DataTableProps["onReorderRows"]; + data: TData[]; + setData: Dispatch>; + draggingRow?: MRT_Row | null; + setDraggingRow: Dispatch | null | undefined>>; resultsPerPage: number; page: number; }) => { @@ -68,7 +66,6 @@ export const useRowReordering = ({ const dragHandleStyles = { padding: odysseyDesignTokens.Spacing1, borderRadius: odysseyDesignTokens.BorderRadiusMain, - "&:focus-visible": { boxShadow: `0 0 0 2px ${odysseyDesignTokens.HueNeutralWhite}, 0 0 0 4px ${odysseyDesignTokens.PalettePrimaryMain}`, outline: "2px solid transparent", @@ -106,12 +103,12 @@ export const useRowReordering = ({ return undefined; }; - const setHoveredRow = ( - table: MRT_TableInstance, - id: MRT_RowData["id"], - ) => { + const setHoveredRow = (table: MRT_TableInstance, id: TData["id"]) => { if (id) { - const nextRow: MRT_RowData = table.getRow(id); + // The `as MRT_Row` is necessary here to overcome some type/generic + // issues with the type of `setHoveredRow` defined by MRT. It's not ideal code, + // but it's the only way that works without a much larger rewrite. + const nextRow = table.getRow(id) as MRT_Row; if (nextRow) { table.setHoveredRow(nextRow); @@ -120,8 +117,8 @@ export const useRowReordering = ({ }; type HandleDragHandleKeyDownArgs = { - table: MRT_TableInstance; - row: MRT_Row; + table: MRT_TableInstance; + row: MRT_Row; event: KeyboardEvent; }; @@ -192,7 +189,7 @@ export const useRowReordering = ({ } }; - const handleDragHandleOnDragEnd = (table: MRT_TableInstance) => { + const handleDragHandleOnDragEnd = (table: MRT_TableInstance) => { const cols = table.getAllColumns(); cols[0].toggleVisibility(); @@ -200,24 +197,20 @@ export const useRowReordering = ({ if (draggingRow) { updateRowOrder({ rowId: draggingRow.id, - newRowIndex: (hoveredRow as MRT_RowData).index, + newRowIndex: (hoveredRow as TData).index, }); } setDraggingRow(null); }; - const handleDragHandleOnDragCapture = ( - table: MRT_TableInstance, - ) => { + const handleDragHandleOnDragCapture = (table: MRT_TableInstance) => { if (!draggingRow && table.getState().draggingRow?.id) { setDraggingRow(table.getState().draggingRow); } }; - const resetDraggingAndHoveredRow = ( - table: MRT_TableInstance, - ) => { + const resetDraggingAndHoveredRow = (table: MRT_TableInstance) => { setDraggingRow(null); table.setHoveredRow(null); }; diff --git a/packages/odyssey-react-mui/src/labs/DataFilters.tsx b/packages/odyssey-react-mui/src/labs/DataFilters.tsx index 62e81d1647..0b9deb7c4b 100644 --- a/packages/odyssey-react-mui/src/labs/DataFilters.tsx +++ b/packages/odyssey-react-mui/src/labs/DataFilters.tsx @@ -82,12 +82,12 @@ export type UpdateFiltersOrValues = ({ }) => void; // This is the shape of each individual filter -export type DataFilter = { +export type DataFilter = { /** * A unique ID for the filter, typically the same id * as the column it'll be applied to. */ - id: Exclude["accessorKey"], undefined>; + id: Exclude["accessorKey"], undefined>; /** * `Autocomplete` normally only allows values that exist in the list box. This feature allows you to enter in any value in the text field and have that be the stored value in `Autocomplete` * @@ -102,7 +102,7 @@ export type DataFilter = { * The type of filter, which determines which filtering control * is shown. */ - variant?: MRT_ColumnDef["filterVariant"]; + variant?: MRT_ColumnDef["filterVariant"]; /** * The current value of the filter. Typically a string, but * filters that allow for multiple selections (such as multi-select) @@ -121,7 +121,7 @@ export type DataFilter = { }; // This is the type of the DataFilters component itself -export type DataFiltersProps = { +export type DataFiltersProps = { /** * The callback that's fired when the search input changes * (either on change or on submit, based on the value of `hasSearchSubmitButton`). @@ -131,7 +131,7 @@ export type DataFiltersProps = { /** * The callback that's fired when filter values change. */ - onChangeFilters?: (filters: Array) => void; + onChangeFilters?: (filters: Array>) => void; /** * If true, a Search button will be provided alongside the search input * and `onChangeSearch` will fire when the button is clicked, rather than @@ -157,30 +157,42 @@ export type DataFiltersProps = { * The filters available in the filter menu. If undefined, * the filter menu won't be shown. */ - filters?: Array; + filters?: Array>; /** * If true, the filter and search will be disabled */ isDisabled?: boolean; }; -type FilterTagsProps = { - activeFilters: DataFilter[]; +type DataFiltersComponent = (( + props: DataFiltersProps, +) => JSX.Element) & { + displayName?: string; +}; + +type FilterTagsProps = { + activeFilters: DataFilter[]; updateFilterAndInputValues: UpdateFiltersOrValues; }; +type FilterTagsComponent = (( + props: FilterTagsProps, +) => JSX.Element) & { + displayName?: string; +}; + type FiltersToRender = { id: string; label: string; value: string; }; -const FilterTags = ({ +const FilterTags: FilterTagsComponent = ({ activeFilters, updateFilterAndInputValues, -}: FilterTagsProps) => { +}: FilterTagsProps) => { const filtersWithValues = activeFilters.filter( - (activeFilter: DataFilter) => activeFilter.value, + (activeFilter: DataFilter) => activeFilter.value, ); const filtersToRender: FiltersToRender[] = []; @@ -252,10 +264,10 @@ const FilterTags = ({ ); }; -const MemoizedFilterTags = memo(FilterTags); +const MemoizedFilterTags = memo(FilterTags) as FilterTagsComponent; MemoizedFilterTags.displayName = "FilterTags"; -const DataFilters = ({ +const DataFilters = ({ onChangeSearch, onChangeFilters, hasSearchSubmitButton = false, @@ -264,8 +276,8 @@ const DataFilters = ({ additionalActions, filters: filtersProp = [], isDisabled, -}: DataFiltersProps) => { - const [filters, setFilters] = useState(filtersProp); +}: DataFiltersProps) => { + const [filters, setFilters] = useState[]>(filtersProp); const { t } = useTranslation(); const odysseyDesignTokens = useOdysseyDesignTokens(); @@ -301,7 +313,7 @@ const DataFilters = ({ >(); const [filterPopoverCurrentFilter, setFilterPopoverCurrentFilter] = useState< - DataFilter | undefined + DataFilter | undefined >(); const menuRef = useRef(); @@ -861,7 +873,7 @@ const DataFilters = ({ ); }; -const MemoizedDataFilters = memo(DataFilters); +const MemoizedDataFilters = memo(DataFilters) as DataFiltersComponent; MemoizedDataFilters.displayName = "DataFilters"; export { MemoizedDataFilters as DataFilters }; diff --git a/packages/odyssey-react-mui/src/labs/DataTable.tsx b/packages/odyssey-react-mui/src/labs/DataTable.tsx index 6e4e420011..bf07cc3de9 100644 --- a/packages/odyssey-react-mui/src/labs/DataTable.tsx +++ b/packages/odyssey-react-mui/src/labs/DataTable.tsx @@ -69,7 +69,7 @@ export type { // The shape of the table columns, // with props named to match their MRT_ColumnDef counterparts -export type DataTableColumn = { +export type DataTableColumn = { /** * The unique ID of the column */ @@ -82,12 +82,12 @@ export type DataTableColumn = { * Customize the way each cell in the column is * displayed via a custom React component. */ - Cell?: MRT_ColumnDef["Cell"]; + Cell?: MRT_ColumnDef["Cell"]; /** * The UI control that will be used to filter the column. * Defaults to a standard text input. */ - filterVariant?: MRT_ColumnDef["filterVariant"]; + filterVariant?: MRT_ColumnDef["filterVariant"]; /** * If the filter control has preset options (such as a select or multi-select), * these are the options provided. @@ -127,16 +127,16 @@ export type DataTableColumn = { enableHiding?: boolean; }; -export type DataTableProps = { +export type DataTableProps = { /** * The columns that make up the table */ - columns: DataTableColumn[]; + columns: DataTableColumn[]; /** * The data that goes into the table, which will be displayed * as the table rows */ - data: MRT_TableOptions["data"]; + data: MRT_TableOptions["data"]; /** * The total number of rows in the table. Optional, because it's sometimes impossible * to calculate. Used in table pagination to know when to disable the "next"/"more" button. @@ -145,7 +145,7 @@ export type DataTableProps = { /** * The function to get the ID of a row */ - getRowId?: MRT_TableOptions["getRowId"]; + getRowId?: MRT_TableOptions["getRowId"]; /** * The initial density of the table. This is available even if the table density * isn't changeable. @@ -217,9 +217,9 @@ export type DataTableProps = { page?: number; resultsPerPage?: number; search?: string; - filters?: DataFilter[]; + filters?: DataFilter[]; sort?: MRT_SortingState; - }) => MRT_TableOptions["data"]; + }) => MRT_TableOptions["data"]; /** * Callback that fires when the user reorders rows within the table. Can be used * to propogate order change to the backend. @@ -248,24 +248,24 @@ export type DataTableProps = { * Action buttons to display in each row */ rowActionButtons?: ( - row: MRT_RowData, + row: TData, ) => ReactElement; /** * Menu items to include in the optional actions menu on each row. */ rowActionMenuItems?: ( - row: MRT_RowData, + row: TData, ) => ReactElement; }; -type TableType = MRT_TableInstance; +type TableType = MRT_TableInstance; -const reorderDataRowsLocally = ({ +const reorderDataRowsLocally = ({ currentData, rowId, newIndex, }: { - currentData: MRT_TableOptions["data"]; + currentData: MRT_TableOptions["data"]; rowId: string; newIndex: number; }) => { @@ -284,7 +284,7 @@ const reorderDataRowsLocally = ({ return updatedData; }; -const DataTable = ({ +const DataTable = ({ columns, data: dataProp, getRowId, @@ -309,13 +309,12 @@ const DataTable = ({ hasRowSelection, hasSearch, hasSorting, -}: DataTableProps) => { +}: DataTableProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const { t } = useTranslation(); - const [draggingRow, setDraggingRow] = useState | null>(); + const [draggingRow, setDraggingRow] = useState | null>(); const [showSkeletons, setShowSkeletons] = useState(true); - const [data, setData] = - useState["data"]>(dataProp); + const [data, setData] = useState["data"]>(dataProp); const [page, setPage] = useState(pageProp); const [resultsPerPage, setResultsPerPage] = useState(resultsPerPageProp); @@ -335,7 +334,7 @@ const DataTable = ({ ); const [globalFilter, setGlobalFilter] = useState(""); - const [filters, setFilters] = useState>(); + const [filters, setFilters] = useState>>(); useEffect(() => { setShowSkeletons(false); @@ -389,9 +388,12 @@ const DataTable = ({ setGlobalFilter(value); }, []); - const handleFilters = useCallback((updatedFilters: Array) => { - setFilters(updatedFilters); - }, []); + const handleFilters = useCallback( + (updatedFilters: Array>) => { + setFilters(updatedFilters); + }, + [], + ); const handleRowSelectionChange = useCallback( (updater: MRT_Updater) => { @@ -428,9 +430,9 @@ const DataTable = ({ const rowVirtualizerInstanceRef = useRef>(null); - const setHoveredRow = (table: TableType, id: MRT_RowData["id"]) => { + const setHoveredRow = (table: TableType, id: TData["id"]) => { if (id) { - const nextRow: MRT_RowData = table.getRow(id); + const nextRow = table.getRow(id) as MRT_Row; if (nextRow) { table.setHoveredRow(nextRow); @@ -438,14 +440,14 @@ const DataTable = ({ } }; - const resetDraggingAndHoveredRow = (table: TableType) => { + const resetDraggingAndHoveredRow = (table: TableType) => { setDraggingRow(null); table.setHoveredRow(null); }; type HandleDragHandleKeyDownArgs = { - table: TableType; - row: MRT_Row; + table: TableType; + row: MRT_Row; event: KeyboardEvent; }; @@ -515,15 +517,17 @@ const DataTable = ({ [ data, draggingRow, - odysseyDesignTokens, + odysseyDesignTokens.TransitionDurationMainAsNumber, page, + resetDraggingAndHoveredRow, resultsPerPage, + setHoveredRow, updateRowOrder, ], ); const handleDragHandleOnDragEnd = useCallback( - (table: TableType) => { + (table: TableType) => { const cols = table.getAllColumns(); cols[0].toggleVisibility(); @@ -531,7 +535,7 @@ const DataTable = ({ if (draggingRow) { updateRowOrder({ rowId: draggingRow.id, - newIndex: (hoveredRow as MRT_RowData).index, + newIndex: (hoveredRow as TData).index, }); } @@ -541,7 +545,7 @@ const DataTable = ({ ); const handleDragHandleOnDragCapture = useCallback( - (table: TableType) => { + (table: TableType) => { if (!draggingRow && table.getState().draggingRow?.id) { setDraggingRow(table.getState().draggingRow); } @@ -683,7 +687,7 @@ const DataTable = ({ return ( - {rowActionButtons?.(row)} + {rowActionButtons?.(row.original)} {(rowActionMenuItems || hasRowReordering) && ( } @@ -694,7 +698,7 @@ const DataTable = ({ > {rowActionMenuItems && ( <> - {rowActionMenuItems(row)} + {rowActionMenuItems(row.original)}
)} diff --git a/packages/odyssey-react-mui/src/labs/DataView/BulkActionsMenu.tsx b/packages/odyssey-react-mui/src/labs/DataView/BulkActionsMenu.tsx index bc39f705fc..b396934b3d 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/BulkActionsMenu.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/BulkActionsMenu.tsx @@ -25,9 +25,9 @@ import { useOdysseyDesignTokens, } from "../../OdysseyDesignTokensContext"; -export type BulkActionsMenuProps = { +export type BulkActionsMenuProps = { data: MRT_RowData[]; - menuItems: UniversalProps["bulkActionMenuItems"]; + menuItems: UniversalProps["bulkActionMenuItems"]; rowSelection: MRT_RowSelectionState; setRowSelection: Dispatch>; }; @@ -41,12 +41,12 @@ const BulkActionsContainer = styled("div", { gap: odysseyDesignTokens.Spacing2, })); -const BulkActionsMenu = ({ +const BulkActionsMenu = ({ data, menuItems, rowSelection, setRowSelection, -}: BulkActionsMenuProps) => { +}: BulkActionsMenuProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const { t } = useTranslation(); diff --git a/packages/odyssey-react-mui/src/labs/DataView/CardLayoutContent.tsx b/packages/odyssey-react-mui/src/labs/DataView/CardLayoutContent.tsx index e647ab4075..dfe9c86152 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/CardLayoutContent.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/CardLayoutContent.tsx @@ -30,19 +30,19 @@ import { RowActions } from "./RowActions"; import { DataCard } from "./DataCard"; import { CardLayout, CardLayoutProps, UniversalProps } from "./componentTypes"; -export type CardLayoutContentProps = { +export type CardLayoutContentProps = { currentLayout: CardLayout; - data: MRT_RowData[]; - draggingRow?: MRT_Row | null; + data: TData[]; + draggingRow?: MRT_Row | null; emptyState: ReactNode; - getRowId: UniversalProps["getRowId"]; - hasRowReordering: UniversalProps["hasRowReordering"]; - hasRowSelection: UniversalProps["hasRowSelection"]; + getRowId: UniversalProps["getRowId"]; + hasRowReordering: UniversalProps["hasRowReordering"]; + hasRowSelection: UniversalProps["hasRowSelection"]; isEmpty?: boolean; isLoading: boolean; isNoResults?: boolean; isRowReorderingDisabled?: boolean; - onReorderRows: UniversalProps["onReorderRows"]; + onReorderRows: UniversalProps["onReorderRows"]; pagination: { pageIndex: number; pageSize: number }; rowReorderingUtilities: { dragHandleStyles: CSSObject; @@ -64,15 +64,13 @@ export type CardLayoutContentProps = { row, event, }: { - table: MRT_TableInstance; - row: MRT_Row; + table: MRT_TableInstance; + row: MRT_Row; event: React.KeyboardEvent; }) => void; - handleDragHandleOnDragCapture: ( - table: MRT_TableInstance, - ) => void; - handleDragHandleOnDragEnd: (table: MRT_TableInstance) => void; - resetDraggingAndHoveredRow: (table: MRT_TableInstance) => void; + handleDragHandleOnDragCapture: (table: MRT_TableInstance) => void; + handleDragHandleOnDragEnd: (table: MRT_TableInstance) => void; + resetDraggingAndHoveredRow: (table: MRT_TableInstance) => void; updateRowOrder: ({ rowId, newRowIndex, @@ -83,8 +81,13 @@ export type CardLayoutContentProps = { }; rowSelection: MRT_RowSelectionState; setRowSelection: Dispatch>; - cardLayoutOptions: CardLayoutProps; - totalRows: UniversalProps["totalRows"]; + cardLayoutOptions: CardLayoutProps; + totalRows: UniversalProps["totalRows"]; +}; +type CardLayoutContentComponent = (( + props: CardLayoutContentProps, +) => JSX.Element) & { + displayName?: string; }; const StackContainer = styled("div", { @@ -134,7 +137,7 @@ const CheckboxContainer = styled("div", { marginBlockStart: `-${odysseyDesignTokens.Spacing1}`, })); -const CardLayoutContent = ({ +const CardLayoutContent = ({ currentLayout, data, emptyState, @@ -151,7 +154,7 @@ const CardLayoutContent = ({ setRowSelection, cardLayoutOptions, totalRows, -}: CardLayoutContentProps) => { +}: CardLayoutContentProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const handleRowSelectionChange = useCallback( @@ -185,7 +188,7 @@ const CardLayoutContent = ({ {emptyState} ) : ( <> - {data.map((row: MRT_RowData, index: number) => { + {data.map((row: TData, index: number) => { const { overline, title, @@ -251,7 +254,9 @@ const CardLayoutContent = ({ ); }; -const MemoizedCardLayoutContent = memo(CardLayoutContent); +const MemoizedCardLayoutContent = memo( + CardLayoutContent, +) as CardLayoutContentComponent; MemoizedCardLayoutContent.displayName = "CardLayoutContent"; export { MemoizedCardLayoutContent as CardLayoutContent }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/DataCard.tsx b/packages/odyssey-react-mui/src/labs/DataView/DataCard.tsx index 19360acbf5..4059352dda 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/DataCard.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/DataCard.tsx @@ -50,13 +50,13 @@ export const CARD_IMAGE_SIZE_COMPACT = "48px"; export const cardVariantValues = ["tile", "stack", "compact"] as const; -export type DataCardProps = { +export type DataCardProps = { children?: ReactNode; description?: string; image?: ReactElement; overline?: string; - renderDetailPanel?: CardLayoutProps["renderDetailPanel"]; - row: MRT_RowData; + renderDetailPanel?: CardLayoutProps["renderDetailPanel"]; + row: TData; title?: string; variant?: (typeof cardVariantValues)[number]; } & ( @@ -74,6 +74,12 @@ export type DataCardProps = { } ); +type DataCardComponent = (( + props: DataCardProps, +) => JSX.Element) & { + displayName?: string; +}; + const AccessoryContainer = styled("div", { shouldForwardProp: (prop) => prop !== "odysseyDesignTokens" && prop !== "variant", @@ -158,7 +164,7 @@ const AccessoryPlaceholder = styled(MuiIconButton)(() => ({ })); const buttonProviderValue = { isFullWidth: true }; -const DataCard = ({ +const DataCard: DataCardComponent = ({ Accessory: AccessoryProp, button, children, @@ -171,7 +177,7 @@ const DataCard = ({ row, title, variant = "tile", -}: DataCardProps) => { +}: DataCardProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const { t } = useTranslation(); @@ -318,7 +324,7 @@ const DataCard = ({ ); }; -const MemoizedDataCard = memo(DataCard); +const MemoizedDataCard = memo(DataCard) as DataCardComponent; MemoizedDataCard.displayName = "DataCard"; export { MemoizedDataCard as DataCard }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/DataTable.tsx b/packages/odyssey-react-mui/src/labs/DataView/DataTable.tsx index 5292be9427..c9a237b87e 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/DataTable.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/DataTable.tsx @@ -14,10 +14,17 @@ import { memo, useMemo } from "react"; import { DataView } from "./DataView"; import { TableLayoutProps, UniversalProps } from "./componentTypes"; +import { MRT_RowData } from "material-react-table"; -export type DataTableProps = UniversalProps & TableLayoutProps; +export type DataTableProps = UniversalProps & + TableLayoutProps; +type DataTableComponent = (( + props: DataTableProps, +) => JSX.Element) & { + displayName?: string; +}; -const DataTable = ({ +const DataTable = ({ additionalActionButton, additionalActionMenuItems, bulkActionMenuItems, @@ -54,7 +61,7 @@ const DataTable = ({ rowActionMenuItems, searchDelayTime, totalRows, -}: DataTableProps) => { +}: DataTableProps) => { const tableLayoutOptions = useMemo( () => ({ columns, @@ -115,7 +122,7 @@ const DataTable = ({ ); }; -const MemoizedDataTable = memo(DataTable); +const MemoizedDataTable = memo(DataTable) as DataTableComponent; MemoizedDataTable.displayName = "DataTable"; export { MemoizedDataTable as DataTable }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/DataView.tsx b/packages/odyssey-react-mui/src/labs/DataView/DataView.tsx index c3b883c818..2b9c8a39f5 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/DataView.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/DataView.tsx @@ -50,7 +50,13 @@ import { } from "../../OdysseyDesignTokensContext"; import styled from "@emotion/styled"; -export type DataViewProps = UniversalProps & ViewProps; +export type DataViewProps = UniversalProps & + ViewProps; +type DataViewComponent = (( + props: DataViewProps, +) => JSX.Element) & { + displayName?: string; +}; const DataViewContainer = styled("div", { shouldForwardProp: (prop) => prop !== "odysseyDesignTokens", @@ -87,7 +93,7 @@ const MetaTextContainer = styled("div", { marginInlineEnd: odysseyDesignTokens.Spacing2, })); -const DataView = ({ +const DataView = ({ additionalActionButton, additionalActionMenuItems, availableLayouts = allAvailableLayouts, @@ -124,7 +130,7 @@ const DataView = ({ maxPages, maxResultsPerPage, onPaginationChange, -}: DataViewProps) => { +}: DataViewProps) => { const odysseyDesignTokens = useOdysseyDesignTokens(); const { t } = useTranslation(); @@ -132,23 +138,23 @@ const DataView = ({ initialLayout ?? availableLayouts[0], ); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(isLoadingProp ?? true); const [isEmpty, setIsEmpty] = useState(isEmptyProp ?? true); const [isNoResults, setIsNoResults] = useState( isNoResultsProp ?? false, ); const [errorMessage, setErrorMessage] = - useState(errorMessageProp); + useState["errorMessage"]>(errorMessageProp); const [search, setSearch] = useState(""); const [initialFilters, setInitialFilters] = - useState(filtersProp); + useState["filters"]>(filtersProp); const [filters, setFilters] = - useState(filtersProp); + useState["filters"]>(filtersProp); - const [draggingRow, setDraggingRow] = useState | null>(); + const [draggingRow, setDraggingRow] = useState | null>(); const [rowSelection, setRowSelection] = useState({}); @@ -201,7 +207,7 @@ const DataView = ({ ], ); - const getRowId = getRowIdProp ? getRowIdProp : (row: MRT_RowData) => row.id; + const getRowId = getRowIdProp ? getRowIdProp : (row: TData) => row.id; // Update pagination state if props change useEffect(() => { @@ -482,7 +488,7 @@ const DataView = ({ ); }; -const MemoizedDataView = memo(DataView); +const MemoizedDataView = memo(DataView) as DataViewComponent; MemoizedDataView.displayName = "DataView"; export { MemoizedDataView as DataView }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/DetailPanel.tsx b/packages/odyssey-react-mui/src/labs/DataView/DetailPanel.tsx index a69f92ee1b..29a20320e3 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/DetailPanel.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/DetailPanel.tsx @@ -12,14 +12,14 @@ import { memo } from "react"; import { CardLayoutProps } from "./componentTypes"; -import { DataRow } from "./dataTypes"; +import { MRT_RowData } from "material-react-table"; -const DetailPanel = ({ +const DetailPanel = ({ row, renderDetailPanel, }: { - row: DataRow; - renderDetailPanel: CardLayoutProps["renderDetailPanel"]; + row: TData; + renderDetailPanel: CardLayoutProps["renderDetailPanel"]; }) => { if (!renderDetailPanel) return null; return renderDetailPanel({ row }); diff --git a/packages/odyssey-react-mui/src/labs/DataView/RowActions.tsx b/packages/odyssey-react-mui/src/labs/DataView/RowActions.tsx index 4587ae6c40..07934e9d63 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/RowActions.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/RowActions.tsx @@ -25,7 +25,7 @@ import { MenuItem } from "../../MenuItem"; import { DataTableProps } from "./DataTable"; import { MenuButtonProps } from "../../MenuButton"; -export type RowActionsProps = { +export type RowActionsProps = { isRowReorderingDisabled?: boolean; row: MRT_Row | MRT_RowData; rowActionButtons?: ( @@ -33,7 +33,7 @@ export type RowActionsProps = { ) => ReactElement | ReactElement; rowActionMenuItems?: (row: MRT_RowData) => MenuButtonProps["children"]; rowIndex: number; - totalRows?: DataTableProps["totalRows"]; + totalRows?: DataTableProps["totalRows"]; updateRowOrder?: ({ newRowIndex, rowId, @@ -43,14 +43,14 @@ export type RowActionsProps = { }) => void; }; -const RowActions = ({ +const RowActions = ({ isRowReorderingDisabled, row, rowActionMenuItems, rowIndex, totalRows, updateRowOrder, -}: RowActionsProps) => { +}: RowActionsProps) => { const { t } = useTranslation(); const handleToFrontClick = useCallback(() => { diff --git a/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx b/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx index e09faee930..245111ff34 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx @@ -65,20 +65,20 @@ const RowActionsContainer = styled("div")(() => ({ display: "flex", })); -export type TableLayoutContentProps = { - columns: TableLayoutProps["columns"]; +export type TableLayoutContentProps = { + columns: TableLayoutProps["columns"]; data: MRT_RowData[]; - draggingRow?: MRT_Row | null; + draggingRow?: MRT_Row | null; emptyState: ReactNode; enableVirtualization?: boolean; - getRowId: UniversalProps["getRowId"]; - hasRowReordering: UniversalProps["hasRowReordering"]; - hasRowSelection: UniversalProps["hasRowSelection"]; + getRowId: UniversalProps["getRowId"]; + hasRowReordering: UniversalProps["hasRowReordering"]; + hasRowSelection: UniversalProps["hasRowSelection"]; isEmpty?: boolean; isLoading: boolean; isNoResults?: boolean; isRowReorderingDisabled?: boolean; - onReorderRows: UniversalProps["onReorderRows"]; + onReorderRows: UniversalProps["onReorderRows"]; pagination: { pageIndex: number; pageSize: number; @@ -103,15 +103,13 @@ export type TableLayoutContentProps = { row, event, }: { - table: MRT_TableInstance; - row: MRT_Row; + table: MRT_TableInstance; + row: MRT_Row; event: React.KeyboardEvent; }) => void; - handleDragHandleOnDragCapture: ( - table: MRT_TableInstance, - ) => void; - handleDragHandleOnDragEnd: (table: MRT_TableInstance) => void; - resetDraggingAndHoveredRow: (table: MRT_TableInstance) => void; + handleDragHandleOnDragCapture: (table: MRT_TableInstance) => void; + handleDragHandleOnDragEnd: (table: MRT_TableInstance) => void; + resetDraggingAndHoveredRow: (table: MRT_TableInstance) => void; updateRowOrder: ({ rowId, newRowIndex, @@ -123,12 +121,18 @@ export type TableLayoutContentProps = { rowSelection: MRT_RowSelectionState; setRowSelection: Dispatch>; setTableState: Dispatch>; - tableLayoutOptions: TableLayoutProps; + tableLayoutOptions: TableLayoutProps; tableState: TableState; - totalRows: UniversalProps["totalRows"]; + totalRows: UniversalProps["totalRows"]; }; -const TableLayoutContent = ({ +type TableLayoutContentComponent = (( + props: TableLayoutContentProps, +) => JSX.Element) & { + displayName?: string; +}; + +const TableLayoutContent = ({ columns, data, draggingRow, @@ -150,7 +154,7 @@ const TableLayoutContent = ({ tableLayoutOptions, tableState, totalRows, -}: TableLayoutContentProps) => { +}: TableLayoutContentProps) => { const [isTableContainerScrolledToStart, setIsTableContainerScrolledToStart] = useState(true); const [isTableContainerScrolledToEnd, setIsTableContainerScrolledToEnd] = @@ -198,7 +202,7 @@ const TableLayoutContent = ({ }, [tableState]); const defaultCell = useCallback< - ({ cell }: { cell: DataTableCell }) => ReactElement | string + ({ cell }: { cell: DataTableCell }) => ReactElement | string >(({ cell }) => { const value = cell.getValue(); const hasTextWrapping = @@ -219,7 +223,7 @@ const TableLayoutContent = ({ } = rowReorderingUtilities; const renderRowActions = useCallback( - ({ row }: { row: MRT_Row }) => { + ({ row }: { row: MRT_Row }) => { const currentIndex = row.index + (pagination.pageIndex - 1) * pagination.pageSize; return ( @@ -310,8 +314,8 @@ const TableLayoutContent = ({ const hasColumnWithGrow = columns.some((column) => column.grow === true); - const dataTable = useMaterialReactTable({ - data: !isEmpty && !isNoResults ? data : [], + const dataTable = useMaterialReactTable({ + data: (!isEmpty && !isNoResults ? data : []) as TData[], columns, getRowId, state: { @@ -477,7 +481,9 @@ const TableLayoutContent = ({ ); }; -const MemoizedTableLayoutContent = memo(TableLayoutContent); +const MemoizedTableLayoutContent = memo( + TableLayoutContent, +) as TableLayoutContentComponent; MemoizedTableLayoutContent.displayName = "TableLayoutContent"; export { MemoizedTableLayoutContent as TableLayoutContent }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/TableSettings.tsx b/packages/odyssey-react-mui/src/labs/DataView/TableSettings.tsx index 7abc448c36..94cb23aee7 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/TableSettings.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/TableSettings.tsx @@ -12,7 +12,7 @@ import { Dispatch, SetStateAction, memo, useCallback, useMemo } from "react"; import { Checkbox as MuiCheckbox } from "@mui/material"; -import { MRT_DensityState } from "material-react-table"; +import { MRT_DensityState, MRT_RowData } from "material-react-table"; import { useTranslation } from "react-i18next"; import { densityValues } from "./constants"; @@ -21,17 +21,23 @@ import { MenuButton } from "../../MenuButton"; import { MenuItem } from "../../MenuItem"; import { TableLayoutProps, TableState } from "./componentTypes"; -export type TableSettingsProps = { +export type TableSettingsProps = { setTableState: Dispatch>; - tableLayoutOptions: TableLayoutProps; + tableLayoutOptions: TableLayoutProps; tableState: TableState; }; -const TableSettings = ({ +type TableSettingsComponent = (( + props: TableSettingsProps, +) => JSX.Element) & { + displayName?: string; +}; + +const TableSettings: TableSettingsComponent = ({ setTableState, tableLayoutOptions, tableState, -}: TableSettingsProps) => { +}: TableSettingsProps) => { const { t } = useTranslation(); const { hasChangeableDensity, hasColumnVisibility, columns } = @@ -133,7 +139,7 @@ const TableSettings = ({ ); }; -const MemoizedTableSettings = memo(TableSettings); +const MemoizedTableSettings = memo(TableSettings) as TableSettingsComponent; MemoizedTableSettings.displayName = "TableSettings"; export { MemoizedTableSettings as TableSettings }; diff --git a/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts b/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts index 8a41f7039e..280e8818eb 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts @@ -39,7 +39,7 @@ export type CardLayout = (typeof availableCardLayouts)[number]; export type AvailableLayouts = DataLayout[]; export type AvailableCardLayouts = CardLayout[]; -export type UniversalProps = { +export type UniversalProps = { additionalActionButton?: ReactNode; additionalActionMenuItems?: ReactNode; bulkActionMenuItems?: ( @@ -49,15 +49,15 @@ export type UniversalProps = { emptyPlaceholder?: ReactNode; enableVirtualization?: boolean; errorMessage?: string; - filters?: Array | string>; + filters?: Array | DataTableColumn | string>; getData: ({ page, resultsPerPage, search, filters, sort, - }: DataGetDataType) => MRT_RowData[] | Promise; - getRowId?: MRT_TableOptions["getRowId"]; + }: DataGetDataType) => TData[] | Promise; + getRowId?: MRT_TableOptions["getRowId"]; hasFilters?: boolean; hasPagination?: boolean; hasRowReordering?: boolean; @@ -92,30 +92,30 @@ export type UniversalProps = { totalRows?: number; }; -export type TableLayoutProps = { - columns: DataTableColumn[]; +export type TableLayoutProps = { + columns: DataTableColumn[]; hasChangeableDensity?: boolean; hasColumnResizing?: boolean; hasColumnVisibility?: boolean; hasSorting?: boolean; initialDensity?: MRT_DensityState; - renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; - rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; + rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; + rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; }; -export type CardLayoutProps = { - itemProps: (row: MRT_RowData) => Omit; +export type CardLayoutProps = { + itemProps: (row: TData) => Omit, "row">; maxGridColumns?: number; - renderDetailPanel?: (props: { row: MRT_RowData }) => ReactNode; - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + renderDetailPanel?: (props: { row: TData }) => ReactNode; + rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; }; -export type ViewProps = { +export type ViewProps = { availableLayouts?: L[]; initialLayout?: L; - cardLayoutOptions?: CardLayoutProps; - tableLayoutOptions?: TableLayoutProps; + cardLayoutOptions?: CardLayoutProps; + tableLayoutOptions?: TableLayoutProps; }; export type TableState = { diff --git a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts index 640b15e009..73f0f940ac 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts @@ -21,42 +21,43 @@ import { import { DataFilter } from "../DataFilters"; -export type DataQueryParamsType = { - filters?: DataFilter[]; +export type DataQueryParamsType = { + filters?: DataFilter[]; page?: number; resultsPerPage?: number; search?: string; sort?: MRT_SortingState; }; -export type DataTableColumn = MRT_ColumnDef & { - /** - * @deprecated use hasTextWrapping instead of enableWrapping - */ - enableWrapping?: boolean; - hasTextWrapping?: boolean; -}; +export type DataTableColumn = + MRT_ColumnDef & { + /** + * @deprecated use hasTextWrapping instead of enableWrapping + */ + enableWrapping?: boolean; + hasTextWrapping?: boolean; + }; -export type DataTableColumnInstance = Omit< - MRT_Column, +export type DataTableColumnInstance = Omit< + MRT_Column, "columnDef" > & { - columnDef: DataTableColumn; + columnDef: DataTableColumn; }; -export type DataTableCell = Omit< - MRT_Cell, +export type DataTableCell = Omit< + MRT_Cell, "column" > & { - column: DataTableColumnInstance; + column: DataTableColumnInstance; }; export type DataColumns = DataTableColumn[]; export type DataRow = MRT_RowData; -export type DataGetDataType = { - filters?: DataFilter[]; +export type DataGetDataType = { + filters?: DataFilter[]; page?: number; resultsPerPage?: number; search?: string; @@ -71,7 +72,8 @@ export type DataOnReorderRowsType = { export type DataRowSelectionState = MRT_RowSelectionState; // Provided for backwards compatibilty with old DataTable types -export type DataTableGetDataType = DataGetDataType; +export type DataTableGetDataType = + DataGetDataType; export type DataTableOnReorderRowsType = DataOnReorderRowsType; export type DataTableRowSelectionState = DataRowSelectionState; export type DataTableRow = DataRow; diff --git a/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts b/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts index 33bac6a16f..bb20d47777 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts @@ -17,23 +17,25 @@ import { t } from "i18next"; import { DataQueryParamsType } from "./dataTypes"; import { UniversalProps } from "./componentTypes"; -type DataRequestType = { - dataQueryParams: DataQueryParamsType; - errorMessageProp: UniversalProps["errorMessage"]; - getData: UniversalProps["getData"]; - setData: Dispatch>; - setErrorMessage: Dispatch>; +type DataRequestType = { + dataQueryParams: DataQueryParamsType; + errorMessageProp: UniversalProps["errorMessage"]; + getData: UniversalProps["getData"]; + setData: Dispatch>; + setErrorMessage: Dispatch< + SetStateAction["errorMessage"]> + >; setIsLoading?: Dispatch>; }; -export const fetchData = async ({ +export const fetchData = async ({ dataQueryParams, errorMessageProp, getData, setData, setErrorMessage, setIsLoading, -}: DataRequestType) => { +}: DataRequestType) => { setIsLoading?.(true); setErrorMessage(errorMessageProp); try { diff --git a/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts b/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts index 349cf3884a..819a83dde7 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts @@ -17,15 +17,15 @@ import { DataFilter } from "../DataFilters"; import { DataTableColumn } from "../../DataTable"; import { UniversalProps, TableLayoutProps } from "./componentTypes"; -type FilterConversionType = { - columns?: TableLayoutProps["columns"]; - filters?: UniversalProps["filters"]; +type FilterConversionType = { + columns?: TableLayoutProps["columns"]; + filters?: UniversalProps["filters"]; }; -export const useFilterConversion = ({ +export const useFilterConversion = ({ columns, filters, -}: FilterConversionType) => { +}: FilterConversionType) => { const convertFilterSelectOptions = useCallback( (options: DataTableColumn["filterSelectOptions"]) => options?.map((option) => @@ -45,14 +45,14 @@ export const useFilterConversion = ({ ); const convertColumnToFilter = useCallback( - (column: DataTableColumn) => + (column: DataTableColumn) => column.enableColumnFilter !== false && column.accessorKey - ? ({ + ? { id: column.accessorKey, label: column.header, variant: column.filterVariant, options: convertFilterSelectOptions(column.filterSelectOptions), - } satisfies DataFilter as DataFilter) + } : null, [convertFilterSelectOptions], ); @@ -62,7 +62,7 @@ export const useFilterConversion = ({ // If not, they may be inferred from `columns` const providedFilters = filters || columns || []; - return providedFilters.reduce((accumulator, item) => { + return providedFilters.reduce[]>((accumulator, item) => { if (typeof item === "string") { const foundColumn = columns?.find( (column) => column.accessorKey === item, diff --git a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts index 99a848ce02..292c2da702 100644 --- a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts +++ b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts @@ -18,7 +18,7 @@ export const filterData = ({ ...args }: { data: Person[]; -} & DataGetDataType) => { +} & DataGetDataType) => { const { search, filters, sort, page = 1, resultsPerPage = 20 } = args; const searchFiltered = search @@ -45,7 +45,7 @@ export const filterData = ({ const handleStartLetterFilter = ( row: Person, - value: DataFilter["value"], + value: DataFilter["value"], ): boolean => { if (typeof row.name !== "string") return true; const firstLetter = row.name[0]?.toLowerCase(); @@ -57,7 +57,7 @@ export const filterData = ({ const handleStandardFilter = ( row: Person, id: keyof Person, - value: DataFilter["value"], + value: DataFilter["value"], ): boolean => { const rowValue = String(row[id]).toLowerCase(); @@ -98,7 +98,7 @@ export const filterData = ({ // for the provided sample data and demo filters. const columnFiltered = filters ? searchFiltered.filter((row) => - filters.every(({ id, value }: DataFilter) => { + filters.every(({ id, value }: DataFilter) => { // If the filter value is null, return all the data if (value === null || value === undefined) { return true; From 092e67d64af25c213555b895428c8e88f34f9273 Mon Sep 17 00:00:00 2001 From: Jordan Koschei Date: Mon, 18 Nov 2024 16:38:31 -0500 Subject: [PATCH 2/4] refactor: remove generics that break consumers --- .../src/DataTable/DataTable.tsx | 91 +++++++++---------- .../src/DataTable/DataTableRowActions.tsx | 8 +- .../src/DataTable/DataTableSettings.tsx | 24 ++--- .../src/DataTable/useRowReordering.tsx | 4 +- .../src/labs/DataFilters.tsx | 46 ++++------ .../odyssey-react-mui/src/labs/DataTable.tsx | 13 +-- .../src/labs/DataView/componentTypes.ts | 12 +-- .../src/labs/DataView/dataTypes.ts | 13 ++- .../src/labs/DataView/fetchData.ts | 2 +- .../src/labs/DataView/useFilterConversion.ts | 2 +- .../DataTable/DataTable.stories.tsx | 8 +- .../DataView/DataView.stories.tsx | 24 ++--- .../odyssey-labs/DataView/dataFunctions.ts | 8 +- .../odyssey-labs/DataView/personData.tsx | 2 +- 14 files changed, 115 insertions(+), 142 deletions(-) diff --git a/packages/odyssey-react-mui/src/DataTable/DataTable.tsx b/packages/odyssey-react-mui/src/DataTable/DataTable.tsx index 1e01ccfdfc..e60e041c7f 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTable.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTable.tsx @@ -33,7 +33,6 @@ import { MRT_Column, MRT_ColumnDef, MRT_TableInstance, - MRT_RowData, } from "material-react-table"; import { useTranslation } from "react-i18next"; import { @@ -65,34 +64,30 @@ import { EmptyState } from "../EmptyState"; import { Button } from "../Button"; import { Callout } from "../Callout"; -export type DataTableColumn = - MRT_ColumnDef & { - /** - * @deprecated use hasTextWrapping instead of enableWrapping - */ - enableWrapping?: boolean; - hasTextWrapping?: boolean; - }; - -type DataTableColumnInstance = Omit< - MRT_Column, +export type DataTableColumn = MRT_ColumnDef & { + /** + * @deprecated use hasTextWrapping instead of enableWrapping + */ + enableWrapping?: boolean; + hasTextWrapping?: boolean; +}; + +type DataTableColumnInstance = Omit< + MRT_Column, "columnDef" > & { - columnDef: DataTableColumn; + columnDef: DataTableColumn; }; -type DataTableCell = Omit< - MRT_Cell, - "column" -> & { - column: DataTableColumnInstance; +type DataTableCell = Omit, "column"> & { + column: DataTableColumnInstance; }; -export type DataTableGetDataType = { +export type DataTableGetDataType = { page?: number; resultsPerPage?: number; search?: string; - filters?: DataFilter[]; + filters?: DataFilter[]; sort?: MRT_SortingState; }; @@ -106,7 +101,7 @@ export type DataTableRenderDetailPanelType = { table: MRT_TableInstance; }; -export type DataTableProps = { +export type DataTableProps = { /** * An optional action button above the table. */ @@ -124,7 +119,7 @@ export type DataTableProps = { /** * The columns that make up the table */ - columns: DataTableColumn[]; + columns: DataTableColumn[]; /** * The current page number. */ @@ -140,11 +135,11 @@ export type DataTableProps = { /** * An optional set of filters to render in the filters menu */ - filters?: Array | DataTableColumn | string>; + filters?: Array | string>; /** * The function to get the ID of a row */ - getRowId?: MRT_TableOptions["getRowId"]; + getRowId?: MRT_TableOptions["getRowId"]; /** * Callback that fires whenever the table needs to fetch new data, due to changes in * page, results per page, search input, filters, or sorting @@ -155,9 +150,9 @@ export type DataTableProps = { search, filters, sort, - }: DataTableGetDataType) => - | MRT_TableOptions["data"] - | Promise["data"]>; + }: DataTableGetDataType) => + | MRT_TableOptions["data"] + | Promise["data"]>; /** * If true, the end user can resize individual columns. */ @@ -224,15 +219,15 @@ export type DataTableProps = { /** * The optional component to display when expanding a row. */ - renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; + renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; /** * Action buttons to display in each row */ - rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; + rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; /** * Menu items to include in the optional actions menu on each row. */ - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; /** * The debounce time, in milliseconds, for the search input firing * `onChangeSearch` when changed. If `hasSearchSubmitButton` is true, @@ -326,7 +321,7 @@ const ScrollableTableContainer = styled("div", { }), ); -const DataTable = ({ +const DataTable = ({ additionalActionButton, additionalActionMenuItems, bulkActionMenuItems, @@ -362,15 +357,16 @@ const DataTable = ({ rowActionMenuItems, searchDelayTime, totalRows, -}: DataTableProps) => { +}: DataTableProps) => { const { t } = useTranslation(); - const [data, setData] = useState([]); + const [data, setData] = useState([]); const [pagination, setPagination] = useState({ pageIndex: currentPage, pageSize: resultsPerPage, }); - const [draggingRow, setDraggingRow] = useState | null>(); + const [draggingRow, setDraggingRow] = + useState | null>(); const [isTableContainerScrolledToStart, setIsTableContainerScrolledToStart] = useState(true); const [isTableContainerScrolledToEnd, setIsTableContainerScrolledToEnd] = @@ -389,8 +385,8 @@ const DataTable = ({ useState(initialDensity); const [rowSelection, setRowSelection] = useState({}); const [search, setSearch] = useState(initialSearchValue); - const [filters, setFilters] = useState[]>(); - const [initialFilters, setInitialFilters] = useState[]>(); + const [filters, setFilters] = useState(); + const [initialFilters, setInitialFilters] = useState(); const [isLoading, setIsLoading] = useState(true); const [isEmpty, setIsEmpty] = useState(); const [errorMessage, setErrorMessage] = useState( @@ -427,7 +423,9 @@ const DataTable = ({ page: pagination.pageIndex, }); - const getRowId = getRowIdProp ? getRowIdProp : (row: TData) => row.id; + const getRowId = getRowIdProp + ? getRowIdProp + : (row: DataTableRowData) => row.id; const rowDensityClassName = useMemo(() => { return rowDensity === "spacious" @@ -438,7 +436,7 @@ const DataTable = ({ }, [rowDensity]); const renderRowActions = useCallback( - ({ row }: { row: MRT_Row }) => { + ({ row }: { row: MRT_Row }) => { const currentIndex = row.index + (pagination.pageIndex - 1) * pagination.pageSize; return ( @@ -470,7 +468,7 @@ const DataTable = ({ * filterOptions format, which allows for strings and { label: string, value: string } */ const convertFilterSelectOptions = useCallback( - (options: DataTableColumn["filterSelectOptions"]) => + (options: DataTableColumn["filterSelectOptions"]) => options?.map((option) => typeof option === "string" ? { @@ -488,16 +486,15 @@ const DataTable = ({ ); const convertColumnToFilter = useCallback( - (column: DataTableColumn) => + (column: DataTableColumn) => column.enableColumnFilter !== false && column.accessorKey - ? { + ? ({ id: column.accessorKey, label: column.header, variant: column.filterVariant, options: convertFilterSelectOptions(column.filterSelectOptions), - } - : // } satisfies DataFilter as DataFilter) - null, + } satisfies DataFilter as DataFilter) + : null, [convertFilterSelectOptions], ); @@ -508,7 +505,7 @@ const DataTable = ({ */ const dataTableFilters = useMemo(() => { const providedFilters = filtersProp || columns; - return providedFilters.reduce[]>((accumulator, item) => { + return providedFilters.reduce((accumulator, item) => { if (typeof item === "string") { const foundColumn = columns.find( (column) => column.accessorKey === item, @@ -535,7 +532,7 @@ const DataTable = ({ }, [columns, filtersProp, convertColumnToFilter]); const defaultCell = useCallback( - ({ cell }: { cell: DataTableCell }) => { + ({ cell }: { cell: DataTableCell }) => { const value = cell.getValue(); const hasTextWrapping = cell.column.columnDef.hasTextWrapping || @@ -748,7 +745,7 @@ const DataTable = ({ // Row actions enableRowActions: shouldDisplayRowActions, positionActionsColumn: - "last" as MRT_TableOptions["positionActionsColumn"], + "last" as MRT_TableOptions["positionActionsColumn"], renderRowActions: ({ row }) => renderRowActions({ row }), // Row selection diff --git a/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx b/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx index 9a59d81bc4..2e80e6407f 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTableRowActions.tsx @@ -26,14 +26,14 @@ import { import { DataTableProps } from "./DataTable"; import { Trans, useTranslation } from "react-i18next"; -export type DataTableRowActionsProps = { +export type DataTableRowActionsProps = { row: MRT_Row | MRT_RowData; rowIndex: number; rowActionButtons?: ( row: MRT_RowData, ) => ReactElement; rowActionMenuItems?: (row: MRT_RowData) => MenuButtonProps["children"]; - totalRows?: DataTableProps["totalRows"]; + totalRows?: DataTableProps["totalRows"]; updateRowOrder?: ({ rowId, newRowIndex, @@ -43,14 +43,14 @@ export type DataTableRowActionsProps = { }) => void; }; -const DataTableRowActions = ({ +const DataTableRowActions = ({ row, rowIndex, rowActionButtons, rowActionMenuItems, totalRows, updateRowOrder, -}: DataTableRowActionsProps) => { +}: DataTableRowActionsProps) => { const { t } = useTranslation(); const handleToFrontClick = useCallback(() => { diff --git a/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx b/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx index 31458b2b5e..2da2769c6a 100644 --- a/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx +++ b/packages/odyssey-react-mui/src/DataTable/DataTableSettings.tsx @@ -17,28 +17,22 @@ import { MenuItem } from "../MenuItem"; import { ListIcon, ShowIcon } from "../icons.generated"; import { densityValues } from "./constants"; import { DataTableProps } from "./DataTable"; -import { MRT_RowData, MRT_VisibilityState } from "material-react-table"; +import { MRT_VisibilityState } from "material-react-table"; import { useTranslation } from "react-i18next"; -export type DataTableSettingsProps = { - hasChangeableDensity: DataTableProps["hasChangeableDensity"]; +export type DataTableSettingsProps = { + hasChangeableDensity: DataTableProps["hasChangeableDensity"]; rowDensity: (typeof densityValues)[number]; setRowDensity: Dispatch>; - hasColumnVisibility: DataTableProps["hasColumnVisibility"]; - columns: DataTableProps["columns"]; + hasColumnVisibility: DataTableProps["hasColumnVisibility"]; + columns: DataTableProps["columns"]; columnVisibility?: MRT_VisibilityState; setColumnVisibility: Dispatch< SetStateAction >; }; -type DataTableSettingsComponent = (( - props: DataTableSettingsProps, -) => JSX.Element) & { - displayName?: string; -}; - -const DataTableSettings = ({ +const DataTableSettings = ({ hasChangeableDensity, rowDensity, setRowDensity, @@ -46,7 +40,7 @@ const DataTableSettings = ({ columns, columnVisibility, setColumnVisibility, -}: DataTableSettingsProps) => { +}: DataTableSettingsProps) => { const { t } = useTranslation(); const changeRowDensity = useCallback( @@ -137,9 +131,7 @@ const DataTableSettings = ({ ); }; -const MemoizedDataTableSettings = memo( - DataTableSettings, -) as DataTableSettingsComponent; +const MemoizedDataTableSettings = memo(DataTableSettings); MemoizedDataTableSettings.displayName = "DataTableSettings"; export { MemoizedDataTableSettings as DataTableSettings }; diff --git a/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx b/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx index 36a664e44c..3c01e4d4f1 100644 --- a/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx +++ b/packages/odyssey-react-mui/src/DataTable/useRowReordering.tsx @@ -26,8 +26,8 @@ export const useRowReordering = ({ resultsPerPage, page, }: { - totalRows: DataTableProps["totalRows"]; - onReorderRows: DataTableProps["onReorderRows"]; + totalRows: DataTableProps["totalRows"]; + onReorderRows: DataTableProps["onReorderRows"]; data: TData[]; setData: Dispatch>; draggingRow?: MRT_Row | null; diff --git a/packages/odyssey-react-mui/src/labs/DataFilters.tsx b/packages/odyssey-react-mui/src/labs/DataFilters.tsx index 0b9deb7c4b..62e81d1647 100644 --- a/packages/odyssey-react-mui/src/labs/DataFilters.tsx +++ b/packages/odyssey-react-mui/src/labs/DataFilters.tsx @@ -82,12 +82,12 @@ export type UpdateFiltersOrValues = ({ }) => void; // This is the shape of each individual filter -export type DataFilter = { +export type DataFilter = { /** * A unique ID for the filter, typically the same id * as the column it'll be applied to. */ - id: Exclude["accessorKey"], undefined>; + id: Exclude["accessorKey"], undefined>; /** * `Autocomplete` normally only allows values that exist in the list box. This feature allows you to enter in any value in the text field and have that be the stored value in `Autocomplete` * @@ -102,7 +102,7 @@ export type DataFilter = { * The type of filter, which determines which filtering control * is shown. */ - variant?: MRT_ColumnDef["filterVariant"]; + variant?: MRT_ColumnDef["filterVariant"]; /** * The current value of the filter. Typically a string, but * filters that allow for multiple selections (such as multi-select) @@ -121,7 +121,7 @@ export type DataFilter = { }; // This is the type of the DataFilters component itself -export type DataFiltersProps = { +export type DataFiltersProps = { /** * The callback that's fired when the search input changes * (either on change or on submit, based on the value of `hasSearchSubmitButton`). @@ -131,7 +131,7 @@ export type DataFiltersProps = { /** * The callback that's fired when filter values change. */ - onChangeFilters?: (filters: Array>) => void; + onChangeFilters?: (filters: Array) => void; /** * If true, a Search button will be provided alongside the search input * and `onChangeSearch` will fire when the button is clicked, rather than @@ -157,42 +157,30 @@ export type DataFiltersProps = { * The filters available in the filter menu. If undefined, * the filter menu won't be shown. */ - filters?: Array>; + filters?: Array; /** * If true, the filter and search will be disabled */ isDisabled?: boolean; }; -type DataFiltersComponent = (( - props: DataFiltersProps, -) => JSX.Element) & { - displayName?: string; -}; - -type FilterTagsProps = { - activeFilters: DataFilter[]; +type FilterTagsProps = { + activeFilters: DataFilter[]; updateFilterAndInputValues: UpdateFiltersOrValues; }; -type FilterTagsComponent = (( - props: FilterTagsProps, -) => JSX.Element) & { - displayName?: string; -}; - type FiltersToRender = { id: string; label: string; value: string; }; -const FilterTags: FilterTagsComponent = ({ +const FilterTags = ({ activeFilters, updateFilterAndInputValues, -}: FilterTagsProps) => { +}: FilterTagsProps) => { const filtersWithValues = activeFilters.filter( - (activeFilter: DataFilter) => activeFilter.value, + (activeFilter: DataFilter) => activeFilter.value, ); const filtersToRender: FiltersToRender[] = []; @@ -264,10 +252,10 @@ const FilterTags: FilterTagsComponent = ({ ); }; -const MemoizedFilterTags = memo(FilterTags) as FilterTagsComponent; +const MemoizedFilterTags = memo(FilterTags); MemoizedFilterTags.displayName = "FilterTags"; -const DataFilters = ({ +const DataFilters = ({ onChangeSearch, onChangeFilters, hasSearchSubmitButton = false, @@ -276,8 +264,8 @@ const DataFilters = ({ additionalActions, filters: filtersProp = [], isDisabled, -}: DataFiltersProps) => { - const [filters, setFilters] = useState[]>(filtersProp); +}: DataFiltersProps) => { + const [filters, setFilters] = useState(filtersProp); const { t } = useTranslation(); const odysseyDesignTokens = useOdysseyDesignTokens(); @@ -313,7 +301,7 @@ const DataFilters = ({ >(); const [filterPopoverCurrentFilter, setFilterPopoverCurrentFilter] = useState< - DataFilter | undefined + DataFilter | undefined >(); const menuRef = useRef(); @@ -873,7 +861,7 @@ const DataFilters = ({ ); }; -const MemoizedDataFilters = memo(DataFilters) as DataFiltersComponent; +const MemoizedDataFilters = memo(DataFilters); MemoizedDataFilters.displayName = "DataFilters"; export { MemoizedDataFilters as DataFilters }; diff --git a/packages/odyssey-react-mui/src/labs/DataTable.tsx b/packages/odyssey-react-mui/src/labs/DataTable.tsx index bf07cc3de9..9ae400785e 100644 --- a/packages/odyssey-react-mui/src/labs/DataTable.tsx +++ b/packages/odyssey-react-mui/src/labs/DataTable.tsx @@ -217,7 +217,7 @@ export type DataTableProps = { page?: number; resultsPerPage?: number; search?: string; - filters?: DataFilter[]; + filters?: DataFilter[]; sort?: MRT_SortingState; }) => MRT_TableOptions["data"]; /** @@ -334,7 +334,7 @@ const DataTable = ({ ); const [globalFilter, setGlobalFilter] = useState(""); - const [filters, setFilters] = useState>>(); + const [filters, setFilters] = useState>(); useEffect(() => { setShowSkeletons(false); @@ -388,12 +388,9 @@ const DataTable = ({ setGlobalFilter(value); }, []); - const handleFilters = useCallback( - (updatedFilters: Array>) => { - setFilters(updatedFilters); - }, - [], - ); + const handleFilters = useCallback((updatedFilters: Array) => { + setFilters(updatedFilters); + }, []); const handleRowSelectionChange = useCallback( (updater: MRT_Updater) => { diff --git a/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts b/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts index 280e8818eb..e2366028e7 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/componentTypes.ts @@ -27,11 +27,11 @@ import { DataRowSelectionState, DataTableColumn, } from "./dataTypes"; -import { DataTableRowActionsProps } from "../../DataTable/DataTableRowActions"; import { MenuButtonProps } from "../.."; import { paginationTypeValues } from "../DataTablePagination"; import { ReactNode } from "react"; import { DataCardProps } from "./DataCard"; +import { RowActionsProps } from "./RowActions"; export type DataLayout = (typeof availableLayouts)[number]; export type CardLayout = (typeof availableCardLayouts)[number]; @@ -49,14 +49,14 @@ export type UniversalProps = { emptyPlaceholder?: ReactNode; enableVirtualization?: boolean; errorMessage?: string; - filters?: Array | DataTableColumn | string>; + filters?: Array | string>; getData: ({ page, resultsPerPage, search, filters, sort, - }: DataGetDataType) => TData[] | Promise; + }: DataGetDataType) => TData[] | Promise; getRowId?: MRT_TableOptions["getRowId"]; hasFilters?: boolean; hasPagination?: boolean; @@ -100,15 +100,15 @@ export type TableLayoutProps = { hasSorting?: boolean; initialDensity?: MRT_DensityState; renderDetailPanel?: MRT_TableOptions["renderDetailPanel"]; - rowActionButtons?: DataTableRowActionsProps["rowActionButtons"]; - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + rowActionButtons?: RowActionsProps["rowActionButtons"]; + rowActionMenuItems?: RowActionsProps["rowActionMenuItems"]; }; export type CardLayoutProps = { itemProps: (row: TData) => Omit, "row">; maxGridColumns?: number; renderDetailPanel?: (props: { row: TData }) => ReactNode; - rowActionMenuItems?: DataTableRowActionsProps["rowActionMenuItems"]; + rowActionMenuItems?: RowActionsProps["rowActionMenuItems"]; }; export type ViewProps = { diff --git a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts index 73f0f940ac..fcaae8f9e1 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts @@ -21,8 +21,8 @@ import { import { DataFilter } from "../DataFilters"; -export type DataQueryParamsType = { - filters?: DataFilter[]; +export type DataQueryParamsType = { + filters?: DataFilter[]; page?: number; resultsPerPage?: number; search?: string; @@ -52,12 +52,12 @@ export type DataTableCell = Omit< column: DataTableColumnInstance; }; -export type DataColumns = DataTableColumn[]; +export type DataColumns = DataTableColumn[]; export type DataRow = MRT_RowData; -export type DataGetDataType = { - filters?: DataFilter[]; +export type DataGetDataType = { + filters?: DataFilter[]; page?: number; resultsPerPage?: number; search?: string; @@ -72,8 +72,7 @@ export type DataOnReorderRowsType = { export type DataRowSelectionState = MRT_RowSelectionState; // Provided for backwards compatibilty with old DataTable types -export type DataTableGetDataType = - DataGetDataType; +export type DataTableGetDataType = DataGetDataType; export type DataTableOnReorderRowsType = DataOnReorderRowsType; export type DataTableRowSelectionState = DataRowSelectionState; export type DataTableRow = DataRow; diff --git a/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts b/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts index bb20d47777..b48bc848ae 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/fetchData.ts @@ -18,7 +18,7 @@ import { DataQueryParamsType } from "./dataTypes"; import { UniversalProps } from "./componentTypes"; type DataRequestType = { - dataQueryParams: DataQueryParamsType; + dataQueryParams: DataQueryParamsType; errorMessageProp: UniversalProps["errorMessage"]; getData: UniversalProps["getData"]; setData: Dispatch>; diff --git a/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts b/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts index 819a83dde7..ff897eedf0 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/useFilterConversion.ts @@ -62,7 +62,7 @@ export const useFilterConversion = ({ // If not, they may be inferred from `columns` const providedFilters = filters || columns || []; - return providedFilters.reduce[]>((accumulator, item) => { + return providedFilters.reduce((accumulator, item) => { if (typeof item === "string") { const foundColumn = columns?.find( (column) => column.accessorKey === item, diff --git a/packages/odyssey-storybook/src/components/odyssey-labs/DataTable/DataTable.stories.tsx b/packages/odyssey-storybook/src/components/odyssey-labs/DataTable/DataTable.stories.tsx index d48b2d642d..f748b48391 100644 --- a/packages/odyssey-storybook/src/components/odyssey-labs/DataTable/DataTable.stories.tsx +++ b/packages/odyssey-storybook/src/components/odyssey-labs/DataTable/DataTable.stories.tsx @@ -39,10 +39,10 @@ import { paginationTypeValues, } from "@okta/odyssey-react-mui"; -type DataTableMetaProps = DataViewProps & - TableLayoutProps & { - tableRowActionMenuItems: TableLayoutProps["rowActionMenuItems"]; - listRowActionMenuItems: CardLayoutProps["rowActionMenuItems"]; +type DataTableMetaProps = DataViewProps & + TableLayoutProps & { + tableRowActionMenuItems: TableLayoutProps["rowActionMenuItems"]; + listRowActionMenuItems: CardLayoutProps["rowActionMenuItems"]; hasCustomEmptyPlaceholder: boolean; hasCustomNoResultsPlaceholder: boolean; hasActionMenuItems: boolean; diff --git a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/DataView.stories.tsx b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/DataView.stories.tsx index a35f7471b4..35ecd4651a 100644 --- a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/DataView.stories.tsx +++ b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/DataView.stories.tsx @@ -52,11 +52,11 @@ import { DataTableRowData, } from "@okta/odyssey-react-mui"; -type DataViewMetaProps = DataViewProps & - TableLayoutProps & - CardLayoutProps & { - tableRowActionMenuItems: TableLayoutProps["rowActionMenuItems"]; - cardRowActionMenuItems: CardLayoutProps["rowActionMenuItems"]; +type DataViewMetaProps = DataViewProps & + TableLayoutProps & + CardLayoutProps & { + tableRowActionMenuItems: TableLayoutProps["rowActionMenuItems"]; + cardRowActionMenuItems: CardLayoutProps["rowActionMenuItems"]; hasCustomEmptyPlaceholder: boolean; hasCustomNoResultsPlaceholder: boolean; hasActionMenuItems: boolean; @@ -920,7 +920,7 @@ export const AdditionalActions: StoryObj = { export const ColumnGrowDemo: StoryObj = { render: function C() { const columns = useMemo( - (): DataColumns => [ + (): DataColumns => [ { accessorKey: "name", header: "Name", @@ -1016,11 +1016,11 @@ export const PaginationHook: StoryObj = { }, }; -const stackItemProps = (row: DataRow) => ({ +const stackItemProps = (row: Person) => ({ overline: "Overline", title: row.name, description: `${row.name} is ${row.age} years old.`, - variant: "stack" as DataCardProps["variant"], + variant: "stack" as DataCardProps["variant"], image: Logo, }); @@ -1043,10 +1043,10 @@ export const StackCards: StoryObj = { }, }; -const compactItemProps = (row: DataRow) => ({ +const compactItemProps = (row: Person) => ({ title: row.name, description: `${row.name} is ${row.age} years old.`, - variant: "compact" as DataCardProps["variant"], + variant: "compact" as DataCardProps["variant"], image: Logo, }); @@ -1075,7 +1075,7 @@ export const GrowColumnWithoutActions: StoryObj = { const [data, setData] = useState(personData); const { getData } = useDataCallbacks(data, setData); - const columns: DataColumns = [ + const columns: DataColumns = [ { accessorKey: "order", header: "ID", @@ -1110,7 +1110,7 @@ export const GrowColumnWithActions: StoryObj = { const [data, setData] = useState(personData); const { getData } = useDataCallbacks(data, setData); - const columns: DataColumns = [ + const columns: DataColumns = [ { accessorKey: "order", header: "ID", diff --git a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts index 292c2da702..99a848ce02 100644 --- a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts +++ b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/dataFunctions.ts @@ -18,7 +18,7 @@ export const filterData = ({ ...args }: { data: Person[]; -} & DataGetDataType) => { +} & DataGetDataType) => { const { search, filters, sort, page = 1, resultsPerPage = 20 } = args; const searchFiltered = search @@ -45,7 +45,7 @@ export const filterData = ({ const handleStartLetterFilter = ( row: Person, - value: DataFilter["value"], + value: DataFilter["value"], ): boolean => { if (typeof row.name !== "string") return true; const firstLetter = row.name[0]?.toLowerCase(); @@ -57,7 +57,7 @@ export const filterData = ({ const handleStandardFilter = ( row: Person, id: keyof Person, - value: DataFilter["value"], + value: DataFilter["value"], ): boolean => { const rowValue = String(row[id]).toLowerCase(); @@ -98,7 +98,7 @@ export const filterData = ({ // for the provided sample data and demo filters. const columnFiltered = filters ? searchFiltered.filter((row) => - filters.every(({ id, value }: DataFilter) => { + filters.every(({ id, value }: DataFilter) => { // If the filter value is null, return all the data if (value === null || value === undefined) { return true; diff --git a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/personData.tsx b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/personData.tsx index 31cfaf9498..e29b01c48b 100644 --- a/packages/odyssey-storybook/src/components/odyssey-labs/DataView/personData.tsx +++ b/packages/odyssey-storybook/src/components/odyssey-labs/DataView/personData.tsx @@ -23,7 +23,7 @@ export type Person = { risk: "high" | "medium" | "low"; }; -export const columns: DataColumns = [ +export const columns: DataColumns = [ { accessorKey: "order", header: "ID", From 07c4efb7f1fae854cc381a70531e943b5bbce546 Mon Sep 17 00:00:00 2001 From: Jordan Koschei Date: Tue, 19 Nov 2024 14:54:39 -0500 Subject: [PATCH 3/4] fix: add proper TValue generic to DataColumns --- .../src/labs/DataView/dataTypes.ts | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts index fcaae8f9e1..67171a473b 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts +++ b/packages/odyssey-react-mui/src/labs/DataView/dataTypes.ts @@ -29,30 +29,35 @@ export type DataQueryParamsType = { sort?: MRT_SortingState; }; -export type DataTableColumn = - MRT_ColumnDef & { - /** - * @deprecated use hasTextWrapping instead of enableWrapping - */ - enableWrapping?: boolean; - hasTextWrapping?: boolean; - }; +export type DataTableColumn< + TData extends MRT_RowData, + TValue = unknown, +> = MRT_ColumnDef & { + /** + * @deprecated use hasTextWrapping instead of enableWrapping + */ + enableWrapping?: boolean; + hasTextWrapping?: boolean; +}; -export type DataTableColumnInstance = Omit< - MRT_Column, +export type DataTableColumnInstance = Omit< + MRT_Column, "columnDef" > & { - columnDef: DataTableColumn; + columnDef: DataTableColumn; }; -export type DataTableCell = Omit< +export type DataTableCell = Omit< MRT_Cell, "column" > & { - column: DataTableColumnInstance; + column: DataTableColumnInstance; }; -export type DataColumns = DataTableColumn[]; +export type DataColumns< + TData extends MRT_RowData, + TValue = unknown, +> = DataTableColumn[]; export type DataRow = MRT_RowData; From 8908f33c70b4a7a44ed8574729e95dbea5b24b2a Mon Sep 17 00:00:00 2001 From: Jordan Koschei Date: Tue, 19 Nov 2024 19:51:03 -0500 Subject: [PATCH 4/4] fix: update broken type --- .../odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx b/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx index 245111ff34..125e459b9f 100644 --- a/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx +++ b/packages/odyssey-react-mui/src/labs/DataView/TableLayoutContent.tsx @@ -202,7 +202,7 @@ const TableLayoutContent = ({ }, [tableState]); const defaultCell = useCallback< - ({ cell }: { cell: DataTableCell }) => ReactElement | string + ({ cell }: { cell: DataTableCell }) => ReactElement | string >(({ cell }) => { const value = cell.getValue(); const hasTextWrapping =