Enable multi-selection on table views (#112)

* Enable multi-selection on table views

* Enable multi-selection
This commit is contained in:
Charles Bochet
2023-05-08 10:58:53 +02:00
committed by GitHub
parent 48a75358b4
commit 94ea9835a9
7 changed files with 86 additions and 18 deletions

View File

@ -4,6 +4,9 @@ import styled from '@emotion/styled';
type OwnProps = { type OwnProps = {
name: string; name: string;
id: string; id: string;
checked?: boolean;
indeterminate?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}; };
const StyledContainer = styled.span` const StyledContainer = styled.span`
@ -28,14 +31,25 @@ const StyledContainer = styled.span`
} }
`; `;
function Checkbox({ name, id }: OwnProps) { function Checkbox({ name, id, checked, onChange, indeterminate }: OwnProps) {
const ref = React.useRef<HTMLInputElement>(null);
React.useEffect(() => {
if (ref.current === null) return;
if (typeof indeterminate === 'boolean') {
ref.current.indeterminate = !checked && indeterminate;
}
}, [ref, indeterminate, checked]);
return ( return (
<StyledContainer> <StyledContainer>
<input <input
ref={ref}
type="checkbox" type="checkbox"
data-testid="input-checkbox" data-testid="input-checkbox"
id={id} id={id}
name={name} name={name}
checked={checked}
onChange={onChange}
></input> ></input>
</StyledContainer> </StyledContainer>
); );

View File

@ -0,0 +1,18 @@
import Checkbox from '../form/Checkbox';
export const SelectAllCheckbox = ({
indeterminate,
onChange,
}: {
indeterminate?: boolean;
onChange?: any;
} & React.HTMLProps<HTMLInputElement>) => {
return (
<Checkbox
name="select-all-checkbox"
id="select-all-checkbox"
indeterminate={indeterminate}
onChange={onChange}
/>
);
};

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import { import {
ColumnDef, ColumnDef,
RowSelectionState,
flexRender, flexRender,
getCoreRowModel, getCoreRowModel,
useReactTable, useReactTable,
@ -34,6 +35,7 @@ type OwnProps<TData, SortField, FilterProperties> = {
filter: FilterType<FilterProperties> | null, filter: FilterType<FilterProperties> | null,
searchValue: string, searchValue: string,
) => void; ) => void;
onRowSelectionChange?: (rowSelection: RowSelectionState) => void;
}; };
const StyledTable = styled.table` const StyledTable = styled.table`
@ -98,11 +100,23 @@ function Table<TData extends { id: string }, SortField, FilterProperies>({
onSortsUpdate, onSortsUpdate,
onFiltersUpdate, onFiltersUpdate,
onFilterSearch, onFilterSearch,
onRowSelectionChange,
}: OwnProps<TData, SortField, FilterProperies>) { }: OwnProps<TData, SortField, FilterProperies>) {
const [rowSelection, setRowSelection] = React.useState({});
React.useEffect(() => {
onRowSelectionChange && onRowSelectionChange(rowSelection);
}, [rowSelection, onRowSelectionChange]);
const table = useReactTable<TData>({ const table = useReactTable<TData>({
data, data,
columns, columns,
state: {
rowSelection,
},
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
enableRowSelection: true, //enable row selection for all rows
onRowSelectionChange: setRowSelection,
}); });
return ( return (

View File

@ -99,6 +99,9 @@ function Companies() {
setSearhInput(searchValue); setSearhInput(searchValue);
setFilterSearch(filter); setFilterSearch(filter);
}} }}
onRowSelectionChange={(selectedRows) => {
console.log(selectedRows);
}}
/> />
</StyledCompaniesContainer> </StyledCompaniesContainer>
</WithTopBarContainer> </WithTopBarContainer>

View File

@ -1,11 +1,10 @@
import { createColumnHelper } from '@tanstack/react-table'; import { CellContext, createColumnHelper } from '@tanstack/react-table';
import { import {
Company, Company,
GraphqlQueryCompany, GraphqlQueryCompany,
} from '../../interfaces/company.interface'; } from '../../interfaces/company.interface';
import { updateCompany } from '../../services/companies'; import { updateCompany } from '../../services/companies';
import ColumnHead from '../../components/table/ColumnHead'; import ColumnHead from '../../components/table/ColumnHead';
import Checkbox from '../../components/form/Checkbox';
import CompanyChip from '../../components/chips/CompanyChip'; import CompanyChip from '../../components/chips/CompanyChip';
import EditableText from '../../components/table/editable-cell/EditableText'; import EditableText from '../../components/table/editable-cell/EditableText';
import { import {
@ -39,6 +38,8 @@ import EditableDate from '../../components/table/editable-cell/EditableDate';
import EditableRelation from '../../components/table/editable-cell/EditableRelation'; import EditableRelation from '../../components/table/editable-cell/EditableRelation';
import { GraphqlQueryUser, PartialUser } from '../../interfaces/user.interface'; import { GraphqlQueryUser, PartialUser } from '../../interfaces/user.interface';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
import Checkbox from '../../components/form/Checkbox';
export const availableSorts = [ export const availableSorts = [
{ {
@ -141,17 +142,24 @@ const columnHelper = createColumnHelper<Company>();
export const useCompaniesColumns = () => { export const useCompaniesColumns = () => {
return useMemo(() => { return useMemo(() => {
return [ return [
columnHelper.accessor('id', { {
header: () => ( id: 'select',
<Checkbox id="company-select-all" name="company-select-all" /> header: ({ table }: any) => (
<SelectAllCheckbox
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
), ),
cell: (props) => ( cell: (props: CellContext<Company, string>) => (
<Checkbox <Checkbox
id={`company-selected-${props.row.original.id}`} id={`company-selected-${props.row.original.id}`}
name={`company-selected-${props.row.original.id}`} name={`company-selected-${props.row.original.id}`}
checked={props.row.getIsSelected()}
onChange={props.row.getToggleSelectedHandler()}
/> />
), ),
}), },
columnHelper.accessor('name', { columnHelper.accessor('name', {
header: () => ( header: () => (
<ColumnHead viewName="Name" viewIcon={<FaRegBuilding />} /> <ColumnHead viewName="Name" viewIcon={<FaRegBuilding />} />

View File

@ -98,6 +98,9 @@ function People() {
setSearchInput(searchValue); setSearchInput(searchValue);
setFilterSearch(filter); setFilterSearch(filter);
}} }}
onRowSelectionChange={(selectedRows) => {
console.log(selectedRows);
}}
/> />
} }
</StyledPeopleContainer> </StyledPeopleContainer>

View File

@ -8,7 +8,7 @@ import {
FaUser, FaUser,
FaBuilding, FaBuilding,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { createColumnHelper } from '@tanstack/react-table'; import { CellContext, createColumnHelper } from '@tanstack/react-table';
import ColumnHead from '../../components/table/ColumnHead'; import ColumnHead from '../../components/table/ColumnHead';
import Checkbox from '../../components/form/Checkbox'; import Checkbox from '../../components/form/Checkbox';
import CompanyChip, { import CompanyChip, {
@ -39,6 +39,7 @@ import EditableDate from '../../components/table/editable-cell/EditableDate';
import EditableRelation from '../../components/table/editable-cell/EditableRelation'; import EditableRelation from '../../components/table/editable-cell/EditableRelation';
import { updatePerson } from '../../services/people'; import { updatePerson } from '../../services/people';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { SelectAllCheckbox } from '../../components/table/SelectAllCheckbox';
export const availableSorts = [ export const availableSorts = [
{ {
@ -247,17 +248,24 @@ const columnHelper = createColumnHelper<Person>();
export const usePeopleColumns = () => { export const usePeopleColumns = () => {
return useMemo(() => { return useMemo(() => {
return [ return [
columnHelper.accessor('id', { {
header: () => ( id: 'select',
<Checkbox id={`person-select-all`} name={`person-select-all`} /> header: ({ table }: any) => (
), <SelectAllCheckbox
cell: (props) => ( checked={table.getIsAllRowsSelected()}
<Checkbox indeterminate={table.getIsSomeRowsSelected()}
id={`person-selected-${props.row.original.email}`} onChange={table.getToggleAllRowsSelectedHandler()}
name={`person-selected-${props.row.original.email}`}
/> />
), ),
}), cell: (props: CellContext<Person, string>) => (
<Checkbox
id={`person-selected-${props.row.original.id}`}
name={`person-selected-${props.row.original.id}`}
checked={props.row.getIsSelected()}
onChange={props.row.getToggleSelectedHandler()}
/>
),
},
columnHelper.accessor('firstname', { columnHelper.accessor('firstname', {
header: () => <ColumnHead viewName="People" viewIcon={<FaRegUser />} />, header: () => <ColumnHead viewName="People" viewIcon={<FaRegUser />} />,
cell: (props) => ( cell: (props) => (