Refactor/remove react table (#642)
* Refactored tables without tan stack * Fixed checkbox behavior with multiple handlers on click * Fixed hotkeys scope * Fix debounce in editable cells * Lowered coverage --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { GET_PEOPLE } from '@/people/services';
|
||||
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
||||
import { EntityTableActionBar } from '@/ui/components/table/action-bar/EntityTableActionBar';
|
||||
import { IconUser } from '@/ui/icons/index';
|
||||
import { IconBuildingSkyscraper } from '@/ui/icons/index';
|
||||
import { FlexExpandingContainer } from '@/ui/layout/containers/FlexExpandingContainer';
|
||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||
import { TableContext } from '@/ui/tables/states/TableContext';
|
||||
@ -38,8 +38,8 @@ export function People() {
|
||||
return (
|
||||
<RecoilScope SpecificContext={TableContext}>
|
||||
<WithTopBarContainer
|
||||
title="People"
|
||||
icon={<IconUser size={theme.icon.size.md} />}
|
||||
title="Companies"
|
||||
icon={<IconBuildingSkyscraper size={theme.icon.size.md} />}
|
||||
onAddButtonClick={handleAddButtonClick}
|
||||
>
|
||||
<FlexExpandingContainer>
|
||||
|
||||
@ -5,14 +5,15 @@ import { defaultOrderBy } from '@/companies/services';
|
||||
import { reduceSortsToOrderBy } from '@/lib/filters-and-sorts/helpers';
|
||||
import { filtersScopedState } from '@/lib/filters-and-sorts/states/filtersScopedState';
|
||||
import { turnFilterIntoWhereClause } from '@/lib/filters-and-sorts/utils/turnFilterIntoWhereClause';
|
||||
import { PeopleSelectedSortType, usePeopleQuery } from '@/people/services';
|
||||
import { PeopleEntityTableData } from '@/people/components/PeopleEntityTableData';
|
||||
import { PeopleSelectedSortType } from '@/people/services';
|
||||
import { peopleColumns } from '@/people/table/components/peopleColumns';
|
||||
import { useRecoilScopedValue } from '@/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { EntityTable } from '@/ui/components/table/EntityTable';
|
||||
import { HooksEntityTable } from '@/ui/components/table/HooksEntityTable';
|
||||
import { TableContext } from '@/ui/tables/states/TableContext';
|
||||
import { PersonOrderByWithRelationInput } from '~/generated/graphql';
|
||||
|
||||
import { usePeopleColumns } from './people-columns';
|
||||
import { peopleFilters } from './people-filters';
|
||||
import { availableSorts } from './people-sorts';
|
||||
|
||||
@ -30,21 +31,14 @@ export function PeopleTable() {
|
||||
return { AND: filters.map(turnFilterIntoWhereClause) };
|
||||
}, [filters]) as any;
|
||||
|
||||
const peopleColumns = usePeopleColumns();
|
||||
|
||||
const { data } = usePeopleQuery(orderBy, whereFilters);
|
||||
|
||||
const people = data?.people ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<PeopleEntityTableData orderBy={orderBy} whereFilters={whereFilters} />
|
||||
<HooksEntityTable
|
||||
numberOfColumns={peopleColumns.length}
|
||||
numberOfRows={people.length}
|
||||
availableTableFilters={peopleFilters}
|
||||
availableFilters={peopleFilters}
|
||||
/>
|
||||
<EntityTable
|
||||
data={people}
|
||||
columns={peopleColumns}
|
||||
viewName="All People"
|
||||
viewIcon={<IconList size={16} />}
|
||||
|
||||
@ -32,18 +32,12 @@ export const InteractWithManyRows: Story = {
|
||||
|
||||
let firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
||||
|
||||
let secondRowEmailCell = await canvas.findByText(mockedPeopleData[1].email);
|
||||
|
||||
expect(
|
||||
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
||||
).toBeNull();
|
||||
|
||||
await userEvent.click(firstRowEmailCell);
|
||||
|
||||
await sleep(100);
|
||||
firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
||||
await userEvent.click(firstRowEmailCell);
|
||||
await sleep(100);
|
||||
firstRowEmailCell = await canvas.findByText(mockedPeopleData[0].email);
|
||||
await userEvent.click(firstRowEmailCell);
|
||||
|
||||
@ -51,7 +45,9 @@ export const InteractWithManyRows: Story = {
|
||||
canvas.queryByTestId('editable-cell-edit-mode-container'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
secondRowEmailCell = await canvas.findByText(mockedPeopleData[1].email);
|
||||
const secondRowEmailCell = await canvas.findByText(
|
||||
mockedPeopleData[1].email,
|
||||
);
|
||||
await userEvent.click(secondRowEmailCell);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
@ -31,12 +31,6 @@ export const Email: Story = {
|
||||
expect(await canvas.getByTestId('remove-icon-email')).toBeInTheDocument();
|
||||
|
||||
expect(await canvas.findByText('Alexandre Prot')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
(await canvas.findAllByRole('checkbox')).map((item) => {
|
||||
return item.getAttribute('id');
|
||||
})[1],
|
||||
).toStrictEqual('checkbox-selected-7dfbc3f7-6e5e-4128-957e-8d86808cdf6b');
|
||||
},
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
|
||||
@ -1,159 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
|
||||
import { EditablePeopleFullName } from '@/people/components/EditablePeopleFullName';
|
||||
import { PeopleCompanyCell } from '@/people/components/PeopleCompanyCell';
|
||||
import { EditableCellDate } from '@/ui/components/editable-cell/types/EditableCellDate';
|
||||
import { EditableCellPhone } from '@/ui/components/editable-cell/types/EditableCellPhone';
|
||||
import { EditableCellText } from '@/ui/components/editable-cell/types/EditableCellText';
|
||||
import { ColumnHead } from '@/ui/components/table/ColumnHead';
|
||||
import {
|
||||
IconBuildingSkyscraper,
|
||||
IconCalendarEvent,
|
||||
IconMail,
|
||||
IconMap,
|
||||
IconPhone,
|
||||
IconUser,
|
||||
} from '@/ui/icons/index';
|
||||
import { getCheckBoxColumn } from '@/ui/tables/utils/getCheckBoxColumn';
|
||||
import { GetPeopleQuery, useUpdatePeopleMutation } from '~/generated/graphql';
|
||||
|
||||
const columnHelper = createColumnHelper<GetPeopleQuery['people'][0]>();
|
||||
|
||||
export const usePeopleColumns = () => {
|
||||
const [updatePerson] = useUpdatePeopleMutation();
|
||||
|
||||
return useMemo(() => {
|
||||
return [
|
||||
getCheckBoxColumn(),
|
||||
columnHelper.accessor('firstName', {
|
||||
header: () => (
|
||||
<ColumnHead viewName="People" viewIcon={<IconUser size={16} />} />
|
||||
),
|
||||
cell: (props) => (
|
||||
<>
|
||||
<EditablePeopleFullName
|
||||
person={props.row.original}
|
||||
onChange={async (firstName: string, lastName: string) => {
|
||||
const person = { ...props.row.original };
|
||||
await updatePerson({
|
||||
variables: {
|
||||
...person,
|
||||
firstName,
|
||||
lastName,
|
||||
companyId: person.company?.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
size: 210,
|
||||
}),
|
||||
columnHelper.accessor('email', {
|
||||
header: () => (
|
||||
<ColumnHead viewName="Email" viewIcon={<IconMail size={16} />} />
|
||||
),
|
||||
cell: (props) => (
|
||||
<EditableCellText
|
||||
placeholder="Email"
|
||||
value={props.row.original.email || ''}
|
||||
onChange={async (value: string) => {
|
||||
const person = props.row.original;
|
||||
await updatePerson({
|
||||
variables: {
|
||||
...person,
|
||||
email: value,
|
||||
companyId: person.company?.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
size: 200,
|
||||
}),
|
||||
columnHelper.accessor('company', {
|
||||
header: () => (
|
||||
<ColumnHead
|
||||
viewName="Company"
|
||||
viewIcon={<IconBuildingSkyscraper size={16} />}
|
||||
/>
|
||||
),
|
||||
cell: (props) => <PeopleCompanyCell people={props.row.original} />,
|
||||
size: 150,
|
||||
}),
|
||||
columnHelper.accessor('phone', {
|
||||
header: () => (
|
||||
<ColumnHead viewName="Phone" viewIcon={<IconPhone size={16} />} />
|
||||
),
|
||||
cell: (props) => (
|
||||
<EditableCellPhone
|
||||
placeholder="Phone"
|
||||
value={props.row.original.phone || ''}
|
||||
changeHandler={async (value: string) => {
|
||||
const person = { ...props.row.original };
|
||||
await updatePerson({
|
||||
variables: {
|
||||
...person,
|
||||
phone: value,
|
||||
companyId: person.company?.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
size: 130,
|
||||
}),
|
||||
columnHelper.accessor('createdAt', {
|
||||
header: () => (
|
||||
<ColumnHead
|
||||
viewName="Creation"
|
||||
viewIcon={<IconCalendarEvent size={16} />}
|
||||
/>
|
||||
),
|
||||
cell: (props) => (
|
||||
<EditableCellDate
|
||||
value={
|
||||
props.row.original.createdAt
|
||||
? new Date(props.row.original.createdAt)
|
||||
: new Date()
|
||||
}
|
||||
onChange={async (value: Date) => {
|
||||
const person = { ...props.row.original };
|
||||
await updatePerson({
|
||||
variables: {
|
||||
...person,
|
||||
createdAt: value.toISOString(),
|
||||
companyId: person.company?.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
size: 100,
|
||||
}),
|
||||
columnHelper.accessor('city', {
|
||||
header: () => (
|
||||
<ColumnHead viewName="City" viewIcon={<IconMap size={16} />} />
|
||||
),
|
||||
cell: (props) => (
|
||||
<EditableCellText
|
||||
editModeHorizontalAlign="right"
|
||||
placeholder="City"
|
||||
value={props.row.original.city || ''}
|
||||
onChange={async (value: string) => {
|
||||
const person = { ...props.row.original };
|
||||
await updatePerson({
|
||||
variables: {
|
||||
...person,
|
||||
city: value,
|
||||
companyId: person.company?.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}),
|
||||
];
|
||||
}, [updatePerson]);
|
||||
};
|
||||
@ -17,7 +17,7 @@ export const availableSorts = [
|
||||
key: 'fullname',
|
||||
label: 'People',
|
||||
icon: <IconUser size={16} />,
|
||||
_type: 'custom_sort',
|
||||
|
||||
orderByTemplates: [
|
||||
(order: Order_By) => ({
|
||||
firstName: order,
|
||||
@ -31,31 +31,27 @@ export const availableSorts = [
|
||||
key: 'company_name',
|
||||
label: 'Company',
|
||||
icon: <IconBuildingSkyscraper size={16} />,
|
||||
_type: 'custom_sort',
|
||||
|
||||
orderByTemplates: [(order: Order_By) => ({ company: { name: order } })],
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
icon: <IconMail size={16} />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'phone',
|
||||
label: 'Phone',
|
||||
icon: <IconPhone size={16} />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: 'Created at',
|
||||
icon: <IconCalendarEvent size={16} />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
label: 'City',
|
||||
icon: <IconMap size={16} />,
|
||||
_type: 'default_sort',
|
||||
},
|
||||
] satisfies Array<SortType<People_Order_By>>;
|
||||
|
||||
Reference in New Issue
Block a user