Skip to content

Commit 7aab047

Browse files
authored
fix(AnalyticalTable): fix tree-table selection, add selectionBehavior prop + enum (#401)
BREAKING CHANGE: replace `noSelectionColumn` prop with `selectionBehavior` enum
1 parent c6b64ba commit 7aab047

14 files changed

+278
-180
lines changed

packages/base/src/styling/CssSizeVariables.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export enum CssSizeVariablesNames {
1313
sapWcrAnalyticalTableRowHeight = 'sapWcrAnalyticalTableRowHeight',
1414
sapWcrAnalyticalTableTreePaddingLevel1 = 'sapWcrAnalyticalTableTreePaddingLevel1',
1515
sapWcrAnalyticalTableTreePaddingLevel2 = 'sapWcrAnalyticalTableTreePaddingLevel2',
16-
sapWcrAnalyticalTableTreePaddingLevel3 = 'sapWcrAnalyticalTableTreePaddingLevel3'
16+
sapWcrAnalyticalTableTreePaddingLevel3 = 'sapWcrAnalyticalTableTreePaddingLevel3',
17+
sapWcrAnalyticalTableSelectionColumnWidth = 'sapWcrAnalyticalTableSelectionColumnWidth'
1718
}
1819

1920
export const CssSizeVariables: Record<CssSizeVariablesNames, string> = Object.values(CssSizeVariablesNames).reduce(
@@ -41,6 +42,7 @@ export const cssVariablesStyles = `
4142
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel1}:1.5rem;
4243
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel2}:2.25rem;
4344
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel3}:2.75rem;
45+
--${CssSizeVariablesNames.sapWcrAnalyticalTableSelectionColumnWidth}:55px;
4446
4547
}
4648
@@ -61,5 +63,6 @@ export const cssVariablesStyles = `
6163
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel1}:1rem;
6264
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel2}:1.5rem;
6365
--${CssSizeVariablesNames.sapWcrAnalyticalTableTreePaddingLevel3}:2rem;
66+
--${CssSizeVariablesNames.sapWcrAnalyticalTableSelectionColumnWidth}:40px;
6467
}
6568
`;

packages/main/src/components/AnalyticalTable/AnalyticalTable.test.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { createPassThroughPropsTest } from '@shared/tests/utils';
2-
import { mount } from 'enzyme';
32
import { AnalyticalTable } from '@ui5/webcomponents-react/lib/AnalyticalTable';
4-
import { ValueState } from '@ui5/webcomponents-react/lib/ValueState';
5-
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
63
import { AnalyticalTableScrollMode } from '@ui5/webcomponents-react/lib/AnalyticalTableScrollMode';
4+
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
5+
import { ValueState } from '@ui5/webcomponents-react/lib/ValueState';
6+
import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior';
7+
import { mount } from 'enzyme';
78
import React, { useRef } from 'react';
89

910
const columns = [
@@ -268,7 +269,7 @@ describe('AnalyticalTable', () => {
268269
data={data}
269270
columns={columns}
270271
selectionMode={TableSelectionMode.SINGLE_SELECT}
271-
noSelectionColumn
272+
selectionBehavior={TableSelectionBehavior.ROW_ONLY}
272273
/>
273274
);
274275

packages/main/src/components/AnalyticalTable/AnayticalTable.jss.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ const styles = {
6868
selectable: {
6969
'& $tr:hover:not($emptyRow)': {
7070
backgroundColor: ThemingParameters.sapList_Hover_Background,
71-
cursor: 'pointer'
71+
'&:not($selectionModeRowSelector)': {
72+
cursor: 'pointer'
73+
}
7274
},
73-
'& $tr:active:not([data-is-selected]):not($tableGroupHeader):not($emptyRow)': {
75+
'& $tr:active:not([data-is-selected]):not($tableGroupHeader):not($emptyRow):not($selectionModeRowSelector)': {
7476
backgroundColor: ThemingParameters.sapList_Active_Background,
7577
'& $tableCell': {
7678
borderRight: `1px solid ${ThemingParameters.sapList_Active_Background}`,
@@ -79,6 +81,7 @@ const styles = {
7981
}
8082
}
8183
},
84+
selectionModeRowSelector: {},
8285
tableCell: {
8386
height: CssSizeVariables.sapWcrAnalyticalTableRowHeight,
8487
fontFamily: ThemingParameters.sapFontFamily,

packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export interface ColumnHeaderProps {
3434

3535
const styles = {
3636
header: {
37-
padding: `0 0.5rem`,
3837
height: '100%',
3938
display: 'flex',
4039
justifyContent: 'begin',
@@ -161,14 +160,18 @@ export const ColumnHeader: FC<ColumnHeaderProps> = (props: ColumnHeaderProps) =>
161160
fontWeight: 'normal',
162161
cursor: 'pointer',
163162
height: '100%',
164-
overflowX: 'hidden'
163+
overflowX: 'hidden',
164+
padding: `0 0.5rem`
165165
};
166166
if (isResizable) {
167167
modifiedStyles.maxWidth = `calc(100% - 16px)`;
168168
}
169169
if (dragOver) {
170170
modifiedStyles.borderLeft = `3px solid ${ThemingParameters.sapSelectedColor}`;
171171
}
172+
if (column.id === '__ui5wcr__internal_highlight_column' || column.id === '__ui5wcr__internal_selection_column') {
173+
modifiedStyles.padding = 0;
174+
}
172175
return modifiedStyles;
173176
}, [isResizable, dragOver]);
174177

packages/main/src/components/AnalyticalTable/__snapshots__/AnalyticalTable.test.tsx.snap

Lines changed: 119 additions & 119 deletions
Large diffs are not rendered by default.

packages/main/src/components/AnalyticalTable/defaults/Column/Expandable.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CssSizeVariables } from '@ui5/webcomponents-react-base/lib/CssSizeVariables';
22
import { Icon } from '@ui5/webcomponents-react/lib/Icon';
33
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
4+
import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior';
45
import React, { CSSProperties } from 'react';
56

67
const tableGroupExpandCollapseIcon = {
@@ -32,13 +33,15 @@ export const Expandable = (props) => {
3233
row,
3334
column,
3435
columns,
35-
webComponentsReactProperties: { selectionMode, noSelectionColumn }
36+
webComponentsReactProperties: { selectionMode, selectionBehavior }
3637
} = props;
3738

3839
const tableColumns =
39-
selectionMode === TableSelectionMode.NONE || noSelectionColumn
40+
selectionMode === TableSelectionMode.NONE || selectionBehavior === TableSelectionBehavior.ROW_ONLY
4041
? columns
41-
: columns.filter(({ id }) => id !== '__ui5wcr__internal_selection_column' && id !== '__ui5wcr__internal_highlight_column');
42+
: columns.filter(
43+
({ id }) => id !== '__ui5wcr__internal_selection_column' && id !== '__ui5wcr__internal_highlight_column'
44+
);
4245

4346
const columnIndex = tableColumns.findIndex((col) => col.id === column.id);
4447

packages/main/src/components/AnalyticalTable/demo/demo.stories.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { AnalyticalTable } from '@ui5/webcomponents-react/lib/AnalyticalTable';
44
import { Button } from '@ui5/webcomponents-react/lib/Button';
55
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
66
import { TextAlign } from '@ui5/webcomponents-react/lib/TextAlign';
7+
import { TableScaleWidthMode } from '@ui5/webcomponents-react/lib/TableScaleWidthMode';
8+
import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior';
79
import { Title } from '@ui5/webcomponents-react/lib/Title';
810
import React from 'react';
9-
import { TableScaleWidthMode } from '../../../enums/TableScaleWidthMode';
1011
import generateData from './generateData';
1112

1213
const columns = [
@@ -88,12 +89,16 @@ export const defaultTable = () => {
8889
rowHeight={number('rowHeight', 44)}
8990
selectedRowIds={object('selectedRowIds', { 3: true })}
9091
onColumnsReordered={action('onColumnsReordered')}
91-
noSelectionColumn={boolean('noSelectionColumn', false)}
9292
withRowHighlight={boolean('withRowHighlight', true)}
9393
highlightField={text('highlightField', 'status')}
9494
infiniteScroll={boolean('infiniteScroll', true)}
9595
infiniteScrollThreshold={number('infiniteScrollThreshold', 20)}
9696
onLoadMore={action('onLoadMore')}
97+
selectionBehavior={select<TableSelectionBehavior>(
98+
'selectionBehavior',
99+
TableSelectionBehavior,
100+
TableSelectionBehavior.ROW
101+
)}
97102
/>
98103
</div>
99104
);
@@ -121,6 +126,11 @@ export const treeTable = () => {
121126
onRowExpandChange={action('onRowExpandChange')}
122127
subRowsKey={text('subRowsKey', 'subRows')}
123128
selectedRowIds={object('selectedRowIds', { 3: true })}
129+
selectionBehavior={select<TableSelectionBehavior>(
130+
'selectionBehavior',
131+
TableSelectionBehavior,
132+
TableSelectionBehavior.ROW
133+
)}
124134
isTreeTable={boolean('isTreeTable', true)}
125135
/>
126136
);

packages/main/src/components/AnalyticalTable/hooks/useRowSelectionColumn.tsx

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,45 @@
1+
import { CssSizeVariablesNames } from '@ui5/webcomponents-react-base/lib/CssSizeVariables';
12
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils';
23
import { CheckBox } from '@ui5/webcomponents-react/lib/CheckBox';
4+
import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior';
35
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
46
import React from 'react';
57
import { PluginHook } from 'react-table';
68

7-
const divStyle = { width: '100%', height: '100%' };
9+
const divStyle = { width: '100%', height: '100%', cursor: 'pointer' };
810
const customCheckBoxStyling = {
9-
'--_ui5_checkbox_compact_width_height': 'var(--_ui5_checkbox_compact_inner_size)',
10-
'--_ui5_checkbox_width_height': 'var(--_ui5_checkbox_inner_width_height)',
11+
cursor: 'pointer',
1112
verticalAlign: 'middle'
1213
};
13-
const noop = () => {
14-
// do nothing
15-
};
1614

1715
export const useRowSelectionColumn: PluginHook<{}> = (hooks) => {
1816
hooks.columns.push((columns, { instance }) => {
19-
const { selectionMode, noSelectionColumn, onRowSelected } = instance.webComponentsReactProperties;
17+
const { webComponentsReactProperties } = instance;
18+
const { selectionMode, onRowSelected, selectionBehavior, tableRef } = webComponentsReactProperties;
2019

21-
if (selectionMode === TableSelectionMode.NONE || noSelectionColumn) {
20+
if (selectionMode === TableSelectionMode.NONE || selectionBehavior === TableSelectionBehavior.ROW_ONLY) {
2221
return columns;
2322
}
2423

2524
const toggleAllRowsSelected = (e) => {
26-
const allRowsSelected = e.detail.checked;
25+
const allRowsSelected = e.target.checked;
2726
instance.toggleAllRowsSelected(allRowsSelected);
2827
if (typeof onRowSelected === 'function') {
29-
onRowSelected(enrichEventWithDetails(e, { allRowsSelected }));
28+
onRowSelected(
29+
//cannot use instance.selectedFlatRows here as it only returns all rows on the first level
30+
enrichEventWithDetails(e, { allRowsSelected, selectedFlatRows: allRowsSelected ? instance.flatRows : [] })
31+
);
3032
}
3133
};
3234

35+
const selectionColumnWidth = tableRef.current
36+
? parseInt(
37+
getComputedStyle(tableRef.current).getPropertyValue(
38+
`--${CssSizeVariablesNames.sapWcrAnalyticalTableSelectionColumnWidth}`
39+
),
40+
10
41+
)
42+
: 47;
3343
return [
3444
// Let's make a column for selection
3545
{
@@ -40,8 +50,9 @@ export const useRowSelectionColumn: PluginHook<{}> = (hooks) => {
4050
disableGroupBy: true,
4151
disableResizing: true,
4252
canReorder: false,
43-
width: 36,
44-
minWidth: 36,
53+
width: selectionColumnWidth,
54+
minWidth: selectionColumnWidth,
55+
maxWidth: selectionColumnWidth,
4556
// The header can use the table's getToggleAllRowsSelectedProps method
4657
// to render a checkbox
4758
// eslint-disable-next-line react/prop-types,react/display-name
@@ -61,35 +72,40 @@ export const useRowSelectionColumn: PluginHook<{}> = (hooks) => {
6172
// to the render a checkbox
6273
// eslint-disable-next-line react/prop-types,react/display-name
6374
Cell: ({ row }) => {
75+
const handleCellClick = (e) => {
76+
if (TableSelectionBehavior.ROW_SELECTOR === selectionBehavior) {
77+
row.getRowProps().onClick(e, true);
78+
}
79+
};
6480
if (row.isGrouped && selectionMode === TableSelectionMode.SINGLE_SELECT) {
6581
return null;
6682
}
6783
if (selectionMode === TableSelectionMode.SINGLE_SELECT) {
68-
// eslint-disable-next-line react/prop-types
69-
return <div style={divStyle} onClick={row.toggleRowSelected} />;
84+
return <div style={divStyle} onClick={handleCellClick} />;
7085
}
71-
// eslint-disable-next-line react/prop-types
72-
return <CheckBox {...row.getToggleRowSelectedProps()} onChange={noop} style={customCheckBoxStyling} />;
86+
return (
87+
<CheckBox {...row.getToggleRowSelectedProps()} onChange={handleCellClick} style={customCheckBoxStyling} />
88+
);
7389
}
7490
},
7591
...columns
7692
];
7793
});
7894

7995
hooks.columnsDeps.push((deps, { instance: { state, webComponentsReactProperties } }) => {
80-
return [...deps, webComponentsReactProperties.selectionMode, webComponentsReactProperties.noSelectionColumn];
96+
return [...deps, webComponentsReactProperties.selectionMode, webComponentsReactProperties.selectionBehavior];
8197
});
8298

8399
hooks.visibleColumnsDeps.push((deps, { instance }) => [
84100
...deps,
85-
instance.webComponentsReactProperties.noSelectionColumn,
86-
instance.webComponentsReactProperties.selectionMode
101+
instance.webComponentsReactProperties.selectionMode,
102+
instance.webComponentsReactProperties.selectionBehavior
87103
]);
88104

89105
hooks.visibleColumns.push((columns, { instance: { webComponentsReactProperties } }) => {
90106
if (
91-
webComponentsReactProperties.noSelectionColumn ||
92-
webComponentsReactProperties.selectionMode === TableSelectionMode.NONE
107+
webComponentsReactProperties.selectionMode === TableSelectionMode.NONE ||
108+
webComponentsReactProperties.selectionBehavior === TableSelectionBehavior.ROW_ONLY
93109
) {
94110
return columns;
95111
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils';
2+
import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior';
3+
import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode';
4+
import { PluginHook } from 'react-table';
5+
6+
export const useSingleRowStateSelection: PluginHook<{}> = (hooks) => {
7+
hooks.getRowProps.push((passedRowProps, { instance, row }) => {
8+
const { webComponentsReactProperties, dispatch, toggleRowSelected, selectedFlatRows } = instance;
9+
const { isTreeTable, selectionMode, onRowSelected, selectionBehavior } = webComponentsReactProperties;
10+
const rowProps = { ...passedRowProps };
11+
const isEmptyRow = row.original?.emptyRow;
12+
13+
if ([TableSelectionMode.SINGLE_SELECT, TableSelectionMode.MULTI_SELECT].includes(selectionMode) && !isEmptyRow) {
14+
rowProps.onClick = (e, selectionCellClick = false) => {
15+
if (row.isGrouped || (TableSelectionBehavior.ROW_SELECTOR === selectionBehavior && !selectionCellClick)) {
16+
return;
17+
}
18+
if (isTreeTable) {
19+
if (selectionMode === TableSelectionMode.MULTI_SELECT) {
20+
dispatch({
21+
type: 'SET_SELECTED_ROWS',
22+
selectedIds: Object.assign({}, ...selectedFlatRows.map((item) => ({ [item.id]: true })), {
23+
[row.id]: !row.isSelected
24+
})
25+
});
26+
} else {
27+
dispatch({ type: 'SET_SELECTED_ROWS', selectedIds: { [row.id]: !row.isSelected } });
28+
}
29+
} else {
30+
row.toggleRowSelected();
31+
}
32+
if (typeof onRowSelected === 'function') {
33+
const payload = {
34+
row,
35+
isSelected: !row.isSelected
36+
};
37+
const payloadWithFlatRows = {
38+
...payload,
39+
selectedFlatRows: !row.isSelected
40+
? [...selectedFlatRows, row]
41+
: selectedFlatRows.filter((prevRow) => prevRow.id !== row.id)
42+
};
43+
onRowSelected(
44+
enrichEventWithDetails(e, TableSelectionMode.MULTI_SELECT === selectionMode ? payloadWithFlatRows : payload)
45+
);
46+
}
47+
if (selectionMode === TableSelectionMode.SINGLE_SELECT && !isTreeTable) {
48+
selectedFlatRows.forEach(({ id }) => {
49+
toggleRowSelected(id, false);
50+
});
51+
}
52+
};
53+
}
54+
55+
return rowProps;
56+
});
57+
};
58+
59+
useSingleRowStateSelection.pluginName = 'useSingleRowStateSelection';

packages/main/src/components/AnalyticalTable/hooks/useTableCellStyling.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const useTableCellStyling: PluginHook<{}> = (hooks) => {
5050
className += ` ${column.className}`;
5151
}
5252

53-
if (column.id === '__ui5wcr__internal_highlight_column') {
53+
if (column.id === '__ui5wcr__internal_highlight_column' || column.id === '__ui5wcr__internal_selection_column') {
5454
style.padding = 0;
5555
}
5656

@@ -59,7 +59,6 @@ export const useTableCellStyling: PluginHook<{}> = (hooks) => {
5959
style.boxSizing = 'border-box';
6060
style.width = `calc(${cellProps.style.width} - ${ThemingParameters.sapScrollBar_Dimension})`;
6161
}
62-
6362
return {
6463
...cellProps,
6564
className,

0 commit comments

Comments
 (0)