Skip to content

Commit 44b408d

Browse files
authored
feat(components/List): Disable item selection and disable "select all" (#4616)
1 parent edec523 commit 44b408d

File tree

13 files changed

+150
-20
lines changed

13 files changed

+150
-20
lines changed

.changeset/shaggy-chicken-vanish.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@talend/react-components': minor
3+
---
4+
5+
feat(components): List - disable item selection & disable "select all"

packages/components/src/Checkbox/Checkbox.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default function Checkbox({ id, className, label, intermediate, ...props
2323
className={classNames(
2424
'checkbox tc-checkbox',
2525
{
26-
'tc-checkbox-disabled': props.disabled,
26+
disabled: props.disabled,
2727
},
2828
className,
2929
)}

packages/components/src/List/ListComposition.stories.js

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react';
1+
import React from 'react';
22
import { action } from '@storybook/addon-actions';
33

44
import { simpleCollection } from './ListComposition/collection';
@@ -698,6 +698,45 @@ export const SelectableItems = () => {
698698
);
699699
};
700700

701+
export const SelectableItemsWithDisabledItems = () => {
702+
const { isSelected, onToggleAll, onToggleItem } = List.hooks.useCollectionSelection(
703+
simpleCollection,
704+
[],
705+
'id',
706+
);
707+
708+
const getRowState = item => (item.id === 1 ? { disabled: true } : {});
709+
const isToggleAllDisabled = items => {
710+
for (let i = 0; i < items.length; i++) {
711+
if (getRowState(items[i]).disabled) {
712+
return true;
713+
}
714+
}
715+
return false;
716+
};
717+
return (
718+
<div className="virtualized-list">
719+
<h1>List with selection disabled items</h1>
720+
<p>
721+
You can pass <b>getRowState</b> for disable a certain row. <br />
722+
<b>isToggleAllDisabled</b> is used for disable "Select all" checkbox
723+
</p>
724+
725+
<section style={{ height: '50vh' }}>
726+
<List.Manager id="my-list" collection={simpleCollection}>
727+
<CustomList
728+
isSelected={isSelected}
729+
onToggleAll={onToggleAll}
730+
selectionToggle={(_, group) => onToggleItem(group)}
731+
getRowState={getRowState}
732+
isToggleAllDisabled={isToggleAllDisabled}
733+
/>
734+
</List.Manager>
735+
</section>
736+
</div>
737+
);
738+
};
739+
701740
export const SelectableItemsActionBar = () => {
702741
const { isSelected, onToggleAll, onToggleItem } = List.hooks.useCollectionSelection(
703742
simpleCollection,

packages/components/src/List/__snapshots__/List.test.js.snap

+3
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ exports[`List should not render the toolbar without toolbar props 1`] = `
269269
"getRowState": undefined,
270270
"id": undefined,
271271
"isSelected": [Function],
272+
"isToggleAllDisabled": undefined,
272273
"label": "Select this element",
273274
"onChange": [MockFunction],
274275
"onToggleAll": [MockFunction],
@@ -1274,6 +1275,7 @@ exports[`List should render 1`] = `
12741275
"getRowState": undefined,
12751276
"id": undefined,
12761277
"isSelected": [Function],
1278+
"isToggleAllDisabled": undefined,
12771279
"label": "Select this element",
12781280
"onChange": [MockFunction],
12791281
"onToggleAll": [MockFunction],
@@ -2303,6 +2305,7 @@ exports[`List should render id if provided 1`] = `
23032305
"getRowState": undefined,
23042306
"id": "context",
23052307
"isSelected": [Function],
2308+
"isToggleAllDisabled": undefined,
23062309
"label": "Select this element",
23072310
"onChange": [MockFunction],
23082311
"onToggleAll": [MockFunction],

packages/components/src/Toggle/__snapshots__/Toggle.snapshot.test.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ exports[`Toggle should render a checked Toggle 1`] = `
137137

138138
exports[`Toggle should render a disabled Toggle 1`] = `
139139
<div
140-
className="checkbox tc-checkbox tc-checkbox-disabled switch tc-toggle tc-toggle-disabled"
140+
className="checkbox tc-checkbox disabled switch tc-toggle tc-toggle-disabled"
141141
>
142142
<label
143143
htmlFor="id"

packages/components/src/VirtualizedList/CellCheckbox/CellCheckbox.component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class CellCheckbox extends React.Component {
2626

2727
return (
2828
<div className={classnames('tc-list-checkbox', theme['tc-list-checkbox'])}>
29-
<div className="checkbox">
29+
<div className={classnames('checkbox', { disabled })}>
3030
<label htmlFor={id && `${id}-${rowIndex}-check`}>
3131
<input
3232
id={id && `${id}-${rowIndex}-check`}

packages/components/src/VirtualizedList/CellCheckbox/CellCheckbox.test.js

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { shallow } from 'enzyme';
3+
import { render, screen } from '@testing-library/react';
34

45
import CellCheckbox from './CellCheckbox.component';
56

@@ -28,6 +29,20 @@ describe('CellActions', () => {
2829
expect(wrapper.getElement()).toMatchSnapshot();
2930
});
3031

32+
it('should render disabled checkbox', () => {
33+
// when
34+
render(
35+
<CellCheckbox
36+
cellData={false}
37+
columnData={{ ...columnData, getRowState: () => ({ disabled: true }) }}
38+
rowIndex={25}
39+
/>,
40+
);
41+
42+
// then
43+
expect(screen.getByRole('checkbox')).toBeDisabled();
44+
});
45+
3146
it('should render radio button', () => {
3247
// when
3348
const wrapper = shallow(

packages/components/src/VirtualizedList/HeaderCheckbox/HeaderCheckbox.component.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@ import I18N_DOMAIN_COMPONENTS from '../../constants';
1111
*/
1212
function HeaderCheckbox({ columnData }) {
1313
const { t } = useTranslation(I18N_DOMAIN_COMPONENTS);
14+
const { id, onToggleAll, collection, isToggleAllDisabled, isSelected } = columnData;
1415

15-
if (!columnData.onToggleAll) {
16-
return null;
17-
}
18-
19-
const { id, onToggleAll, collection, isSelected } = columnData;
2016
const checked = useMemo(
2117
() => collection.length > 0 && collection.every(isSelected),
2218
[collection, isSelected],
@@ -30,6 +26,13 @@ function HeaderCheckbox({ columnData }) {
3026
}, [collection, isSelected]);
3127

3228
const title = t('LIST_SELECT_ALL', { defaultValue: 'Select all' });
29+
30+
const disabled = !collection.length || (isToggleAllDisabled && isToggleAllDisabled(collection));
31+
32+
if (!columnData.onToggleAll) {
33+
return null;
34+
}
35+
3336
return (
3437
<form className={classnames('tc-list-checkbox', theme['tc-list-checkbox'])}>
3538
<div className="checkbox" title={title}>
@@ -40,7 +43,7 @@ function HeaderCheckbox({ columnData }) {
4043
onChange={onToggleAll}
4144
checked={checked}
4245
intermediate={partial}
43-
disabled={!collection.length}
46+
disabled={disabled}
4447
data-feature="list.select_all"
4548
/>
4649
<span className="sr-only">{title}</span>
@@ -58,6 +61,8 @@ HeaderCheckbox.propTypes = {
5861
id: PropTypes.string,
5962
// all items in list, used by onToggleAll callback.
6063
collection: PropTypes.array.isRequired,
64+
// The function is to check if toggle all is disabled.
65+
isToggleAllDisabled: PropTypes.func,
6166
// The function is to check if item is selected.
6267
isSelected: PropTypes.func.isRequired,
6368
// The onToggleAll callback triggered on header checkbox toggle.

packages/components/src/VirtualizedList/HeaderCheckbox/HeaderCheckbox.test.js

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22
import { shallow } from 'enzyme';
3+
import { render, screen } from '@testing-library/react';
4+
35
import toJson from 'enzyme-to-json';
46
import HeaderCheckbox from './HeaderCheckbox.component';
57

@@ -44,6 +46,14 @@ describe('Header "Select All" checkbox', () => {
4446
expect(checkbox.prop('disabled')).toBe(true);
4547
});
4648

49+
it('should render disabled checkbox when isToggleAllDisabled() is true', () => {
50+
// when
51+
render(<HeaderCheckbox columnData={{ ...columnData, isToggleAllDisabled: () => true }} />);
52+
53+
// then
54+
expect(screen.getByRole('checkbox')).toBeDisabled();
55+
});
56+
4757
it('should render a checked checkbox on header', () => {
4858
// when
4959
const wrapper = shallow(

packages/components/src/VirtualizedList/VirtualizedList.component.js

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function VirtualizedList(props) {
2929
isSelected,
3030
getRowState,
3131
inProgress,
32+
isToggleAllDisabled,
3233
onRowClick,
3334
onRowDoubleClick,
3435
onRowsRendered,
@@ -56,6 +57,7 @@ function VirtualizedList(props) {
5657
children,
5758
collection,
5859
isSelected,
60+
isToggleAllDisabled,
5961
onToggleAll,
6062
selectionToggle,
6163
getRowState,

packages/components/src/VirtualizedList/utils/__snapshots__/tablerow.test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ exports[`tablerow #insertSelectionConfiguration should insert selection column w
3737
columnData={
3838
{
3939
"collection": undefined,
40+
"getRowState": undefined,
4041
"isSelected": [MockFunction],
42+
"isToggleAllDisabled": undefined,
4143
"label": "Select this element",
4244
"onChange": [MockFunction],
4345
"onToggleAll": undefined,

packages/components/src/VirtualizedList/utils/tablerow.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@ import { internalIds } from './constants';
1212
* Insert a checkbox column configuration to select a row.
1313
*/
1414
export function insertSelectionConfiguration(props) {
15-
const { collection, children, isSelected, onToggleAll, selectionToggle, selectionMode } = props;
15+
const {
16+
collection,
17+
children,
18+
getRowState,
19+
isSelected,
20+
isToggleAllDisabled,
21+
onToggleAll,
22+
selectionMode,
23+
selectionToggle,
24+
} = props;
1625
let contentsConfiguration = React.Children.toArray(children);
1726
if (selectionToggle && isSelected) {
1827
const toggleColumn = (
@@ -26,12 +35,14 @@ export function insertSelectionConfiguration(props) {
2635
flexGrow={0}
2736
cellDataGetter={({ rowData }) => isSelected(rowData)}
2837
columnData={{
38+
collection,
39+
getRowState,
40+
isSelected,
41+
isToggleAllDisabled,
2942
label: 'Select this element',
3043
onChange: selectionToggle,
31-
selectionMode,
3244
onToggleAll,
33-
collection,
34-
isSelected,
45+
selectionMode,
3546
}}
3647
{...CellCheckboxRenderer}
3748
{...HeaderCheckboxRenderer}

packages/components/src/VirtualizedList/utils/tablerow.test.js

+44-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ describe('tablerow', () => {
99
const isSelected = jest.fn();
1010
const selectionToggle = jest.fn();
1111
const children = [
12-
<VirtualizedList.Content label="Id" dataKey="id" width={50} />,
13-
<VirtualizedList.Content label="Name" dataKey="name" width={350} />,
12+
<VirtualizedList.Content key={0} label="Id" dataKey="id" width={50} />,
13+
<VirtualizedList.Content key={1} label="Name" dataKey="name" width={350} />,
1414
];
1515

1616
// when
17-
const result = insertSelectionConfiguration({ isSelected, selectionToggle, children });
17+
const result = insertSelectionConfiguration({
18+
isSelected,
19+
selectionToggle,
20+
children,
21+
});
1822

1923
// then
2024
expect(result).toMatchSnapshot();
@@ -23,8 +27,8 @@ describe('tablerow', () => {
2327
it('should NOT insert selection column when selection callback is NOT provided', () => {
2428
// given
2529
const children = [
26-
<VirtualizedList.Content label="Id" dataKey="id" width={50} />,
27-
<VirtualizedList.Content label="Name" dataKey="name" width={350} />,
30+
<VirtualizedList.Content key={0} label="Id" dataKey="id" width={50} />,
31+
<VirtualizedList.Content key={1} label="Name" dataKey="name" width={350} />,
2832
];
2933

3034
// when
@@ -33,6 +37,30 @@ describe('tablerow', () => {
3337
// then
3438
expect(result).toMatchSnapshot();
3539
});
40+
it('should pass selection props to Column', () => {
41+
// given
42+
const isSelected = jest.fn();
43+
const selectionToggle = jest.fn();
44+
const getRowState = jest.fn();
45+
const isToggleAllDisabled = jest.fn();
46+
const children = [
47+
<VirtualizedList.Content key={0} label="Id" dataKey="id" width={50} />,
48+
<VirtualizedList.Content key={1} label="Name" dataKey="name" width={350} />,
49+
];
50+
51+
// when
52+
const result = insertSelectionConfiguration({
53+
getRowState,
54+
isSelected,
55+
isToggleAllDisabled,
56+
selectionToggle,
57+
children,
58+
});
59+
60+
// then
61+
expect(result[0].props.columnData.getRowState).toBe(getRowState);
62+
expect(result[0].props.columnData.isToggleAllDisabled).toBe(isToggleAllDisabled);
63+
});
3664
});
3765

3866
describe('#toColumns', () => {
@@ -41,6 +69,7 @@ describe('tablerow', () => {
4169
const theme = { header: 'theme-header-classname' };
4270
const children = [
4371
<VirtualizedList.Content
72+
key={0}
4473
label="Id"
4574
dataKey="id"
4675
headerClassName="my-header-classname"
@@ -59,7 +88,13 @@ describe('tablerow', () => {
5988
// given
6089
const theme = { cell: 'theme-classname' };
6190
const children = [
62-
<VirtualizedList.Content label="Id" dataKey="id" className="my-classname" width={50} />,
91+
<VirtualizedList.Content
92+
key={0}
93+
label="Id"
94+
dataKey="id"
95+
className="my-classname"
96+
width={50}
97+
/>,
6398
];
6499

65100
// when
@@ -74,6 +109,7 @@ describe('tablerow', () => {
74109
const id = 'my-id';
75110
const children = [
76111
<VirtualizedList.Content
112+
key={0}
77113
label="Id"
78114
dataKey="id"
79115
columnData={{ custom: 'lol' }}
@@ -92,6 +128,7 @@ describe('tablerow', () => {
92128
const id = 'my-id';
93129
const children = [
94130
<VirtualizedList.Content
131+
key={0}
95132
label="Id"
96133
dataKey="id"
97134
columnData={{ custom: 'lol' }}
@@ -113,6 +150,7 @@ describe('tablerow', () => {
113150
const id = 'my-id';
114151
const children = [
115152
<VirtualizedList.Content
153+
key={0}
116154
label="Id"
117155
dataKey="id"
118156
columnData={{ custom: 'lol' }}

0 commit comments

Comments
 (0)