From 19bcfae31e2b025883f27989efa658a5abfe0112 Mon Sep 17 00:00:00 2001 From: Douglas Egiemeh Date: Fri, 17 May 2024 14:34:37 +0200 Subject: [PATCH] Decoupling data table manager components (#2807) * feat(data-table-manager): managing state using window state - poc * feat(data-table-manager): update data row * feat(data-table-manager): fix state update async behaviour * feat(data-table-manager): rertore story to original order * feat(data-table-manager): add changeset * feat(data-table-manager): use provider method * feat(data-table-manager): use proper import * feat(data-table-manager): remove unnecesary prop * feat(data table manager): rename data table manager provider * feat(data table manager): add datatable manager as data table dependency * feat(data table manager): add tests for when data table is used in decoupled approach * feat(data table manager): proper function initial values * feat(data table manager): move data table context to separate file * feat(data table manager): update usage example * feat(data table manager): update data table import * feat(data table manager): resolve conflict * feat(data table manager): update tests * feat(data table manager): reuse exported types * feat(data table manager): reuse exported types * feat(data table manager): introduce story for when data table is decoupled * feat(data table manager): add documentation when datatable is decoupled * feat(data table manager): remove children prop from decoupled data table manager * feat(data table manager): remove children prop from decoupled data table manager * feat(data table manager): update package * feat(data table manager): update package import path * feat(data table manager): refactor data table provider * feat(data table manager): refactor data table manager to move all logic to context * refactor(data table manager): refactor data table types location * feat(data table manager): throw error to notify users of provider alternative * feat(data table manager): remove unused file * refactor(data table manager): refactor manager provider * feat(data table manager): update readme * feat(selectable search input): update test * feat(data table manager): update readme * feat(data table manager): update types import * feat(data table manager): update usage example --------- Co-authored-by: Ddouglasz --- .changeset/shy-goats-obey.md | 6 + .../components/data-table-manager/README.md | 63 ++++- .../data-table-manager/_docs/description.md | 1 + .../data-table-manager/_docs/usage-example.js | 28 ++- .../data-table-manager-provider/package.json | 4 + .../data-table-manager/{index.js => index.ts} | 0 .../data-table-manager/package.json | 5 +- .../data-table-manager-provider.tsx | 72 ++++++ .../src/data-table-manager-provider/index.ts | 6 + .../src/data-table-manager-provider/types.tsx | 91 +++++++ .../src/data-table-manager.spec.js | 159 +++++++++++- .../src/data-table-manager.story.js | 163 ++++++++++++ .../src/data-table-manager.tsx | 196 +++------------ .../data-table-settings.tsx | 99 +------- .../src/data-table-settings/index.ts | 6 +- .../data-table-manager/src/export-types.ts | 9 +- .../data-table-manager/src/index.ts | 4 + .../data-table-manager/src/types.tsx | 233 ++++++++++++++++++ packages/components/data-table/package.json | 1 + .../components/data-table/src/data-table.tsx | 28 ++- yarn.lock | 1 + 21 files changed, 884 insertions(+), 291 deletions(-) create mode 100644 .changeset/shy-goats-obey.md create mode 100644 packages/components/data-table-manager/data-table-manager-provider/package.json rename packages/components/data-table-manager/{index.js => index.ts} (100%) create mode 100644 packages/components/data-table-manager/src/data-table-manager-provider/data-table-manager-provider.tsx create mode 100644 packages/components/data-table-manager/src/data-table-manager-provider/index.ts create mode 100644 packages/components/data-table-manager/src/data-table-manager-provider/types.tsx create mode 100644 packages/components/data-table-manager/src/types.tsx diff --git a/.changeset/shy-goats-obey.md b/.changeset/shy-goats-obey.md new file mode 100644 index 0000000000..acb44709b4 --- /dev/null +++ b/.changeset/shy-goats-obey.md @@ -0,0 +1,6 @@ +--- +'@commercetools-uikit/data-table-manager': minor +'@commercetools-uikit/data-table': minor +--- + +decouple the datatable manager from the data table to enable users position the data table manager settings dropdown anywhere in the DOM structure. diff --git a/packages/components/data-table-manager/README.md b/packages/components/data-table-manager/README.md index 6817f3b52a..a7554be55e 100644 --- a/packages/components/data-table-manager/README.md +++ b/packages/components/data-table-manager/README.md @@ -5,9 +5,6 @@ ## Description -> THIS COMPONENT IS IN BETA! -> Please be aware that it may be subject to upcoming breaking changes as it's still in active development. - This component enhances the `` component and additionally provides a UI and state management to handle configuration of the table such as column manager. - The `disableDisplaySettings` enables / disables the layout settings panel, allowing the user to select wrapping text and density display options. @@ -15,6 +12,8 @@ This component enhances the `` component and additionally provides a Both panels delegate the handling of the settings change on the parent through function properties, allowing the settings to be persisted or just used as state props. +This component will render a triggering element (icon button) above the `` (top-right corner) which users can click to select the panel to open. + ## Installation ``` @@ -37,6 +36,12 @@ npm --save install react react-dom react-intl ## Usage +There are two ways this component can be used: + +### Attached + +The basic usage is when it just wraps the DataTable component. + ```jsx import DataTableManager from '@commercetools-uikit/data-table-manager'; import DataTable from '@commercetools-uikit/data-table'; @@ -61,10 +66,54 @@ const Example = () => ( export default Example; ``` +### Detached + +As mentioned earlier, the default behaviour places the triggering element above the `` component (top-right corner), but there may be use cases where the triggering element needs to be positioned differently on the page. This is also possible but requires the usage of one more component (`DataTableManagerProvider`) in order to share the manager state between the manager panels and the `DataTable` component. + +In this mode, you should pass the manager props to the `DataTableManagerProvider` component and the `DataTableManager` does not need to receive any prop; it can be placed anywhere in the component's tree without requiring any prop. + +```jsx +import DataTableManager, { + DataTableManagerProvider, +} from '@commercetools-uikit/data-table-manager'; +import DataTable from '@commercetools-uikit/data-table'; +import SearchTextInput from '@commercetools-uikit/search-text-input'; + +const rows = [ + { id: 'parasite', title: 'Parasite', country: 'South Korea' }, + { id: 'portrait', title: 'Portrait of a Lady on Fire', country: 'France' }, + { id: 'wat', title: 'Woman at War', country: 'Iceland' }, +]; + +const columns = [ + { key: 'title', label: 'Title' }, + { key: 'country', label: 'Country' }, +]; + +const SomeOtherComponent = () => { + return
Some other component
; +}; + +const Example = () => ( + +
+ + +
+
+ + +
+
+); + +export default Example; +``` + ## Properties -| Props | Type | Required | Default | Description | -| ------------------------- | -------------------------------------------------------------- | :------: | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Props | Type | Required | Default | Description | +| ---------------------------------------------- | ----------------------------------------------------------- | :------: | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `columns` | `array` | ✅ | | Each object requires a unique `key` which should correspond to property key of the items of `rows` that you want to render under this column, and a `label` which defines the name shown on the header. The list of columns to be rendered. Each column can be customized (see properties below). | | `columns[].key` | `string` | ✅ | | The unique key of the column that is used to identify your data type. You can use this value to determine which value from a row item should be rendered.
For example, if the data is a list of users, where each user has a `firstName` property, the column key should be `firstName`, which renders the correct value by default. The key can also be some custom or computed value, in which case you need to provide an explicit mapping of the value by implementing either the `itemRendered` function or the column-specific `renderItem` function. | | `columns[].label` | `node` | ✅ | | The label of the column that will be shown on the column header. | @@ -91,10 +140,12 @@ export default Example; | `columnManager.hideableColumns[].key` | `string` | ✅ | | | | `columnManager.hideableColumns[].label` | `` | ✅ | | | | `columnManager.areHiddenColumnsSearchable` | `bool` | | | Set this to `true` to show a search input for the hidden columns panel. | -| `columnManager.searchHiddenColumns` | `func` | | | A callback function, called when the search input for the hidden columns panel changes.
Signature: `(searchTerm: string) => Promise | void` | +| `columnManager.searchHiddenColumns` | `func` | | | A callback function, called when the search input for the hidden columns panel changes.
Signature: `(searchTerm: string) => Promise | | `columnManager.searchHiddenColumnsPlaceholder` | `string` | | | Placeholder value of the search input for the hidden columns panel. | | `columnManager.primaryButton` | `element` | | | A React element to be rendered as the primary button, useful when the column settings work as a form. | | `columnManager.secondaryButton` | `element` | | | A React element to be rendered as the secondary button, useful when the column settings work as a form. | | `onSettingsChange` | `func` | | | A callback function, called when any of the properties of either display settings or column settings is modified.
Signature: `(action: string, nextValue: object) => void` | | `topBar` | `node` | | | A React node for rendering additional information within the table manager. | | `managerTheme` | `enum`
Possible values:
`'light', 'dark'` | | | Sets the background theme of the Card that contains the settings | + +> `*`: `DataTableManagerProvider` component accepts the same properties as the `DataTableManager` diff --git a/packages/components/data-table-manager/_docs/description.md b/packages/components/data-table-manager/_docs/description.md index e049283530..6cf5960ee3 100644 --- a/packages/components/data-table-manager/_docs/description.md +++ b/packages/components/data-table-manager/_docs/description.md @@ -5,5 +5,6 @@ This component enhances the `` component and additionally provides a - The `disableDisplaySettings` enables / disables the layout settings panel, allowing the user to select wrapping text and density display options. - The `disableColumnManager` enables / disables the column manager panel, allowing the user to select which columns are visible. +- To Detach the `DatatableManager` settings dropdown from the Datatable and position it anywhere else, you would need to import a `DataTableManagerProvider` and wrap both components with the provider. Both panels delegate the handling of the settings change on the parent through function properties, allowing the settings to be persisted or just used as state props. diff --git a/packages/components/data-table-manager/_docs/usage-example.js b/packages/components/data-table-manager/_docs/usage-example.js index f12768128d..c354f1ac94 100644 --- a/packages/components/data-table-manager/_docs/usage-example.js +++ b/packages/components/data-table-manager/_docs/usage-example.js @@ -1,5 +1,8 @@ -import DataTableManager from '@commercetools-uikit/data-table-manager'; +import DataTableManager, { + DataTableManagerProvider, +} from '@commercetools-uikit/data-table-manager'; import DataTable from '@commercetools-uikit/data-table'; +import SearchTextInput from '@commercetools-uikit/search-text-input'; const rows = [ { id: 'parasite', title: 'Parasite', country: 'South Korea' }, @@ -12,10 +15,29 @@ const columns = [ { key: 'country', label: 'Country' }, ]; -const Example = () => ( +export const Example = () => ( ); - export default Example; + +// Introduce this component to test that DataTable and DataTableManager should not necessarily be direct descendants +const SomeOtherComponent = () => { + return
Some other component
; +}; + +// With the data table settings decoupled. +// It can also be exported as default. +export const ExampleWithDecoupledDataTableManager = () => ( + +
+ + +
+
+ + +
+
+); diff --git a/packages/components/data-table-manager/data-table-manager-provider/package.json b/packages/components/data-table-manager/data-table-manager-provider/package.json new file mode 100644 index 0000000000..fa091512dc --- /dev/null +++ b/packages/components/data-table-manager/data-table-manager-provider/package.json @@ -0,0 +1,4 @@ +{ + "main": "dist/commercetools-uikit-data-table-manager-data-table-manager-provider.cjs.js", + "module": "dist/commercetools-uikit-data-table-manager-data-table-manager-provider.esm.js" +} diff --git a/packages/components/data-table-manager/index.js b/packages/components/data-table-manager/index.ts similarity index 100% rename from packages/components/data-table-manager/index.js rename to packages/components/data-table-manager/index.ts diff --git a/packages/components/data-table-manager/package.json b/packages/components/data-table-manager/package.json index 5313a99990..cb4eb4cc32 100644 --- a/packages/components/data-table-manager/package.json +++ b/packages/components/data-table-manager/package.json @@ -17,7 +17,10 @@ "sideEffects": false, "main": "dist/commercetools-uikit-data-table-manager.cjs.js", "module": "dist/commercetools-uikit-data-table-manager.esm.js", - "files": ["dist"], + "preconstruct": { + "entrypoints": ["./index.ts", "data-table-manager-provider/index.ts"] + }, + "files": ["dist", "data-table-manager-provider"], "dependencies": { "@babel/runtime": "^7.20.13", "@babel/runtime-corejs3": "^7.20.13", diff --git a/packages/components/data-table-manager/src/data-table-manager-provider/data-table-manager-provider.tsx b/packages/components/data-table-manager/src/data-table-manager-provider/data-table-manager-provider.tsx new file mode 100644 index 0000000000..d886a72f95 --- /dev/null +++ b/packages/components/data-table-manager/src/data-table-manager-provider/data-table-manager-provider.tsx @@ -0,0 +1,72 @@ +import { createContext, useContext, useMemo } from 'react'; +import type { TDataTableSettingsProps, TColumnManagerProps } from '../types'; +import type { TDataTableManagerColumnProps, TRow } from './types'; + +export type TDataTableManagerContext = + TDataTableSettingsProps & { + columns: TDataTableManagerColumnProps[]; + isCondensed?: boolean; + }; + +const DataTableManagerContext = createContext({ + columns: [], + displaySettings: undefined, + isCondensed: true, +}); + +export const useDataTableManagerContext = () => { + const dataTableManagerContext = useContext(DataTableManagerContext); + + if (!dataTableManagerContext) { + throw new Error( + 'ui-kit/DataTableManager: `useDataTableManagerContext` must be used within the DataTableManagerProvider.' + ); + } + + return dataTableManagerContext; +}; + +export const DataTableManagerProvider = ({ + children, + columns, + displaySettings, + topBar, + onSettingsChange, + columnManager, +}: { + children: React.ReactNode; + columns: TDataTableManagerColumnProps[]; + displaySettings: TDataTableSettingsProps['displaySettings']; + topBar: string; + onSettingsChange: () => void; + columnManager: TColumnManagerProps; +}) => { + const decoupledDataTableManagerContext = useMemo(() => { + const areDisplaySettingsEnabled = Boolean( + displaySettings && !displaySettings.disableDisplaySettings + ); + + const isWrappingText = + areDisplaySettingsEnabled && displaySettings!.isWrappingText; + + return { + columns: columns.map((column) => ({ + ...column, + isTruncated: areDisplaySettingsEnabled + ? isWrappingText + : column.isTruncated, + })), + displaySettings, + topBar, + onSettingsChange, + columnManager, + isCondensed: areDisplaySettingsEnabled && displaySettings!.isCondensed, + }; + }, [columns, displaySettings, topBar, onSettingsChange, columnManager]); + + return ( + + {children} + + ); +}; diff --git a/packages/components/data-table-manager/src/data-table-manager-provider/index.ts b/packages/components/data-table-manager/src/data-table-manager-provider/index.ts new file mode 100644 index 0000000000..232690efe7 --- /dev/null +++ b/packages/components/data-table-manager/src/data-table-manager-provider/index.ts @@ -0,0 +1,6 @@ +export { + DataTableManagerProvider, + useDataTableManagerContext, +} from './data-table-manager-provider'; + +export type { TDataTableManagerContext } from './data-table-manager-provider'; diff --git a/packages/components/data-table-manager/src/data-table-manager-provider/types.tsx b/packages/components/data-table-manager/src/data-table-manager-provider/types.tsx new file mode 100644 index 0000000000..e44cd6f8ab --- /dev/null +++ b/packages/components/data-table-manager/src/data-table-manager-provider/types.tsx @@ -0,0 +1,91 @@ +import { type ReactNode, type MouseEventHandler } from 'react'; + +export interface TRow { + id: string; +} + +export type TDataTableManagerColumnProps = { + /** + * The unique key of the column that is used to identify your data type. + * You can use this value to determine which value from a row item should be rendered. + *
+ * For example, if the data is a list of users, where each user has a `firstName` property, + * the column key should be `firstName`, which renders the correct value by default. + * The key can also be some custom or computed value, in which case you need to provide + * an explicit mapping of the value by implementing either the `itemRendered` function or + * the column-specific `renderItem` function. + */ + key: string; + + /** + * The label of the column that will be shown on the column header. + */ + label: ReactNode; + + /** + * Sets a width for this column. Accepts the same values as the ones specified for + * individual [grid-template-columns](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns). + *
+ * For example, using `minmax` pairs (e.g. `minmax(200px, 400px)`), a combinations of + * fraction values (`1fr`/`2fr`/etc), or fixed values such as `200px`. + * By default, the column grows according to the content and respecting the total table available width. + */ + width?: string; + + /** + * Use this to override the table's own `horizontalCellAlignment` prop for this specific column. + */ + align?: 'left' | 'center' | 'right'; + + /** + * A callback function, called when the header cell is clicked. + */ + onClick?: (event: MouseEventHandler) => void; + + /** + * A callback function to render the content of cells under this column, overriding + * the default `itemRenderer` prop of the table. + */ + renderItem?: (row: Row, isRowCollapsed: boolean) => ReactNode; + + /** + * Use this prop to place an `Icon` or `IconButton` on the left of the column label. + * It is advised to place these types of components through this prop instead of `label`, + * in order to properly position and align the elements. + * This is particularly useful for medium-sized icons which require more vertical space than the typography. + */ + headerIcon?: ReactNode; + + /** + * Set this to `true` to allow text content of this cell to be truncated with an ellipsis, + * instead of breaking into multiple lines. + *
+ * NOTE: when using this option, it is recommended to specify a `width` for the column, because + * if the table doesn't have enough space for all columns, it will start clipping the columns + * with _truncated_ content, and if no `width` is set (or the value is set `auto` -- the default) + * it can shrink until the column disappears completely. + * By enforcing a minimum width for these columns, the table will respect them and grow horizontally, + * adding scrollbars if needed. + */ + isTruncated?: boolean; + + /** + * Set this to `true` to show a sorting button, which calls `onSortChange` upon being clicked. + * You should enable this flag for every column you want to be able to sort. + * When at least one column is sortable, the table props `sortBy`, `sortDirection` and `onSortChange` should be provided. + */ + isSortable?: boolean; + + /** + * Set this to `true` to prevent this column from being manually resized by dragging + * the edge of the header with a mouse. + */ + disableResizing?: boolean; + + /** + * Set this to `true` to prevent click event propagation for this cell. + * You might want this if you need the column to have its own call-to-action or input while + * the row also has a defined `onRowClick`. + */ + shouldIgnoreRowClick?: boolean; +}; diff --git a/packages/components/data-table-manager/src/data-table-manager.spec.js b/packages/components/data-table-manager/src/data-table-manager.spec.js index b1f4baf74b..0e24940b35 100644 --- a/packages/components/data-table-manager/src/data-table-manager.spec.js +++ b/packages/components/data-table-manager/src/data-table-manager.spec.js @@ -1,18 +1,26 @@ import { useState } from 'react'; import { screen, render, fireEvent, within } from '../../../../test/test-utils'; import DataTableManager from './data-table-manager'; +import { + useDataTableManagerContext, + DataTableManagerProvider, +} from '@commercetools-uikit/data-table-manager/data-table-manager-provider'; import { UPDATE_ACTIONS } from './constants'; /* eslint-disable react/prop-types */ -const TestTable = (props) => ( -
-
    - {props.columns.map((column) => ( -
  • {column.label}
  • - ))} -
-
-); +const TestTable = (props) => { + const { columns } = useDataTableManagerContext(); + + return ( +
+
    + {(columns.length !== 0 ? columns : props.columns).map((column) => ( +
  • {column.label}
  • + ))} +
+
+ ); +}; const TestComponent = (props) => { const [isCondensed, setIsCondensed] = useState(false); @@ -41,8 +49,47 @@ const TestComponent = (props) => { ); }; -/* eslint-enable react/prop-types */ +// Introduce this component to test that DataTable and DataTableManager should not necessarily be direct descendants +const SomeOtherComponent = () => { + return
Some other component
; +}; + +const DetachedDatatableTestComponent = (props) => { + const [isCondensed, setIsCondensed] = useState(false); + const [isWrappingText, setIsWrappingText] = useState(false); + const tableSettingsChangeHandler = { + [UPDATE_ACTIONS.IS_TABLE_CONDENSED_UPDATE]: setIsCondensed, + [UPDATE_ACTIONS.IS_TABLE_WRAPPING_TEXT_UPDATE]: setIsWrappingText, + }; + + return ( + { + tableSettingsChangeHandler[action](nextValue); + }} + > +
+ +
+ + +
+ ); +}; + +/* eslint-enable react/prop-types */ const defaultColumns = [ { key: 'title', label: 'Title' }, { key: 'country', label: 'Country' }, @@ -150,3 +197,95 @@ describe('rendering', () => { ).toBeInTheDocument(); }); }); + +describe('rendering with detached data table', () => { + it('should not render the dropdown if no settings options are passed', async () => { + const props = createTestProps({ + displaySettings: undefined, + columnManager: undefined, + }); + render(); + + expect(screen.queryByText('Table layout settings')).not.toBeInTheDocument(); + expect(screen.queryByText('Column Manager')).not.toBeInTheDocument(); + expect( + screen.queryByLabelText('Open table manager dropdown') + ).not.toBeInTheDocument(); + }); + it('should render the layout settings panel when clicking on the dropdown option and interact with the layout options', async () => { + const props = createTestProps(); + render(); + + expect(screen.queryByText('Table layout settings')).not.toBeInTheDocument(); + expect(screen.queryByText('Column Manager')).not.toBeInTheDocument(); + + const selectDropdown = await screen.findByLabelText( + 'Open table manager dropdown' + ); + + fireEvent.focus(selectDropdown); + const layoutSettingsOption = await screen.findByLabelText( + 'Layout settings' + ); + fireEvent.click(layoutSettingsOption); + + await screen.findByText('Table layout settings'); + screen.getByText('Table layout settings'); + + const textPreviewsOption = screen.getByLabelText( + 'Select radio option: display full previews' + ); + const fullTextsOption = screen.getByLabelText( + 'Select radio option: display full text' + ); + const densityCompactOption = screen.getByLabelText( + 'Select radio option: density compact' + ); + const densityDefaultOption = screen.getByLabelText( + 'Select radio option: density default' + ); + + expect(fullTextsOption).toBeChecked(); + expect(densityDefaultOption).toBeChecked(); + + fireEvent.click(textPreviewsOption); + expect(textPreviewsOption).toBeChecked(); + + fireEvent.click(fullTextsOption); + expect(fullTextsOption).toBeChecked(); + + fireEvent.click(densityCompactOption); + expect(densityCompactOption).toBeChecked(); + + fireEvent.click(densityDefaultOption); + expect(densityDefaultOption).toBeChecked(); + }); + it('should render the column settings panel when clicking on the dropdown option with no column options in either hidden or visible panels', async () => { + const props = createTestProps(); + render(); + + expect(screen.queryByText('Table layout settings')).not.toBeInTheDocument(); + expect(screen.queryByText('Column Manager')).not.toBeInTheDocument(); + + const selectDropdown = await screen.findByLabelText( + 'Open table manager dropdown' + ); + + fireEvent.focus(selectDropdown); + const columnManagerOption = await screen.findByLabelText('Column manager'); + fireEvent.click(columnManagerOption); + + await screen.findByText('Column Manager'); + screen.getByLabelText('Hidden columns'); + expect( + screen.getByText('There are no hidden columns to show.') + ).toBeInTheDocument(); + const visibleColumnsContainer = screen.getByLabelText('Visible columns'); + expect( + within(visibleColumnsContainer).getByText('Title') + ).toBeInTheDocument(); + expect( + within(visibleColumnsContainer).getByText('Country') + ).toBeInTheDocument(); + }); +}); diff --git a/packages/components/data-table-manager/src/data-table-manager.story.js b/packages/components/data-table-manager/src/data-table-manager.story.js index c19cc604f3..fc34c15c9f 100644 --- a/packages/components/data-table-manager/src/data-table-manager.story.js +++ b/packages/components/data-table-manager/src/data-table-manager.story.js @@ -5,12 +5,15 @@ import withReadme from 'storybook-readme/with-readme'; import times from 'lodash/times'; import DataTable from '@commercetools-uikit/data-table'; import CheckboxInput from '@commercetools-uikit/checkbox-input'; +import SearchTextInput from '@commercetools-uikit/search-text-input'; import { useRowSelection, useSorting } from '@commercetools-uikit/hooks'; import PrimaryButton from '@commercetools-uikit/primary-button'; import SecondaryButton from '@commercetools-uikit/secondary-button'; import Readme from '../README.md'; import { UPDATE_ACTIONS } from './constants'; import DataTableManager from './data-table-manager'; +import { DataTableManagerProvider } from '@commercetools-uikit/data-table-manager/data-table-manager-provider'; +import Spacings from '@commercetools-uikit/spacings'; const items = [ { @@ -321,4 +324,164 @@ storiesOf('Components|DataTable', module)
); + }) + .add('DetachedDataTableManager', () => { + const [tableData, setTableData] = useState({ + columns: initialColumnsState, + visibleColumnKeys: initialVisibleColumns.map(({ key }) => key), + }); + + const [isCondensed, setIsCondensed] = useState(true); + const [isWrappingText, setIsWrappingText] = useState(false); + + const { + items: rows, + sortedBy, + sortDirection, + onSortChange, + } = useSorting(items); + + const withRowSelection = boolean('withRowSelection', true); + const showDisplaySettingsConfirmationButtons = boolean( + 'showDisplaySettingsConfirmationButtons', + false + ); + const showColumnManagerConfirmationButtons = boolean( + 'showColumnManagerConfirmationButtons', + false + ); + + const { + rows: rowsWithSelection, + toggleRow, + selectAllRows, + deselectAllRows, + getIsRowSelected, + getNumberOfSelectedRows, + } = useRowSelection('checkbox', rows); + + const countSelectedRows = getNumberOfSelectedRows(); + const isSelectColumnHeaderIndeterminate = + countSelectedRows > 0 && countSelectedRows < rowsWithSelection.length; + const handleSelectColumnHeaderChange = + countSelectedRows === 0 ? selectAllRows : deselectAllRows; + + const mappedColumns = tableData.columns.reduce( + (columns, column) => ({ + ...columns, + [column.key]: column, + }), + {} + ); + const visibleColumns = tableData.visibleColumnKeys.map( + (columnKey) => mappedColumns[columnKey] + ); + + const columnsWithSelect = [ + { + key: 'checkbox', + label: ( + + ), + shouldIgnoreRowClick: true, + align: 'center', + renderItem: (row) => ( + toggleRow(row.id)} + /> + ), + disableResizing: true, + }, + ...visibleColumns, + ]; + + const tableSettingsChangeHandler = { + [UPDATE_ACTIONS.COLUMNS_UPDATE]: (visibleColumnKeys) => + setTableData({ + ...tableData, + visibleColumnKeys, + }), + [UPDATE_ACTIONS.IS_TABLE_CONDENSED_UPDATE]: setIsCondensed, + [UPDATE_ACTIONS.IS_TABLE_WRAPPING_TEXT_UPDATE]: setIsWrappingText, + }; + + const displaySettingsButtons = showDisplaySettingsConfirmationButtons + ? { + primaryButton: , + secondaryButton: , + } + : {}; + + const columnManagerButtons = showColumnManagerConfirmationButtons + ? { + primaryButton: , + secondaryButton: , + } + : {}; + + const displaySettings = { + disableDisplaySettings: boolean('disableDisplaySettings', false), + isCondensed, + isWrappingText, + ...displaySettingsButtons, + }; + + const columnManager = { + areHiddenColumnsSearchable: boolean('areHiddenColumnsSearchable', true), + searchHiddenColumns: (searchTerm) => { + setTableData({ + ...tableData, + columns: initialColumnsState.filter( + (column) => + tableData.visibleColumnKeys.includes(column.key) || + column.label + .toLocaleLowerCase() + .includes(searchTerm.toLocaleLowerCase()) + ), + }); + }, + disableColumnManager: boolean('disableColumnManager', false), + visibleColumnKeys: tableData.visibleColumnKeys, + hideableColumns: tableData.columns, + ...columnManagerButtons, + }; + + return ( + { + tableSettingsChangeHandler[action](nextValue); + }} + columnManager={columnManager} + > + +
+ + + + +
+
+ +
+ +
+
+
+
+ ); }); diff --git a/packages/components/data-table-manager/src/data-table-manager.tsx b/packages/components/data-table-manager/src/data-table-manager.tsx index ffada4635e..7dc944f731 100644 --- a/packages/components/data-table-manager/src/data-table-manager.tsx +++ b/packages/components/data-table-manager/src/data-table-manager.tsx @@ -1,185 +1,63 @@ -import { - useMemo, - cloneElement, - type ReactElement, - type ReactNode, - type MouseEventHandler, -} from 'react'; +import { useMemo, cloneElement } from 'react'; import Spacings from '@commercetools-uikit/spacings'; -import DataTableSettings, { - type TDataTableSettingsProps, -} from './data-table-settings'; - -export interface TRow { - id: string; -} - -export type TColumnProps = { - /** - * The unique key of the column that is used to identify your data type. - * You can use this value to determine which value from a row item should be rendered. - *
- * For example, if the data is a list of users, where each user has a `firstName` property, - * the column key should be `firstName`, which renders the correct value by default. - * The key can also be some custom or computed value, in which case you need to provide - * an explicit mapping of the value by implementing either the `itemRendered` function or - * the column-specific `renderItem` function. - */ - key: string; - - /** - * The label of the column that will be shown on the column header. - */ - label: ReactNode; - - /** - * Sets a width for this column. Accepts the same values as the ones specified for - * individual [grid-template-columns](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns). - *
- * For example, using `minmax` pairs (e.g. `minmax(200px, 400px)`), a combinations of - * fraction values (`1fr`/`2fr`/etc), or fixed values such as `200px`. - * By default, the column grows according to the content and respecting the total table available width. - */ - width?: string; - - /** - * Use this to override the table's own `horizontalCellAlignment` prop for this specific column. - */ - align?: 'left' | 'center' | 'right'; - - /** - * A callback function, called when the header cell is clicked. - */ - onClick?: (event: MouseEventHandler) => void; - - /** - * A callback function to render the content of cells under this column, overriding - * the default `itemRenderer` prop of the table. - */ - renderItem?: (row: Row, isRowCollapsed: boolean) => ReactNode; - - /** - * Use this prop to place an `Icon` or `IconButton` on the left of the column label. - * It is advised to place these types of components through this prop instead of `label`, - * in order to properly position and align the elements. - * This is particularly useful for medium-sized icons which require more vertical space than the typography. - */ - headerIcon?: ReactNode; - - /** - * Set this to `true` to allow text content of this cell to be truncated with an ellipsis, - * instead of breaking into multiple lines. - *
- * NOTE: when using this option, it is recommended to specify a `width` for the column, because - * if the table doesn't have enough space for all columns, it will start clipping the columns - * with _truncated_ content, and if no `width` is set (or the value is set `auto` -- the default) - * it can shrink until the column disappears completely. - * By enforcing a minimum width for these columns, the table will respect them and grow horizontally, - * adding scrollbars if needed. - */ - isTruncated?: boolean; - - /** - * Set this to `true` to show a sorting button, which calls `onSortChange` upon being clicked. - * You should enable this flag for every column you want to be able to sort. - * When at least one column is sortable, the table props `sortBy`, `sortDirection` and `onSortChange` should be provided. - */ - isSortable?: boolean; - - /** - * Set this to `true` to prevent this column from being manually resized by dragging - * the edge of the header with a mouse. - */ - disableResizing?: boolean; - - /** - * Set this to `true` to prevent click event propagation for this cell. - * You might want this if you need the column to have its own call-to-action or input while - * the row also has a defined `onRowClick`. - */ - shouldIgnoreRowClick?: boolean; -}; - -type TDataTableManagerProps = { - /** - * Each object requires a unique `key` which should correspond to property key of - * the items of `rows` that you want to render under this column, and a `label` - * which defines the name shown on the header. - * The list of columns to be rendered. - * Each column can be customized (see properties below). - */ - columns: TColumnProps[]; - - /** - * Any React node. Usually you want to render the `` component. - *
- * Note that the child component will implicitly receive the props `columns` and `isCondensed` from the ``. - */ - children: ReactElement; - - /** - * The managed display settings of the table. - */ - displaySettings?: TDataTableSettingsProps['displaySettings']; - - /** - * The managed column settings of the table. - */ - columnManager?: TDataTableSettingsProps['columnManager']; - - /** - * A callback function, called when any of the properties of either display settings or column settings is modified. - */ - onSettingsChange?: ( - settingName: string, - settingValue: boolean | string[] - ) => void; - - /** - * A React node for rendering additional information within the table manager. - */ - topBar?: ReactNode; - - /** - * Sets the background theme of the Card that contains the settings - */ - managerTheme?: 'light' | 'dark'; -}; +import DataTableSettings from './data-table-settings'; +import type { TRow, TColumnProps, TDataTableManagerProps } from './types'; +import { useDataTableManagerContext } from '@commercetools-uikit/data-table-manager/data-table-manager-provider'; const DataTableManager = ( props: TDataTableManagerProps ) => { + const dataTableManagerContext = useDataTableManagerContext(); + + const dataTableColumns: TColumnProps[] = + props.columns || dataTableManagerContext.columns; + const displaySettings = + props.displaySettings || dataTableManagerContext.displaySettings; + const topBar = props.topBar || dataTableManagerContext.topBar; + const onSettingsChange = + props.onSettingsChange || dataTableManagerContext.onSettingsChange; + const columnManager = + props.columnManager || dataTableManagerContext.columnManager; + const areDisplaySettingsEnabled = Boolean( - props.displaySettings && !props.displaySettings.disableDisplaySettings + displaySettings && !displaySettings.disableDisplaySettings ); const isWrappingText = - areDisplaySettingsEnabled && props.displaySettings!.isWrappingText; + areDisplaySettingsEnabled && displaySettings!.isWrappingText; + + if (!dataTableColumns) { + throw new Error( + 'ui-kit/DataTableManager: missing `columns` prop. If you do not provide it to the component, then you should use the DataTableManagerProvider component.' + ); + } const columns = useMemo( () => - props.columns.map((column) => ({ + dataTableColumns.map((column) => ({ ...column, isTruncated: areDisplaySettingsEnabled ? isWrappingText : column.isTruncated, })), - [areDisplaySettingsEnabled, props.columns, isWrappingText] + [dataTableColumns, areDisplaySettingsEnabled, isWrappingText] ); return ( - {cloneElement(props.children, { - columns, - isCondensed: - areDisplaySettingsEnabled && props.displaySettings!.isCondensed, - })} + {props.children + ? cloneElement(props.children, { + columns, + isCondensed: + areDisplaySettingsEnabled && props.displaySettings!.isCondensed, + }) + : null} ); }; diff --git a/packages/components/data-table-manager/src/data-table-settings/data-table-settings.tsx b/packages/components/data-table-manager/src/data-table-settings/data-table-settings.tsx index 958dd95193..08481a40d1 100644 --- a/packages/components/data-table-manager/src/data-table-settings/data-table-settings.tsx +++ b/packages/components/data-table-manager/src/data-table-settings/data-table-settings.tsx @@ -1,4 +1,4 @@ -import { useState, type ReactElement, type ReactNode } from 'react'; +import { useState } from 'react'; import { warning } from '@commercetools-uikit/utils'; import { useIntl, type MessageDescriptor } from 'react-intl'; import styled from '@emotion/styled'; @@ -14,6 +14,7 @@ import messages from './messages'; import DropdownMenu from '@commercetools-uikit/dropdown-menu'; import IconButton from '@commercetools-uikit/icon-button'; import Tooltip from '@commercetools-uikit/tooltip'; +import { TColumnData, TDataTableSettingsProps } from '../types'; export type TSelectChangeEvent = { target: { @@ -23,104 +24,8 @@ export type TSelectChangeEvent = { persist: () => void; }; -type TColumnData = { - key: string; - label: ReactNode; -}; - type MappedColumns = Record; -export type TDisplaySettingsProps = { - /** - * Set this flag to `false` to show the display settings panel option. - * - * @@defaultValue@@: true - */ - disableDisplaySettings?: boolean; - - /** - * Set this to `true` to reduce the paddings of all cells, allowing the table to display - * more data in less space. - * - * @@defaultValue@@: true - */ - isCondensed?: boolean; - - /** - * Set this to `true` to allow text in a cell to wrap. - *
- * This is required if `disableDisplaySettings` is set to `false`. - * - * @@defaultValue@@: false - */ - isWrappingText?: boolean; - - /** - * A React element to be rendered as the primary button, useful when the display settings work as a form. - */ - primaryButton?: ReactElement; - - /** - * A React element to be rendered as the secondary button, useful when the display settings work as a form. - */ - secondaryButton?: ReactElement; -}; - -export type TColumnManagerProps = { - /** - * Set this to `true` to show a search input for the hidden columns panel. - */ - areHiddenColumnsSearchable?: boolean; - - /** - * Set this to `false` to show the column settings panel option. - * - * @@defaultValue@@: true - */ - disableColumnManager?: boolean; - - /** - * The keys of the visible columns. - */ - visibleColumnKeys: string[]; - - /** - * The keys of the visible columns. - */ - hideableColumns?: TColumnData[]; - - /** - * A callback function, called when the search input for the hidden columns panel changes. - */ - searchHiddenColumns?: (searchTerm: string) => Promise | void; - - /** - * Placeholder value of the search input for the hidden columns panel. - */ - searchHiddenColumnsPlaceholder?: string; - - /** - * A React element to be rendered as the primary button, useful when the column settings work as a form. - */ - primaryButton?: ReactElement; - - /** - * A React element to be rendered as the secondary button, useful when the column settings work as a form. - */ - secondaryButton?: ReactElement; -}; - -export type TDataTableSettingsProps = { - topBar?: ReactNode; - onSettingsChange?: ( - settingName: string, - settingValue: boolean | string[] - ) => void; - displaySettings?: TDisplaySettingsProps; - columnManager?: TColumnManagerProps; - managerTheme?: 'light' | 'dark'; -}; - export type TDropdownOption = { value: string; label: string; diff --git a/packages/components/data-table-manager/src/data-table-settings/index.ts b/packages/components/data-table-manager/src/data-table-settings/index.ts index c9c7b74764..a3e0e84b88 100644 --- a/packages/components/data-table-manager/src/data-table-settings/index.ts +++ b/packages/components/data-table-manager/src/data-table-settings/index.ts @@ -1,5 +1 @@ -export { - default, - type TDataTableSettingsProps, - type TSelectChangeEvent, -} from './data-table-settings'; +export { default, type TSelectChangeEvent } from './data-table-settings'; diff --git a/packages/components/data-table-manager/src/export-types.ts b/packages/components/data-table-manager/src/export-types.ts index 3db2ee2403..3f777f2bab 100644 --- a/packages/components/data-table-manager/src/export-types.ts +++ b/packages/components/data-table-manager/src/export-types.ts @@ -1 +1,8 @@ -export type { TRow, TColumnProps } from './data-table-manager'; +export type { + TRow, + TColumnProps, + TDataTableManagerProps, + TColumnData, + TDataTableSettingsProps, +} from './types'; +export type { TDataTableManagerContext } from './data-table-manager-provider'; diff --git a/packages/components/data-table-manager/src/index.ts b/packages/components/data-table-manager/src/index.ts index 3b4e190aae..772a7b7397 100644 --- a/packages/components/data-table-manager/src/index.ts +++ b/packages/components/data-table-manager/src/index.ts @@ -1,4 +1,8 @@ export { default } from './data-table-manager'; +export { + DataTableManagerProvider, + useDataTableManagerContext, +} from './data-table-manager-provider'; export { UPDATE_ACTIONS } from './constants'; export { default as version } from './version'; export * from './export-types'; diff --git a/packages/components/data-table-manager/src/types.tsx b/packages/components/data-table-manager/src/types.tsx new file mode 100644 index 0000000000..29c929531e --- /dev/null +++ b/packages/components/data-table-manager/src/types.tsx @@ -0,0 +1,233 @@ +import type { ReactNode, MouseEventHandler, ReactElement } from 'react'; + +export type TColumnData = { + key: string; + label: ReactNode; +}; + +export type TDisplaySettingsProps = { + /** + * Set this flag to `false` to show the display settings panel option. + * + * @@defaultValue@@: true + */ + disableDisplaySettings?: boolean; + + /** + * Set this to `true` to reduce the paddings of all cells, allowing the table to display + * more data in less space. + * + * @@defaultValue@@: true + */ + isCondensed?: boolean; + + /** + * Set this to `true` to allow text in a cell to wrap. + *
+ * This is required if `disableDisplaySettings` is set to `false`. + * + * @@defaultValue@@: false + */ + isWrappingText?: boolean; + + /** + * A React element to be rendered as the primary button, useful when the display settings work as a form. + */ + primaryButton?: ReactElement; + + /** + * A React element to be rendered as the secondary button, useful when the display settings work as a form. + */ + secondaryButton?: ReactElement; +}; + +export type TColumnManagerProps = { + /** + * Set this to `true` to show a search input for the hidden columns panel. + */ + areHiddenColumnsSearchable?: boolean; + + /** + * Set this to `false` to show the column settings panel option. + * + * @@defaultValue@@: true + */ + disableColumnManager?: boolean; + + /** + * The keys of the visible columns. + */ + visibleColumnKeys: string[]; + + /** + * The keys of the visible columns. + */ + hideableColumns?: TColumnData[]; + + /** + * A callback function, called when the search input for the hidden columns panel changes. + */ + searchHiddenColumns?: (searchTerm: string) => Promise | void; + + /** + * Placeholder value of the search input for the hidden columns panel. + */ + searchHiddenColumnsPlaceholder?: string; + + /** + * A React element to be rendered as the primary button, useful when the column settings work as a form. + */ + primaryButton?: ReactElement; + + /** + * A React element to be rendered as the secondary button, useful when the column settings work as a form. + */ + secondaryButton?: ReactElement; +}; + +export type TDataTableSettingsProps = { + topBar?: ReactNode; + onSettingsChange?: ( + settingName: string, + settingValue: boolean | string[] + ) => void; + displaySettings?: TDisplaySettingsProps; + columnManager?: TColumnManagerProps; + managerTheme?: 'light' | 'dark'; +}; + +export interface TRow { + id: string; +} + +export type TColumnProps = { + /** + * The unique key of the column that is used to identify your data type. + * You can use this value to determine which value from a row item should be rendered. + *
+ * For example, if the data is a list of users, where each user has a `firstName` property, + * the column key should be `firstName`, which renders the correct value by default. + * The key can also be some custom or computed value, in which case you need to provide + * an explicit mapping of the value by implementing either the `itemRendered` function or + * the column-specific `renderItem` function. + */ + key: string; + + /** + * The label of the column that will be shown on the column header. + */ + label: ReactNode; + + /** + * Sets a width for this column. Accepts the same values as the ones specified for + * individual [grid-template-columns](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns). + *
+ * For example, using `minmax` pairs (e.g. `minmax(200px, 400px)`), a combinations of + * fraction values (`1fr`/`2fr`/etc), or fixed values such as `200px`. + * By default, the column grows according to the content and respecting the total table available width. + */ + width?: string; + + /** + * Use this to override the table's own `horizontalCellAlignment` prop for this specific column. + */ + align?: 'left' | 'center' | 'right'; + + /** + * A callback function, called when the header cell is clicked. + */ + onClick?: (event: MouseEventHandler) => void; + + /** + * A callback function to render the content of cells under this column, overriding + * the default `itemRenderer` prop of the table. + */ + renderItem?: (row: Row, isRowCollapsed: boolean) => ReactNode; + + /** + * Use this prop to place an `Icon` or `IconButton` on the left of the column label. + * It is advised to place these types of components through this prop instead of `label`, + * in order to properly position and align the elements. + * This is particularly useful for medium-sized icons which require more vertical space than the typography. + */ + headerIcon?: ReactNode; + + /** + * Set this to `true` to allow text content of this cell to be truncated with an ellipsis, + * instead of breaking into multiple lines. + *
+ * NOTE: when using this option, it is recommended to specify a `width` for the column, because + * if the table doesn't have enough space for all columns, it will start clipping the columns + * with _truncated_ content, and if no `width` is set (or the value is set `auto` -- the default) + * it can shrink until the column disappears completely. + * By enforcing a minimum width for these columns, the table will respect them and grow horizontally, + * adding scrollbars if needed. + */ + isTruncated?: boolean; + + /** + * Set this to `true` to show a sorting button, which calls `onSortChange` upon being clicked. + * You should enable this flag for every column you want to be able to sort. + * When at least one column is sortable, the table props `sortBy`, `sortDirection` and `onSortChange` should be provided. + */ + isSortable?: boolean; + + /** + * Set this to `true` to prevent this column from being manually resized by dragging + * the edge of the header with a mouse. + */ + disableResizing?: boolean; + + /** + * Set this to `true` to prevent click event propagation for this cell. + * You might want this if you need the column to have its own call-to-action or input while + * the row also has a defined `onRowClick`. + */ + shouldIgnoreRowClick?: boolean; +}; + +export type TDataTableManagerProps = { + /** + * Each object requires a unique `key` which should correspond to property key of + * the items of `rows` that you want to render under this column, and a `label` + * which defines the name shown on the header. + * The list of columns to be rendered. + * Each column can be customized (see properties below). + */ + columns: TColumnProps[]; + + /** + * Any React node. Usually you want to render the `` component. + *
+ * Note that the child component will implicitly receive the props `columns` and `isCondensed` from the ``. + */ + children?: ReactElement; + + /** + * The managed display settings of the table. + */ + displaySettings?: TDataTableSettingsProps['displaySettings']; + + /** + * The managed column settings of the table. + */ + columnManager?: TDataTableSettingsProps['columnManager']; + + /** + * A callback function, called when any of the properties of either display settings or column settings is modified. + */ + onSettingsChange?: ( + settingName: string, + settingValue: boolean | string[] + ) => void; + + /** + * A React node for rendering additional information within the table manager. + */ + topBar?: ReactNode; + + /** + * Sets the background theme of the Card that contains the settings + */ + managerTheme?: 'light' | 'dark'; +}; diff --git a/packages/components/data-table/package.json b/packages/components/data-table/package.json index 34b4c71081..7218eb1090 100644 --- a/packages/components/data-table/package.json +++ b/packages/components/data-table/package.json @@ -22,6 +22,7 @@ "@babel/runtime": "^7.20.13", "@babel/runtime-corejs3": "^7.20.13", "@commercetools-uikit/accessible-button": "19.3.1", + "@commercetools-uikit/data-table-manager": "19.3.1", "@commercetools-uikit/design-system": "19.3.1", "@commercetools-uikit/hooks": "19.3.1", "@commercetools-uikit/icons": "19.3.1", diff --git a/packages/components/data-table/src/data-table.tsx b/packages/components/data-table/src/data-table.tsx index ab8d070dd5..e462b4f8e6 100644 --- a/packages/components/data-table/src/data-table.tsx +++ b/packages/components/data-table/src/data-table.tsx @@ -20,7 +20,7 @@ import HeaderCell from './header-cell'; import DataRow from './data-row'; import useManualColumnResizing from './use-manual-column-resizing-reducer'; import ColumnResizingContext from './column-resizing-context'; - +import { useDataTableManagerContext } from '@commercetools-uikit/data-table-manager/data-table-manager-provider'; export interface TRow { id: string; } @@ -239,8 +239,16 @@ export type TDataTableProps = { }; const DataTable = (props: TDataTableProps) => { + const { columns, isCondensed } = useDataTableManagerContext(); + const isValueFromProvider = Boolean(columns && columns.length !== 0); + const columnsData = isValueFromProvider ? columns : props.columns; + const condensedValue = + isValueFromProvider && isCondensed !== undefined + ? isCondensed + : props.isCondensed; + warning( - props.columns.length > 0, + columnsData.length > 0, `ui-kit/DataTable: empty table "columns", expected at least one column. If you are using DataTableManager you need to pass the "columns" there and they will be injected into DataTable.` ); const tableRef = useRef(); @@ -249,14 +257,14 @@ const DataTable = (props: TDataTableProps) => { // if the table columns have been measured // and if the list of columns, their width field, or the isCondensed prop has changed // then we need to reset the resized column widths - const columnsInfo = getColumnsLayoutInfo(props.columns); + const columnsInfo = getColumnsLayoutInfo(columnsData); const prevLayout = usePrevious({ columns: columnsInfo, - isCondensed: props.isCondensed, + isCondensed: condensedValue, }); const currentLayout = { columns: columnsInfo, - isCondensed: props.isCondensed, + isCondensed: condensedValue, }; const hasLayoutChanged = !isEqual(prevLayout, currentLayout); useLayoutEffect(() => { @@ -283,7 +291,7 @@ const DataTable = (props: TDataTableProps) => { } {...filterDataAttributes(props)} - columns={props.columns as TColumn[]} + columns={columnsData as TColumn[]} maxHeight={props.maxHeight} disableSelfContainment={!!props.disableSelfContainment} resizedTotalWidth={resizedTotalWidth} @@ -291,11 +299,11 @@ const DataTable = (props: TDataTableProps) => { - {props.columns.map((column) => ( + {columnsData.map((column) => ( (props: TDataTableProps) => { {props.rows.map((row, rowIndex) => ( {...props} + isCondensed={condensedValue} + columns={columnsData} row={row} key={row.id} rowIndex={rowIndex} @@ -340,7 +350,7 @@ const DataTable = (props: TDataTableProps) => { {props.footer && (