From 72cc434eadf6af4101e2e6784aae3eed5da39925 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Wed, 13 Nov 2024 14:54:59 +0100 Subject: [PATCH 1/3] feat(AnalyticalTable): introduce `popinDisplay` column option --- .../AnalyticalTable/AnalyticalTable.mdx | 88 ++++++++------- .../AnalyticalTable.stories.tsx | 59 +++++++++- .../defaults/Column/PopIn.module.css | 4 + .../AnalyticalTable/defaults/Column/PopIn.tsx | 43 +++++--- .../AnalyticalTable/defaults/Column/index.tsx | 9 +- .../AnalyticalTable/hooks/usePopIn.ts | 22 ++-- .../src/components/AnalyticalTable/index.tsx | 26 +++-- .../components/AnalyticalTable/types/index.ts | 102 ++++++++++-------- .../src/enums/AnalyticalTablePopinDisplay.ts | 19 ++++ packages/main/src/enums/index.ts | 4 +- 10 files changed, 256 insertions(+), 120 deletions(-) create mode 100644 packages/main/src/enums/AnalyticalTablePopinDisplay.ts diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.mdx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.mdx index e1ef69a3c78..4f3b14548bc 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.mdx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.mdx @@ -159,40 +159,41 @@ import * as ComponentStories from './AnalyticalTable.stories'; **Optional Properties** -| Attribute | Type | Description | -| ----------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Header` | `string OR ComponentType` | Can either be `string` or a React component that will be rendered as column header | -| `headerLabel` | `string` | Defines the `aria-label` for the whole column read by screen readers. | -| `headerTooltip` | `string` | Tooltip for the column header. If not set, the display text will be the same as the Header if it is a `string` | -| `Cell` | `ComponentType` | Custom cell renderer. If set, the table will call that component for every cell and pass all required information as props, e.g. the cell value as `props.cell.value` | -| `cellLabel` | `({cell, instance}) => string` | Defines a function that receives an object as a parameter, including the cell and table instance, and should return the `aria-label` of the current cell.

**Note:** Use this property if there is no textual content available through the dataset (e.g. no `accessor` field available), or if you want to provide additional context when navigating to the respective cell for screen readers.

**Note:** To retrieve the internal `aria-label`, utilize the `cell.cellLabel` property. | -| `width` | `number` | Cell width, if not set the table will distribute all columns without a width evenly | -| `minWidth` | `number` | min width of the column, e.g. used for resizing | -| `maxWidth` | `number` | max width of the column, e.g. used for resizing | -| `Filter` | `ComponentType` | Filter Component to be rendered in the Header | -| `disableFilters` | `boolean` | Disable filters for this column | -| `disableGlobalFilter` | `boolean` | Disable global filtering for this column | -| `defaultCanFilter` | `boolean` | If set to true, this column will be filterable, regardless if it has a valid `accessor` | -| `filter` | `string OR Function` | Either a string or a filter function.
Supported String Values: | -| `Aggregated` | `ComponentType` | Component to render for aggregated cells | -| `aggregate` | `string` OR
`((leafValues, aggregatedValues) => any)` | Aggregation function or string.
Supported String Values: | -| `aggregateValue` | `string` OR
`((values, row, column) => any)` | When attempting to group/aggregate non primitive cell values (eg. arrays of items) you will likely need to resolve a stable primitive value like a number or string to use in normal row aggregations. This property can be used to aggregate or simply access the value to be used in aggregations eg. count-ing the unique number of items in a cell's array value before sum-ing that count across the table | -| `disableGroupBy` | `boolean` | Disable groupBy for this column | -| `defaultCanSort` | `boolean` | If set to true, this column will be sortable, regardless if it has a valid `accessor` | -| `disableSortBy` | `boolean` | Disable sorting for this column | -| `sortDescFirst` | `boolean` | If set to `true`, the first sort direction for this column will be descending instead of ascending. | -| `sortInverted` | `boolean` | If set to `true`, the underlying sorting direction will be inverted, but the UI will not. | -| `sortType` | `string` OR
`((rowA, rowB, columnId: string, descending: boolean) => number)` | String or custom **memoized** sort function.
Supported String Values: **Default:**`'alphanumeric'` | -| `disableResizing` | `boolean` | Disable resizing for this column | -| `hAlign` | `TextAlign` | Horizontal align of the cell | -| `vAlign` | `VerticalAlign` | Vertical align of the cell | -| `scaleWidthModeOptions` | `ScaleWidthModeOptions` | Allows passing a custom string for the internal width calculation of custom cells for `scaleWidthMode` `Grow` and `Smart`. More here | -| `responsivePopIn` | `boolean` | Enables the pop-in behavior of the column. When the `responsiveMinWidth` is smaller then the width of the table, the content of each cell will move to the first cell in the row, improving usability on small or mobile devices. | -| `responsiveMinWidth` | `number` | Defines how the table should react when its width falls below the `responsiveMinWidth`. | -| `PopInHeader` | `string OR ComponentType` | Custom pop-in header renderer. If set, the table will call that component for every column that is "popped-in" and pass the table instance as prop. | -| `disableDragAndDrop` | `boolean` | Defines if the column is reorderable by dragging and dropping columns. | -| `enableMultiSort` | `boolean` | Defines whether this column should allow multi-sort.

**Note:** When sorting by a column that does not allow multiple sorting, only the current column is sorted and all other sorted columns are reset. | -| `autoResizable` | `boolean` | Defines whether double-clicking a columns resizer will automatically resize the column to fit the largest cell content of visible rows.

**Note:** Only default text content is supported by this option, for custom content it might work as well, but we recommend checking the behavior carefully as the logic can't account for all possible implementations. | +| Attribute | Type | Description | +| ----------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Header` | `string OR ComponentType` | Can either be `string` or a React component that will be rendered as column header | +| `headerLabel` | `string` | Defines the `aria-label` for the whole column read by screen readers. | +| `headerTooltip` | `string` | Tooltip for the column header. If not set, the display text will be the same as the Header if it is a `string` | +| `Cell` | `ComponentType` | Custom cell renderer. If set, the table will call that component for every cell and pass all required information as props, e.g. the cell value as `props.cell.value` | +| `cellLabel` | `({cell, instance}) => string` | Defines a function that receives an object as a parameter, including the cell and table instance, and should return the `aria-label` of the current cell.

**Note:** Use this property if there is no textual content available through the dataset (e.g. no `accessor` field available), or if you want to provide additional context when navigating to the respective cell for screen readers.

**Note:** To retrieve the internal `aria-label`, utilize the `cell.cellLabel` property. | +| `width` | `number` | Cell width, if not set the table will distribute all columns without a width evenly | +| `minWidth` | `number` | min width of the column, e.g. used for resizing | +| `maxWidth` | `number` | max width of the column, e.g. used for resizing | +| `Filter` | `ComponentType` | Filter Component to be rendered in the Header | +| `disableFilters` | `boolean` | Disable filters for this column | +| `disableGlobalFilter` | `boolean` | Disable global filtering for this column | +| `defaultCanFilter` | `boolean` | If set to true, this column will be filterable, regardless if it has a valid `accessor` | +| `filter` | `string OR Function` | Either a string or a filter function.
Supported String Values: | +| `Aggregated` | `ComponentType` | Component to render for aggregated cells | +| `aggregate` | `string` OR
`((leafValues, aggregatedValues) => any)` | Aggregation function or string.
Supported String Values: | +| `aggregateValue` | `string` OR
`((values, row, column) => any)` | When attempting to group/aggregate non primitive cell values (eg. arrays of items) you will likely need to resolve a stable primitive value like a number or string to use in normal row aggregations. This property can be used to aggregate or simply access the value to be used in aggregations eg. count-ing the unique number of items in a cell's array value before sum-ing that count across the table | +| `disableGroupBy` | `boolean` | Disable groupBy for this column | +| `defaultCanSort` | `boolean` | If set to true, this column will be sortable, regardless if it has a valid `accessor` | +| `disableSortBy` | `boolean` | Disable sorting for this column | +| `sortDescFirst` | `boolean` | If set to `true`, the first sort direction for this column will be descending instead of ascending. | +| `sortInverted` | `boolean` | If set to `true`, the underlying sorting direction will be inverted, but the UI will not. | +| `sortType` | `string` OR
`((rowA, rowB, columnId: string, descending: boolean)`
`=> number)` | String or custom **memoized** sort function.
Supported String Values: **Default:**`'alphanumeric'` | +| `disableResizing` | `boolean` | Disable resizing for this column | +| `hAlign` | `TextAlign` | Horizontal align of the cell | +| `vAlign` | `VerticalAlign` | Vertical align of the cell | +| `scaleWidthModeOptions` | `ScaleWidthModeOptions` | Allows passing a custom string for the internal width calculation of custom cells for `scaleWidthMode` `Grow` and `Smart`. More here | +| `responsivePopIn` | `boolean` | Enables the pop-in behavior of the column. When the `responsiveMinWidth` is smaller then the width of the table, the content of each cell will move to the first cell in the row, improving usability on small or mobile devices. | +| `responsiveMinWidth` | `number` | Defines how the table should react when its width falls below the `responsiveMinWidth`. | +| `PopInHeader` | `string OR ComponentType` | Custom pop-in header renderer. If set, the table will call that component for every column that is "popped-in" and pass the table instance as prop. | +| `popinDisplay` | `AnalyticalTablePopinDisplay` | Defines the display of `AnalyticalTable` pop-ins.
**Default:** `AnalyticalTablePopinDisplay.Block` | +| `disableDragAndDrop` | `boolean` | Defines if the column is reorderable by dragging and dropping columns. | +| `enableMultiSort` | `boolean` | Defines whether this column should allow multi-sort.

**Note:** When sorting by a column that does not allow multiple sorting, only the current column is sorted and all other sorted columns are reset. | +| `autoResizable` | `boolean` | Defines whether double-clicking a columns resizer will automatically resize the column to fit the largest cell content of visible rows.

**Note:** Only default text content is supported by this option, for custom content it might work as well, but we recommend checking the behavior carefully as the logic can't account for all possible implementations. |
@@ -426,6 +427,7 @@ const COLUMNS = [ accessor: 'name' }, { + disableSortBy: true, responsivePopIn: true, responsiveMinWidth: 601, PopInHeader: 'Custom Header Text (age)', @@ -433,6 +435,7 @@ const COLUMNS = [ accessor: 'age' }, { + disableSortBy: true, responsivePopIn: true, responsiveMinWidth: 401, Header: 'Friend Name', @@ -441,12 +444,9 @@ const COLUMNS = [ }, accessor: 'friend.name' }, + { disableSortBy: true, responsiveMinWidth: 401, Header: 'Friend Age', accessor: 'friend.age' }, { - responsiveMinWidth: 401, - Header: 'Friend Age', - accessor: 'friend.age' - }, - { + disableSortBy: true, responsivePopIn: true, responsiveMinWidth: 801, id: 'actions', @@ -461,6 +461,16 @@ const COLUMNS = [ ); } + }, + { + id: 'popinDisplay', + Header: 'PopinDisplay Modes', + responsivePopIn: true, + responsiveMinWidth: 801, + popinDisplay: popinDisplay, // possible values: "Block", "Inline", "WithoutHeader" + Cell: () => { + return Using popinDisplay: {popinDisplay}; + } } ]; ``` diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx index a455f7fb047..49dc11b9b46 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx @@ -6,6 +6,7 @@ import '@ui5/webcomponents-icons/dist/edit.js'; import '@ui5/webcomponents-icons/dist/settings.js'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { + AnalyticalTablePopinDisplay, AnalyticalTableScaleWidthMode, AnalyticalTableSelectionBehavior, AnalyticalTableSelectionMode, @@ -15,8 +16,15 @@ import { FlexBoxJustifyContent, TextAlign } from '../../enums/index.js'; -import { Button, MultiComboBox, MultiComboBoxItem, Option, Select, Tag, Text } from '../../webComponents/index.js'; -import { FlexBox } from '../FlexBox'; +import { Button } from '../../webComponents/Button/index.js'; +import { Label } from '../../webComponents/Label/index.js'; +import { MultiComboBox } from '../../webComponents/MultiComboBox/index.js'; +import { MultiComboBoxItem } from '../../webComponents/MultiComboBoxItem/index.js'; +import { Option } from '../../webComponents/Option/index.js'; +import { Select } from '../../webComponents/Select/index.js'; +import { Tag } from '../../webComponents/Tag/index.js'; +import { Text } from '../../webComponents/Text/index.js'; +import { FlexBox } from '../FlexBox/index.js'; import { AnalyticalTable } from './index.js'; const meta = { @@ -325,6 +333,16 @@ export const ResponsiveColumns: Story = { ); } + }, + { + id: 'popinDisplay', + Header: 'PopinDisplay Modes', + responsivePopIn: true, + responsiveMinWidth: 801, + popinDisplay: AnalyticalTablePopinDisplay.Block, + Cell: () => { + return Using popinDisplay: 'Block'; + } } ] }, @@ -336,19 +354,52 @@ export const ResponsiveColumns: Story = { type: 'radio' }, description: - 'Select an option to change the width of the surrounding container of the table (in `px`).
__Note__: This is not an actual prop of the table.' + 'Select an option to change the width of the surrounding container of the table (in `px`).
__Note__: This is not a prop of the table.' } }, render: (args) => { + const [columns, setColumns] = useState(args.columns); + const [popinDisplay, setPopinDisplay] = useState(AnalyticalTablePopinDisplay.Block); + + useEffect(() => { + setColumns((prev) => { + return [ + ...prev.slice(0, -1), + { + id: 'popinDisplay', + Header: 'PopinDisplay Modes', + responsivePopIn: true, + responsiveMinWidth: 801, + popinDisplay: popinDisplay, + Cell: () => { + return Using popinDisplay: {popinDisplay}; + } + } + ]; + }); + }, [popinDisplay]); + return (
+ + { +export const PopIn = (instance: TableInstance) => { const { state, contentToRender, cell, row, internalRowHeight } = instance; - useStylesheet(styleData, PopIn.displayName); + return ( { {contentToRender !== RenderColumnTypes.Grouped && state.popInColumns?.map((item) => { + const { popinDisplay, id, column } = item; const popInInstanceProps = row.allCells.find((cell) => cell.column.id === item.id); const renderHeader = () => { - if (item.column.PopInHeader) { - return typeof item.column.PopInHeader === 'function' - ? item.column.PopInHeader({ ...instance, ...popInInstanceProps }) - : item.column.PopInHeader; + if (column.PopInHeader) { + return typeof column.PopInHeader === 'function' + ? column.PopInHeader({ ...instance, ...popInInstanceProps }) + : column.PopInHeader; } - return typeof item.column.Header === 'function' - ? makeRenderer({ ...instance, ...popInInstanceProps }, item.column)(item.column.Header) - : item.column.Header; + return typeof column.Header === 'function' + ? makeRenderer({ ...instance, ...popInInstanceProps }, column)(column.Header) + : column.Header; }; const renderCell = () => { - if (item.column?.Cell) { - const cell = item.column.Cell; + if (column?.Cell) { + const cell = column.Cell; if (typeof cell === 'string') { return ( @@ -49,7 +54,7 @@ export const PopIn = (instance) => { ); } - return makeRenderer({ ...instance, ...popInInstanceProps, isPopIn: true }, item.column)(item.column.Cell); + return makeRenderer({ ...instance, ...popInInstanceProps, isPopIn: true }, column)(column.Cell); } return popInInstanceProps?.value ? ( @@ -58,8 +63,16 @@ export const PopIn = (instance) => { ) : null; }; return ( - - {item.column?.Header &&
{renderHeader()}:
} + + {popinDisplay !== AnalyticalTablePopinDisplay.WithoutHeader && column?.Header && ( +
{renderHeader()}:
+ )}
{popInInstanceProps && renderCell()}
); diff --git a/packages/main/src/components/AnalyticalTable/defaults/Column/index.tsx b/packages/main/src/components/AnalyticalTable/defaults/Column/index.tsx index 13748fb5e2a..17248a3c118 100644 --- a/packages/main/src/components/AnalyticalTable/defaults/Column/index.tsx +++ b/packages/main/src/components/AnalyticalTable/defaults/Column/index.tsx @@ -1,4 +1,6 @@ -import { VerticalAlign } from '../../../../enums/index.js'; +import { AnalyticalTablePopinDisplay } from '../../../../enums/AnalyticalTablePopinDisplay.js'; +import { VerticalAlign } from '../../../../enums/VerticalAlign.js'; +import type { AnalyticalTableColumnDefinition } from '../../types/index.js'; import { DefaultFilterComponent } from '../FilterComponent/index.js'; import { Cell } from './Cell.js'; import { ColumnHeaderModal } from './ColumnHeaderModal.js'; @@ -9,7 +11,7 @@ import { RepeatedValue } from './RepeatedValue.js'; export const DEFAULT_COLUMN_WIDTH = 60; -export const DefaultColumn = { +export const DefaultColumn: AnalyticalTableColumnDefinition = { Filter: DefaultFilterComponent, minWidth: DEFAULT_COLUMN_WIDTH, vAlign: VerticalAlign.Middle, @@ -18,5 +20,6 @@ export const DefaultColumn = { Expandable: Expandable, RepeatedValue: RepeatedValue, PopIn: PopIn, - Popover: ColumnHeaderModal + Popover: ColumnHeaderModal, + popinDisplay: AnalyticalTablePopinDisplay.Block }; diff --git a/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts b/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts index 9f776c03b69..527428e0a7e 100644 --- a/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts +++ b/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts @@ -1,20 +1,28 @@ -import type { ReactTableHooks, TableInstance } from '../types/index.js'; +import { AnalyticalTablePopinDisplay } from '../../../enums/AnalyticalTablePopinDisplay.js'; +import type { AnalyticalTableState, ReactTableHooks } from '../types/index.js'; -const popInVisibleColumnsDeps = (deps, { instance: { state } }: { instance: TableInstance }) => [ +const popInVisibleColumnsDeps: ReactTableHooks['visibleColumnsDeps'][0] = (deps, { instance: { state } }) => [ ...deps, state.tableClientWidth ]; -const popInVisibleColumns = (cols, { instance }: { instance: TableInstance }) => { +const popInVisibleColumns: ReactTableHooks['visibleColumns'][0] = (cols, { instance }) => { const { state, dispatch } = instance; const tableClientWidth = state.isScrollable ? state?.tableClientWidth + 13 /*scrollbar width*/ : state?.tableClientWidth; - const popInColumns = cols - .filter((item) => item.responsivePopIn && tableClientWidth < item.responsiveMinWidth) - .map((item) => ({ id: item.id ?? item.accessor, column: item })); + const popInColumns: AnalyticalTableState['popInColumns'] = cols + .filter((item) => { + return item.responsivePopIn && tableClientWidth < item.responsiveMinWidth; + }) + .map((item) => ({ + id: item.id ?? item.accessor, + column: item, + // initially visibleColumns don't include the defaults + popinDisplay: item.popinDisplay || AnalyticalTablePopinDisplay.Block + })); dispatch({ type: 'SET_POPIN_COLUMNS', payload: popInColumns }); @@ -31,3 +39,5 @@ export const usePopIn = (hooks: ReactTableHooks) => { hooks.visibleColumns.push(popInVisibleColumns); hooks.visibleColumnsDeps.push(popInVisibleColumnsDeps); }; + +usePopIn.pluginName = 'usePopIn'; diff --git a/packages/main/src/components/AnalyticalTable/index.tsx b/packages/main/src/components/AnalyticalTable/index.tsx index 7720619cf30..04bcee8baae 100644 --- a/packages/main/src/components/AnalyticalTable/index.tsx +++ b/packages/main/src/components/AnalyticalTable/index.tsx @@ -25,6 +25,7 @@ import { useTable } from 'react-table'; import { + AnalyticalTablePopinDisplay, AnalyticalTableScaleWidthMode, AnalyticalTableSelectionBehavior, AnalyticalTableSelectionMode, @@ -43,10 +44,10 @@ import { LIST_NO_DATA, NO_DATA_FILTERED, SELECT_ALL, - SELECT_PRESS_SPACE, - UNSELECT_PRESS_SPACE, SELECT_ALL_PRESS_SPACE, - UNSELECT_ALL_PRESS_SPACE + SELECT_PRESS_SPACE, + UNSELECT_ALL_PRESS_SPACE, + UNSELECT_PRESS_SPACE } from '../../i18n/i18n-defaults.js'; import { BusyIndicator } from '../../webComponents/BusyIndicator/index.js'; import { Text } from '../../webComponents/Text/index.js'; @@ -278,7 +279,7 @@ const AnalyticalTable = forwardRef 0 - ? internalRowHeight + tableState.popInColumns.length * (internalRowHeight + 16) - : internalRowHeight; + const popInRowHeight = (() => { + if (popInColumns?.length) { + return popInColumns.reduce( + (acc, cur) => + cur.popinDisplay === AnalyticalTablePopinDisplay.Block + ? acc + internalRowHeight + 16 // 16px for Header + : acc + internalRowHeight, + internalRowHeight + ); + } else { + return internalRowHeight; + } + })(); const internalVisibleRowCount = tableState.visibleRows ?? visibleRows; diff --git a/packages/main/src/components/AnalyticalTable/types/index.ts b/packages/main/src/components/AnalyticalTable/types/index.ts index 60bef705e60..03d5a3c45da 100644 --- a/packages/main/src/components/AnalyticalTable/types/index.ts +++ b/packages/main/src/components/AnalyticalTable/types/index.ts @@ -1,7 +1,8 @@ import type { ScrollToOptions } from '@tanstack/react-virtual'; import type ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js'; -import type { ComponentType, Dispatch, MutableRefObject, ReactNode, Ref, SetStateAction } from 'react'; +import type { ComponentType, DependencyList, Dispatch, MutableRefObject, ReactNode, Ref, SetStateAction } from 'react'; import type { + AnalyticalTablePopinDisplay, AnalyticalTableScaleWidthMode, AnalyticalTableScrollMode, AnalyticalTableSelectionBehavior, @@ -247,52 +248,18 @@ export interface ScrollToRefType { scrollToOffset: (item: number, options: Omit) => void; } -export interface ReactTableHooks { - useOptions: any[]; - stateReducers: any[]; - useControlledState: any[]; - columns: any[]; - columnsDeps: any[]; - allColumns: any[]; - allColumnsDeps: any[]; - accessValue: any[]; - materializedColumns: any[]; - materializedColumnsDeps: any[]; - useInstanceAfterData: any[]; - visibleColumns: any[]; - visibleColumnsDeps: any[]; - headerGroups: any[]; - headerGroupsDeps: any[]; - useInstanceBeforeDimensions: any[]; - useInstance: any[]; - prepareRow: any[]; - getTableProps: any[]; - getTableBodyProps: any[]; - getHeaderGroupProps: any[]; - getFooterGroupProps: any[]; - getHeaderProps: any[]; - getFooterProps: any[]; - getRowProps: any[]; - getCellProps: any[]; - useFinalInstance: any[]; - getToggleHiddenProps: any[]; - getToggleHideAllColumnsProps: any[]; - getGroupByToggleProps: any[]; - getSortByToggleProps: any[]; - getToggleAllRowsExpandedProps: any[]; - getToggleRowExpandedProps: any[]; - getToggleRowSelectedProps: any[]; - getToggleAllRowsSelectedProps: any[]; - getToggleAllPageRowsSelectedProps: any[]; - getResizerProps: any[]; -} - export interface TriggerScrollState { type: 'offset' | 'item'; direction: 'vertical' | 'horizontal'; args: [number, Omit?]; } +interface PopInColumnsState { + id: string; + column: ColumnType; + popinDisplay: AnalyticalTableColumnDefinition['popinDisplay']; +} + export interface AnalyticalTableState { columnOrder: string[]; columnResizing: Record; @@ -305,7 +272,7 @@ export interface AnalyticalTableState { globalFilter?: string; tableClientWidth?: number; dndColumn?: string; - popInColumns?: Record[]; + popInColumns?: PopInColumnsState[]; isRtl?: boolean; isScrollable?: boolean; subComponentsHeight?: Record; @@ -556,6 +523,13 @@ export interface AnalyticalTableColumnDefinition { * Custom pop-in header renderer. If set, the table will call that component for every column that is "popped-in" and pass the table instance as prop. */ PopInHeader?: string | ComponentType | ((props?: any) => ReactNode); + /** + * Defines the display of `AnalyticalTable` pop-ins. + * + * @default AnalyticalTablePopinDisplay.Block + * @since 2.5.0 + */ + popinDisplay?: AnalyticalTablePopinDisplay | keyof typeof AnalyticalTablePopinDisplay; //use useDragAndDrop /** @@ -969,3 +943,47 @@ export interface AnalyticalTablePropTypes extends Omit { */ tableInstance?: Ref; } + +interface ConfigParam { + instance: TableInstance; +} + +export interface ReactTableHooks { + useOptions: any[]; + stateReducers: any[]; + useControlledState: any[]; + columns: ((columns: ColumnType[], config: ConfigParam) => ColumnType[])[]; + columnsDeps: ((deps: DependencyList, config: ConfigParam) => DependencyList)[]; + allColumns: any[]; + allColumnsDeps: any[]; + accessValue: any[]; + materializedColumns: any[]; + materializedColumnsDeps: any[]; + useInstanceAfterData: any[]; + visibleColumns: ((columns: ColumnType[], config: ConfigParam) => ColumnType[])[]; + visibleColumnsDeps: ((deps: DependencyList, config: ConfigParam) => DependencyList)[]; + headerGroups: any[]; + headerGroupsDeps: any[]; + useInstanceBeforeDimensions: any[]; + useInstance: any[]; + prepareRow: any[]; + getTableProps: any[]; + getTableBodyProps: any[]; + getHeaderGroupProps: any[]; + getFooterGroupProps: any[]; + getHeaderProps: any[]; + getFooterProps: any[]; + getRowProps: any[]; + getCellProps: any[]; + useFinalInstance: any[]; + getToggleHiddenProps: any[]; + getToggleHideAllColumnsProps: any[]; + getGroupByToggleProps: any[]; + getSortByToggleProps: any[]; + getToggleAllRowsExpandedProps: any[]; + getToggleRowExpandedProps: any[]; + getToggleRowSelectedProps: any[]; + getToggleAllRowsSelectedProps: any[]; + getToggleAllPageRowsSelectedProps: any[]; + getResizerProps: any[]; +} diff --git a/packages/main/src/enums/AnalyticalTablePopinDisplay.ts b/packages/main/src/enums/AnalyticalTablePopinDisplay.ts new file mode 100644 index 00000000000..cf0f0bba6d9 --- /dev/null +++ b/packages/main/src/enums/AnalyticalTablePopinDisplay.ts @@ -0,0 +1,19 @@ +/** + * Defines the display of `AnalyticalTable` pop-ins. + * + * @since 2.5.0 + */ +export enum AnalyticalTablePopinDisplay { + /* + *Inside the table popin, the header is displayed in the first line and cell content is displayed in the next line. + */ + Block = 'Block', + /* + * Inside the table popin, cell content is displayed next to the header in the same line. + */ + Inline = 'Inline', + /* + *Inside the table popin, only the cell content will be visible. + */ + WithoutHeader = 'WithoutHeader' +} diff --git a/packages/main/src/enums/index.ts b/packages/main/src/enums/index.ts index 122177b9e66..3b1d431dabe 100644 --- a/packages/main/src/enums/index.ts +++ b/packages/main/src/enums/index.ts @@ -1,6 +1,4 @@ -// This is an autogenerated file, please do not modify this file manually. -// In case you added a new file to the /enums folder, please rerun the scripts/create-enum-export.js script. - +export * from './AnalyticalTablePopinDisplay.js'; export * from './AnalyticalTableScaleWidthMode.js'; export * from './AnalyticalTableScrollMode.js'; export * from './AnalyticalTableSelectionBehavior.js'; From 81a38bcb32aa1a63aeb9f9c7cc030d268ff4f4fe Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Wed, 13 Nov 2024 16:02:35 +0100 Subject: [PATCH 2/3] adjust tests --- .../AnalyticalTable/AnalyticalTable.cy.tsx | 141 +++++++++++++++++- .../AnalyticalTable/defaults/Column/PopIn.tsx | 4 +- 2 files changed, 140 insertions(+), 5 deletions(-) diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index 8bd08acfa68..0bcfd167a62 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -8,9 +8,9 @@ import type { PopoverDomRef } from '../..'; import { - Popover, AnalyticalTable, AnalyticalTableHooks, + AnalyticalTablePopinDisplay, AnalyticalTableScaleWidthMode, AnalyticalTableSelectionBehavior, AnalyticalTableSelectionMode, @@ -18,7 +18,9 @@ import { AnalyticalTableVisibleRowCountMode, Button, IndicationColor, - Input + Input, + Popover, + Text } from '../..'; import { useManualRowSelect } from './pluginHooks/useManualRowSelect'; import { useRowDisableSelection } from './pluginHooks/useRowDisableSelection'; @@ -1986,13 +1988,15 @@ describe('AnalyticalTable', () => { ['ltr', 'rtl'].forEach((dir) => { cy.mount(); cy.viewport(801, 1024); - cy.findByText('Name').should('be.visible'); cy.findByText('Age').should('be.visible'); cy.findByText('Friend Name').should('be.visible'); cy.findByText('Custom original Header1').should('be.visible'); cy.findByText('Custom original Header2').should('be.visible'); cy.findByText('Custom Header').should('be.visible'); + cy.findByText('Custom Header').should('be.visible'); + cy.findByText('PopinDisplay Modes').should('be.visible').should('have.attr', 'ui5-text'); + cy.findAllByTestId('popinCell').should('exist'); cy.contains('Custom Cell 2').should('be.visible'); cy.contains('Custom Header 1').should('not.exist'); @@ -2017,18 +2021,77 @@ describe('AnalyticalTable', () => { cy.contains('Custom Header 2').should('be.visible'); cy.contains('pop-in content').should('exist'); cy.contains('C').should('exist'); + cy.findAllByTestId('popinCell').should('exist'); + cy.findAllByText('PopinDisplay Modes:').as('popinHeader').should('be.exist'); + //popinDisplay: Block + cy.get('@popinHeader').parent().should('have.css', 'flex-direction', 'column'); cy.viewport(600, 1024); cy.wait(200); cy.contains('Age').should('not.exist'); cy.contains('40').should('not.exist'); + + cy.mount( + { + return ( + + Popin Cell + + ); + } + } + ]} + dir={dir} + /> + ); + cy.findAllByText('PopinDisplay Modes:').as('popinHeader').should('be.exist'); + //popinDisplay: Row + cy.get('@popinHeader').parent().should('have.css', 'flex-direction', 'row'); + cy.findAllByTestId('popinCell').should('exist'); + + cy.mount( + { + return ( + + Popin Cell + + ); + } + } + ]} + dir={dir} + /> + ); + //popinDisplay: WithoutHeader + cy.findAllByText('PopinDisplay Modes:').should('not.exist'); + cy.findAllByTestId('popinCell').should('exist'); }); }); it('pop-in columns: adjustTableHeightOnPopIn ', () => { document.body.style.margin = '0px'; cy.viewport(800, 2000); - cy.mount(); + cy.mount(); cy.findByText('Name').should('be.visible'); cy.findByText('Age').should('be.visible'); @@ -2040,12 +2103,68 @@ describe('AnalyticalTable', () => { cy.findByText('Custom original Header2').should('not.exist'); cy.contains('Custom Header').should('be.visible'); cy.contains('Custom Cell 2').should('be.visible'); + cy.findAllByTestId('popinCell').should('be.visible'); cy.contains('Custom Header 1').should('be.visible'); cy.contains('Custom Header 2').should('be.visible'); cy.contains('pop-in content').should('be.visible'); cy.contains('C').should('be.visible'); + cy.findByTestId('table').should('have.css', 'height', '1764px'); + + cy.mount( + { + return ( + + Popin Cell + + ); + } + } + ]} + data-testid="table" + adjustTableHeightOnPopIn + /> + ); + cy.findAllByTestId('popinCell').should('be.visible'); + cy.findByTestId('table').should('have.css', 'height', '1684px'); + + cy.mount( + { + return ( + + Popin Cell + + ); + } + } + ]} + data-testid="table" + adjustTableHeightOnPopIn + /> + ); + cy.findAllByTestId('popinCell').should('be.visible'); + cy.findByTestId('table').should('have.css', 'height', '1684px'); }); it('plugin hook: useRowDisableSelection', () => { @@ -3427,6 +3546,20 @@ const columnsWithPopIn = [ } return 'original content'; } + }, + { + id: 'popinDisplay', + Header: 'PopinDisplay Modes', + responsivePopIn: true, + responsiveMinWidth: 801, + popinDisplay: 'Block', + Cell: () => { + return ( + + Popin Cell + + ); + } } ]; diff --git a/packages/main/src/components/AnalyticalTable/defaults/Column/PopIn.tsx b/packages/main/src/components/AnalyticalTable/defaults/Column/PopIn.tsx index d5f3826f61e..b94d964d325 100644 --- a/packages/main/src/components/AnalyticalTable/defaults/Column/PopIn.tsx +++ b/packages/main/src/components/AnalyticalTable/defaults/Column/PopIn.tsx @@ -71,7 +71,9 @@ export const PopIn = (instance: TableInstance) => { key={id} > {popinDisplay !== AnalyticalTablePopinDisplay.WithoutHeader && column?.Header && ( -
{renderHeader()}:
+
+ {renderHeader()}: +
)}
{popInInstanceProps && renderCell()}
From 8c7c5ed672efddad553cdb29713c6bb5f8bf4559 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Thu, 14 Nov 2024 08:39:27 +0100 Subject: [PATCH 3/3] fix types --- .../defaults/Column/ColumnHeaderModal.tsx | 4 ++-- .../src/components/AnalyticalTable/types/index.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx b/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx index 2457bfa39d3..42514db0ea8 100644 --- a/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx +++ b/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx @@ -29,10 +29,10 @@ import type { PopoverDomRef } from '../../../../webComponents/Popover/index.js'; import { Popover } from '../../../../webComponents/Popover/index.js'; import { Text } from '../../../../webComponents/Text/index.js'; import { FlexBox } from '../../../FlexBox/index.js'; -import type { TableInstance } from '../../types/index.js'; +import type { TableInstanceWithPopoverProps } from '../../types/index.js'; import { classNames, styleData } from './ColumnHeaderModal.module.css.js'; -export const ColumnHeaderModal = (instance: TableInstance) => { +export const ColumnHeaderModal = (instance: TableInstanceWithPopoverProps) => { const { setOpen, openerRef } = instance.popoverProps; const { column, state, webComponentsReactProperties } = instance; const { isRtl, groupBy } = state; diff --git a/packages/main/src/components/AnalyticalTable/types/index.ts b/packages/main/src/components/AnalyticalTable/types/index.ts index 03d5a3c45da..06506fa6450 100644 --- a/packages/main/src/components/AnalyticalTable/types/index.ts +++ b/packages/main/src/components/AnalyticalTable/types/index.ts @@ -76,8 +76,8 @@ export interface ColumnType extends Omit export interface TableInstance { allColumns?: ColumnType[]; allColumnsHidden?: boolean; - columns?: ColumnType[]; - data?: Record[]; + columns: ColumnType[]; + data: Record[]; defaultColumn?: Record; disableFilters?: boolean; disableGlobalFilter?: boolean; @@ -133,7 +133,7 @@ export interface TableInstance { preSortedRows?: RowType[]; prepareRow?: (row: RowType) => void; resetResizing?: any; - rows?: RowType[]; + rows: RowType[]; rowsById?: Record; selectSubRows?: boolean; selectedFlatRows?: RowType[]; @@ -147,7 +147,7 @@ export interface TableInstance { sortTypes?: Record; sortedFlatRows?: Record[]; sortedRows?: Record[]; - state?: AnalyticalTableState & Record; + state: AnalyticalTableState & Record; stateReducer?: ( state: TableInstance['state'], action: any, @@ -171,9 +171,9 @@ export interface TableInstance { startIndex: number; endIndex: number; }; - visibleColumns?: ColumnType[]; + visibleColumns: ColumnType[]; visibleColumnsWidth?: number[]; - webComponentsReactProperties?: WCRPropertiesType; + webComponentsReactProperties: WCRPropertiesType; [key: string]: any; } @@ -397,7 +397,7 @@ export interface AnalyticalTableColumnDefinition { */ Popover?: | ComponentType<{ instance: TableInstanceWithPopoverProps }> - | ((props?: { instance: TableInstanceWithPopoverProps }) => ReactNode); + | ((instance: TableInstanceWithPopoverProps) => ReactNode); // useFilters /**