Skip to content

Commit

Permalink
feat(components/List): Disable item selection and disable "select all" (
Browse files Browse the repository at this point in the history
  • Loading branch information
yyanwang authored Mar 13, 2023
1 parent edec523 commit 44b408d
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-chicken-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@talend/react-components': minor
---

feat(components): List - disable item selection & disable "select all"
2 changes: 1 addition & 1 deletion packages/components/src/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Checkbox({ id, className, label, intermediate, ...props
className={classNames(
'checkbox tc-checkbox',
{
'tc-checkbox-disabled': props.disabled,
disabled: props.disabled,
},
className,
)}
Expand Down
41 changes: 40 additions & 1 deletion packages/components/src/List/ListComposition.stories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import { action } from '@storybook/addon-actions';

import { simpleCollection } from './ListComposition/collection';
Expand Down Expand Up @@ -698,6 +698,45 @@ export const SelectableItems = () => {
);
};

export const SelectableItemsWithDisabledItems = () => {
const { isSelected, onToggleAll, onToggleItem } = List.hooks.useCollectionSelection(
simpleCollection,
[],
'id',
);

const getRowState = item => (item.id === 1 ? { disabled: true } : {});
const isToggleAllDisabled = items => {
for (let i = 0; i < items.length; i++) {
if (getRowState(items[i]).disabled) {
return true;
}
}
return false;
};
return (
<div className="virtualized-list">
<h1>List with selection disabled items</h1>
<p>
You can pass <b>getRowState</b> for disable a certain row. <br />
<b>isToggleAllDisabled</b> is used for disable "Select all" checkbox
</p>

<section style={{ height: '50vh' }}>
<List.Manager id="my-list" collection={simpleCollection}>
<CustomList
isSelected={isSelected}
onToggleAll={onToggleAll}
selectionToggle={(_, group) => onToggleItem(group)}
getRowState={getRowState}
isToggleAllDisabled={isToggleAllDisabled}
/>
</List.Manager>
</section>
</div>
);
};

export const SelectableItemsActionBar = () => {
const { isSelected, onToggleAll, onToggleItem } = List.hooks.useCollectionSelection(
simpleCollection,
Expand Down
3 changes: 3 additions & 0 deletions packages/components/src/List/__snapshots__/List.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ exports[`List should not render the toolbar without toolbar props 1`] = `
"getRowState": undefined,
"id": undefined,
"isSelected": [Function],
"isToggleAllDisabled": undefined,
"label": "Select this element",
"onChange": [MockFunction],
"onToggleAll": [MockFunction],
Expand Down Expand Up @@ -1274,6 +1275,7 @@ exports[`List should render 1`] = `
"getRowState": undefined,
"id": undefined,
"isSelected": [Function],
"isToggleAllDisabled": undefined,
"label": "Select this element",
"onChange": [MockFunction],
"onToggleAll": [MockFunction],
Expand Down Expand Up @@ -2303,6 +2305,7 @@ exports[`List should render id if provided 1`] = `
"getRowState": undefined,
"id": "context",
"isSelected": [Function],
"isToggleAllDisabled": undefined,
"label": "Select this element",
"onChange": [MockFunction],
"onToggleAll": [MockFunction],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ exports[`Toggle should render a checked Toggle 1`] = `

exports[`Toggle should render a disabled Toggle 1`] = `
<div
className="checkbox tc-checkbox tc-checkbox-disabled switch tc-toggle tc-toggle-disabled"
className="checkbox tc-checkbox disabled switch tc-toggle tc-toggle-disabled"
>
<label
htmlFor="id"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class CellCheckbox extends React.Component {

return (
<div className={classnames('tc-list-checkbox', theme['tc-list-checkbox'])}>
<div className="checkbox">
<div className={classnames('checkbox', { disabled })}>
<label htmlFor={id && `${id}-${rowIndex}-check`}>
<input
id={id && `${id}-${rowIndex}-check`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
import { render, screen } from '@testing-library/react';

import CellCheckbox from './CellCheckbox.component';

Expand Down Expand Up @@ -28,6 +29,20 @@ describe('CellActions', () => {
expect(wrapper.getElement()).toMatchSnapshot();
});

it('should render disabled checkbox', () => {
// when
render(
<CellCheckbox
cellData={false}
columnData={{ ...columnData, getRowState: () => ({ disabled: true }) }}
rowIndex={25}
/>,
);

// then
expect(screen.getByRole('checkbox')).toBeDisabled();
});

it('should render radio button', () => {
// when
const wrapper = shallow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import I18N_DOMAIN_COMPONENTS from '../../constants';
*/
function HeaderCheckbox({ columnData }) {
const { t } = useTranslation(I18N_DOMAIN_COMPONENTS);
const { id, onToggleAll, collection, isToggleAllDisabled, isSelected } = columnData;

if (!columnData.onToggleAll) {
return null;
}

const { id, onToggleAll, collection, isSelected } = columnData;
const checked = useMemo(
() => collection.length > 0 && collection.every(isSelected),
[collection, isSelected],
Expand All @@ -30,6 +26,13 @@ function HeaderCheckbox({ columnData }) {
}, [collection, isSelected]);

const title = t('LIST_SELECT_ALL', { defaultValue: 'Select all' });

const disabled = !collection.length || (isToggleAllDisabled && isToggleAllDisabled(collection));

if (!columnData.onToggleAll) {
return null;
}

return (
<form className={classnames('tc-list-checkbox', theme['tc-list-checkbox'])}>
<div className="checkbox" title={title}>
Expand All @@ -40,7 +43,7 @@ function HeaderCheckbox({ columnData }) {
onChange={onToggleAll}
checked={checked}
intermediate={partial}
disabled={!collection.length}
disabled={disabled}
data-feature="list.select_all"
/>
<span className="sr-only">{title}</span>
Expand All @@ -58,6 +61,8 @@ HeaderCheckbox.propTypes = {
id: PropTypes.string,
// all items in list, used by onToggleAll callback.
collection: PropTypes.array.isRequired,
// The function is to check if toggle all is disabled.
isToggleAllDisabled: PropTypes.func,
// The function is to check if item is selected.
isSelected: PropTypes.func.isRequired,
// The onToggleAll callback triggered on header checkbox toggle.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { render, screen } from '@testing-library/react';

import toJson from 'enzyme-to-json';
import HeaderCheckbox from './HeaderCheckbox.component';

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

it('should render disabled checkbox when isToggleAllDisabled() is true', () => {
// when
render(<HeaderCheckbox columnData={{ ...columnData, isToggleAllDisabled: () => true }} />);

// then
expect(screen.getByRole('checkbox')).toBeDisabled();
});

it('should render a checked checkbox on header', () => {
// when
const wrapper = shallow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function VirtualizedList(props) {
isSelected,
getRowState,
inProgress,
isToggleAllDisabled,
onRowClick,
onRowDoubleClick,
onRowsRendered,
Expand Down Expand Up @@ -56,6 +57,7 @@ function VirtualizedList(props) {
children,
collection,
isSelected,
isToggleAllDisabled,
onToggleAll,
selectionToggle,
getRowState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ exports[`tablerow #insertSelectionConfiguration should insert selection column w
columnData={
{
"collection": undefined,
"getRowState": undefined,
"isSelected": [MockFunction],
"isToggleAllDisabled": undefined,
"label": "Select this element",
"onChange": [MockFunction],
"onToggleAll": undefined,
Expand Down
19 changes: 15 additions & 4 deletions packages/components/src/VirtualizedList/utils/tablerow.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ import { internalIds } from './constants';
* Insert a checkbox column configuration to select a row.
*/
export function insertSelectionConfiguration(props) {
const { collection, children, isSelected, onToggleAll, selectionToggle, selectionMode } = props;
const {
collection,
children,
getRowState,
isSelected,
isToggleAllDisabled,
onToggleAll,
selectionMode,
selectionToggle,
} = props;
let contentsConfiguration = React.Children.toArray(children);
if (selectionToggle && isSelected) {
const toggleColumn = (
Expand All @@ -26,12 +35,14 @@ export function insertSelectionConfiguration(props) {
flexGrow={0}
cellDataGetter={({ rowData }) => isSelected(rowData)}
columnData={{
collection,
getRowState,
isSelected,
isToggleAllDisabled,
label: 'Select this element',
onChange: selectionToggle,
selectionMode,
onToggleAll,
collection,
isSelected,
selectionMode,
}}
{...CellCheckboxRenderer}
{...HeaderCheckboxRenderer}
Expand Down
50 changes: 44 additions & 6 deletions packages/components/src/VirtualizedList/utils/tablerow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ describe('tablerow', () => {
const isSelected = jest.fn();
const selectionToggle = jest.fn();
const children = [
<VirtualizedList.Content label="Id" dataKey="id" width={50} />,
<VirtualizedList.Content label="Name" dataKey="name" width={350} />,
<VirtualizedList.Content key={0} label="Id" dataKey="id" width={50} />,
<VirtualizedList.Content key={1} label="Name" dataKey="name" width={350} />,
];

// when
const result = insertSelectionConfiguration({ isSelected, selectionToggle, children });
const result = insertSelectionConfiguration({
isSelected,
selectionToggle,
children,
});

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

// when
Expand All @@ -33,6 +37,30 @@ describe('tablerow', () => {
// then
expect(result).toMatchSnapshot();
});
it('should pass selection props to Column', () => {
// given
const isSelected = jest.fn();
const selectionToggle = jest.fn();
const getRowState = jest.fn();
const isToggleAllDisabled = jest.fn();
const children = [
<VirtualizedList.Content key={0} label="Id" dataKey="id" width={50} />,
<VirtualizedList.Content key={1} label="Name" dataKey="name" width={350} />,
];

// when
const result = insertSelectionConfiguration({
getRowState,
isSelected,
isToggleAllDisabled,
selectionToggle,
children,
});

// then
expect(result[0].props.columnData.getRowState).toBe(getRowState);
expect(result[0].props.columnData.isToggleAllDisabled).toBe(isToggleAllDisabled);
});
});

describe('#toColumns', () => {
Expand All @@ -41,6 +69,7 @@ describe('tablerow', () => {
const theme = { header: 'theme-header-classname' };
const children = [
<VirtualizedList.Content
key={0}
label="Id"
dataKey="id"
headerClassName="my-header-classname"
Expand All @@ -59,7 +88,13 @@ describe('tablerow', () => {
// given
const theme = { cell: 'theme-classname' };
const children = [
<VirtualizedList.Content label="Id" dataKey="id" className="my-classname" width={50} />,
<VirtualizedList.Content
key={0}
label="Id"
dataKey="id"
className="my-classname"
width={50}
/>,
];

// when
Expand All @@ -74,6 +109,7 @@ describe('tablerow', () => {
const id = 'my-id';
const children = [
<VirtualizedList.Content
key={0}
label="Id"
dataKey="id"
columnData={{ custom: 'lol' }}
Expand All @@ -92,6 +128,7 @@ describe('tablerow', () => {
const id = 'my-id';
const children = [
<VirtualizedList.Content
key={0}
label="Id"
dataKey="id"
columnData={{ custom: 'lol' }}
Expand All @@ -113,6 +150,7 @@ describe('tablerow', () => {
const id = 'my-id';
const children = [
<VirtualizedList.Content
key={0}
label="Id"
dataKey="id"
columnData={{ custom: 'lol' }}
Expand Down

0 comments on commit 44b408d

Please sign in to comment.