Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiBasicTable][EuiInMemoryTable] Support controlled selection API #7321

Merged
merged 7 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import React, { useState, useRef, ReactNode } from 'react';
import React, { useState, ReactNode } from 'react';
import { faker } from '@faker-js/faker';
import { formatDate, Random } from '../../../../../src/services';
import { Random } from '../../../../../src/services';

import {
EuiInMemoryTable,
EuiBasicTableColumn,
EuiTableSelectionType,
EuiSearchBarProps,
EuiLink,
EuiHealth,
EuiButton,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
} from '../../../../../src/components';

type User = {
id: number;
firstName: string | null | undefined;
lastName: string;
github: string;
dateOfBirth: Date;
online: boolean;
location: {
city: string;
Expand All @@ -36,8 +31,6 @@ for (let i = 0; i < 20; i++) {
id: i + 1,
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
github: faker.internet.userName(),
dateOfBirth: faker.date.past(),
online: faker.datatype.boolean(),
location: {
city: faker.location.city(),
Expand All @@ -46,17 +39,6 @@ for (let i = 0; i < 20; i++) {
});
}

const onlineUsers = userData.filter((user) => user.online);

const deleteUsersByIds = (...ids: number[]) => {
ids.forEach((id) => {
const index = userData.findIndex((user) => user.id === id);
if (index >= 0) {
userData.splice(index, 1);
}
});
};

const columns: Array<EuiBasicTableColumn<User>> = [
{
field: 'firstName',
Expand All @@ -83,23 +65,6 @@ const columns: Array<EuiBasicTableColumn<User>> = [
show: false,
},
},
{
field: 'github',
name: 'Github',
render: (username: User['github']) => (
<EuiLink href="#" target="_blank">
{username}
</EuiLink>
),
},
{
field: 'dateOfBirth',
name: 'Date of Birth',
dataType: 'date',
render: (dateOfBirth: User['dateOfBirth']) =>
formatDate(dateOfBirth, 'dobLong'),
sortable: true,
},
{
field: 'location',
name: 'Location',
Expand Down Expand Up @@ -153,7 +118,6 @@ export default () => {

const [selection, setSelection] = useState<User[]>([]);
const [error, setError] = useState<string | undefined>();
const tableRef = useRef<EuiInMemoryTable<User> | null>(null);

const loadUsers = () => {
setMessage('Loading users...');
Expand All @@ -168,6 +132,8 @@ export default () => {
}, random.number({ min: 0, max: 3000 }));
};

const onlineUsers = users.filter((user) => user.online);

const loadUsersWithError = () => {
setMessage('Loading users...');
setLoading(true);
Expand All @@ -187,7 +153,23 @@ export default () => {
}

const onClick = () => {
deleteUsersByIds(...selection.map((user) => user.id));
const deleteUsersByIds = (users: User[], ids: number[]) => {
const updatedUsers = [...users];
ids.forEach((id) => {
const index = updatedUsers.findIndex((user) => user.id === id);
if (index >= 0) {
updatedUsers.splice(index, 1);
}
});
return updatedUsers;
};

setUsers((users) =>
deleteUsersByIds(
users,
selection.map((user) => user.id)
)
);
setSelection([]);
};

Expand Down Expand Up @@ -256,27 +238,19 @@ export default () => {
selectableMessage: (selectable) =>
!selectable ? 'User is currently offline' : '',
onSelectionChange: (selection) => setSelection(selection),
initialSelected: onlineUsers,
};

const onSelection = () => {
tableRef.current?.setSelection(onlineUsers);
selected: selection,
};

return (
<>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton onClick={onSelection}>Select online users</EuiButton>
</EuiFlexItem>
<EuiFlexItem />
</EuiFlexGroup>
<EuiButton onClick={() => setSelection(onlineUsers)}>
Select online users
</EuiButton>

<EuiSpacer size="l" />

<EuiInMemoryTable
tableCaption="Demo of EuiInMemoryTable with selection"
ref={tableRef}
tableCaption="Demo of EuiInMemoryTable with controlled selection"
items={users}
itemId="id"
error={error}
Expand Down
102 changes: 41 additions & 61 deletions src-docs/src/views/tables/in_memory/in_memory_selection_section.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,50 @@
import React from 'react';
import { EuiCode } from '../../../../../src/components';
import { GuideSectionTypes } from '../../../components';
import { EuiCode, EuiSpacer } from '../../../../../src/components';

import Table from './in_memory_selection';
import { EuiInMemoryTable } from '../../../../../src/components/basic_table/in_memory_table';
import {
Criteria,
CriteriaWithPagination,
} from '!!prop-loader!../../../../../src/components/basic_table/basic_table';
import { Pagination } from '../paginated/_props';
import {
EuiTableFieldDataColumnType,
EuiTableComputedColumnType,
EuiTableActionsColumnType,
EuiTableSelectionType,
EuiTableSortingType,
} from '!!prop-loader!../../../../../src/components/basic_table/table_types';
import { CustomItemAction } from '!!prop-loader!../../../../../src/components/basic_table/action_types';
import {
DefaultItemActionProps as DefaultItemAction,
SearchProps as Search,
SearchFilterConfigProps as SearchFilterConfig,
} from '../props/props';
import { FieldValueOptionType } from '!!prop-loader!../../../../../src/components/search_bar/filters/field_value_selection_filter';
import { FieldValueToggleGroupFilterItemType } from '!prop-loader!../../../../../src/components/search_bar/filters/field_value_toggle_group_filter.tsx';
import { ConditionallyControlledDemo } from '../../tables/selection/selection_section';

const source = require('!!raw-loader!./in_memory_selection');
import Controlled from './in_memory_selection_controlled';
const controlledSource = require('!!raw-loader!./in_memory_selection_controlled');

import Uncontrolled from './in_memory_selection_uncontrolled';
const uncontrolledSource = require('!!raw-loader!./in_memory_selection_uncontrolled');

export const selectionSection = {
title: 'In-memory table selection',
source: [
{
type: GuideSectionTypes.TSX,
code: source,
},
],
text: (
<p>
The following example shows how to use <strong>EuiInMemoryTable</strong>{' '}
along with item selection. It also shows how you can display messages,
errors and show loading indication. You can set items to be selected
initially by passing an array of items as the{' '}
<EuiCode>initialSelected</EuiCode> value inside{' '}
<EuiCode>selection</EuiCode> property and passing{' '}
<EuiCode>itemId</EuiCode> property to enable selection. You can also use
the <EuiCode>setSelection</EuiCode> method to take complete control over
table selection. This can be useful if you want to handle selection in
table based on user interaction with another part of the UI.
</p>
<>
<p>
To enable selection, both the <EuiCode>itemId</EuiCode> and{' '}
<EuiCode>selection</EuiCode> props must be passed. The following example
shows how to use <strong>EuiInMemoryTable</strong> with both controlled
and uncontrolled item selection. It also shows how you can display
messages, errors and show loading indication.
</p>
<p>
For uncontrolled usage, where selection changes are determined entirely
to be selected initially by passing an array of items to{' '}
<EuiCode>selection.initialSelected</EuiCode>. You can also use{' '}
<EuiCode>selected.onSelectionChange</EuiCode> to track or respond to the
items that users select.
</p>
<p>
To completely control table selection, use{' '}
<EuiCode>selection.selected</EuiCode> instead (which requires passing{' '}
<EuiCode>selected.onSelectionChange</EuiCode>). This can be useful if
you want to handle table selections based on user interaction with
another part of the UI.
</p>
</>
),
children: (
<>
<EuiSpacer />
<ConditionallyControlledDemo
controlledDemo={<Controlled />}
uncontrolledDemo={<Uncontrolled />}
controlledSource={controlledSource}
uncontrolledSource={uncontrolledSource}
/>
</>
),
props: {
EuiInMemoryTable,
Criteria,
CriteriaWithPagination,
Pagination,
EuiTableSortingType,
EuiTableSelectionType,
EuiTableFieldDataColumnType,
EuiTableComputedColumnType,
EuiTableActionsColumnType,
DefaultItemAction,
CustomItemAction,
Search,
SearchFilterConfig,
FieldValueOptionType,
FieldValueToggleGroupFilterItemType,
},
demo: <Table />,
};
Loading
Loading