Merge pull request #67 from twentyhq/sammy/t-129-i-see-all-text-sorts-available-in-the

feature: Add all sortable fields of People table
This commit is contained in:
Sammy Teillet
2023-04-24 15:47:35 +02:00
committed by GitHub
8 changed files with 100 additions and 44 deletions

View File

@ -17,6 +17,7 @@ type OwnProps<TData> = {
viewName: string; viewName: string;
viewIcon?: IconProp; viewIcon?: IconProp;
onSortsUpdate?: (sorts: Array<SortType>) => void; onSortsUpdate?: (sorts: Array<SortType>) => void;
sortsAvailable?: Array<SortType>;
}; };
const StyledTable = styled.table` const StyledTable = styled.table`
@ -68,6 +69,7 @@ function Table<TData>({
viewName, viewName,
viewIcon, viewIcon,
onSortsUpdate, onSortsUpdate,
sortsAvailable,
}: OwnProps<TData>) { }: OwnProps<TData>) {
const table = useReactTable({ const table = useReactTable({
data, data,
@ -81,6 +83,7 @@ function Table<TData>({
viewName={viewName} viewName={viewName}
viewIcon={viewIcon} viewIcon={viewIcon}
onSortsUpdate={onSortsUpdate} onSortsUpdate={onSortsUpdate}
sortsAvailable={sortsAvailable || []}
/> />
<StyledTable> <StyledTable>
<thead> <thead>

View File

@ -15,6 +15,7 @@ const StyledDropdownButtonContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative; position: relative;
z-index: 1;
`; `;
type StyledDropdownButtonProps = { type StyledDropdownButtonProps = {
@ -36,16 +37,33 @@ const StyledDropdownButton = styled.div<StyledDropdownButtonProps>`
`; `;
const StyledDropdown = styled.ul` const StyledDropdown = styled.ul`
--wraper-border: 1px;
--wraper-border-radius: 8px;
--outer-border-radius: calc(var(--wraper-border-radius) - 2px);
display: flex; display: flex;
flex-direction: column;
position: absolute; position: absolute;
top: 14px; top: 14px;
right: 0; right: 0;
border: 1px solid ${(props) => props.theme.primaryBorder}; border: var(--wraper-border) solid ${(props) => props.theme.primaryBorder};
box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.09); box-shadow: 0px 3px 12px rgba(0, 0, 0, 0.09);
border-radius: 8px; border-radius: var(--wraper-border-radius);
padding: 0px; padding: 0px;
min-width: 160px; min-width: 160px;
${modalBackground} ${modalBackground}
li {
border-radius: 2px;
&:first-child {
border-top-left-radius: var(--outer-border-radius);
border-top-right-radius: var(--outer-border-radius);
}
&:last-child {
border-bottom-left-radius: var(--outer-border-radius);
border-bottom-right-radius: var(--outer-border-radius);
}
}
`; `;
const StyledDropdownItem = styled.li` const StyledDropdownItem = styled.li`
@ -53,14 +71,12 @@ const StyledDropdownItem = styled.li`
padding: ${(props) => props.theme.spacing(2)} padding: ${(props) => props.theme.spacing(2)}
calc(${(props) => props.theme.spacing(2)} - 2px); calc(${(props) => props.theme.spacing(2)} - 2px);
margin: 2px; margin: 2px;
background: ${(props) => props.theme.primaryBackground}; background: rgba(0, 0, 0, 0);
cursor: pointer; cursor: pointer;
width: 100%;
border-radius: 4px;
color: ${(props) => props.theme.text60}; color: ${(props) => props.theme.text60};
&:hover { &:hover {
filter: brightness(0.95); background: rgba(0, 0, 0, 0.04);
} }
`; `;

View File

@ -8,10 +8,10 @@ type OwnProps = {
onRemoveSort: (sortId: string) => void; onRemoveSort: (sortId: string) => void;
}; };
export type SortType = { export type SortType<SortIds = string> = {
label: string; label: string;
order: 'asc' | 'desc'; order: 'asc' | 'desc';
id: string; id: SortIds;
icon?: IconProp; icon?: IconProp;
}; };

View File

@ -2,14 +2,14 @@ import styled from '@emotion/styled';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DropdownButton from './DropdownButton'; import DropdownButton from './DropdownButton';
import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { faCalendar } from '@fortawesome/pro-regular-svg-icons';
import SortAndFilterBar, { SortType } from './SortAndFilterBar'; import SortAndFilterBar, { SortType } from './SortAndFilterBar';
import { useEffect, useState } from 'react'; import { useCallback, useState } from 'react';
type OwnProps = { type OwnProps = {
viewName: string; viewName: string;
viewIcon?: IconProp; viewIcon?: IconProp;
onSortsUpdate?: (sorts: Array<SortType>) => void; onSortsUpdate?: (sorts: Array<SortType>) => void;
sortsAvailable: Array<SortType>;
}; };
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -44,34 +44,37 @@ const StyledFilters = styled.div`
margin-right: ${(props) => props.theme.spacing(2)}; margin-right: ${(props) => props.theme.spacing(2)};
`; `;
function TableHeader({ viewName, viewIcon, onSortsUpdate }: OwnProps) { function TableHeader({
viewName,
viewIcon,
onSortsUpdate,
sortsAvailable,
}: OwnProps) {
const [sorts, setSorts] = useState([] as Array<SortType>); const [sorts, setSorts] = useState([] as Array<SortType>);
const onSortItemSelect = (sortId: string) => { const onSortItemSelect = useCallback(
setSorts([ (sortId: string) => {
{ const newSorts = [
label: 'Created at', {
order: 'asc', label: 'Created at',
id: sortId, order: 'asc',
} as SortType, id: sortId,
]); } satisfies SortType,
}; ];
const onSortItemUnSelect = (sortId: string) => { setSorts(newSorts);
setSorts([]); onSortsUpdate && onSortsUpdate(newSorts);
};
useEffect(() => {
onSortsUpdate && onSortsUpdate(sorts);
}, [sorts, onSortsUpdate]);
const sortsAvailable: Array<SortType> = [
{
id: 'created_at',
label: 'Created at',
order: 'asc',
icon: faCalendar,
}, },
]; [onSortsUpdate],
);
const onSortItemUnSelect = useCallback(
(sortId: string) => {
const newSorts = [] as SortType[];
setSorts(newSorts);
onSortsUpdate && onSortsUpdate(newSorts);
},
[onSortsUpdate],
);
return ( return (
<StyledContainer> <StyledContainer>

View File

@ -1,7 +1,8 @@
import TableHeader from '../TableHeader'; import TableHeader from '../TableHeader';
import { ThemeProvider } from '@emotion/react'; import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../../layout/styles/themes'; import { lightTheme } from '../../../../layout/styles/themes';
import { faBuilding } from '@fortawesome/pro-regular-svg-icons'; import { faBuilding, faCalendar } from '@fortawesome/pro-regular-svg-icons';
import { SortType } from '../SortAndFilterBar';
const component = { const component = {
title: 'TableHeader', title: 'TableHeader',
@ -11,9 +12,21 @@ const component = {
export default component; export default component;
export const RegularTableHeader = () => { export const RegularTableHeader = () => {
const sortsAvailable: Array<SortType> = [
{
id: 'created_at',
label: 'Created at',
order: 'asc',
icon: faCalendar,
},
];
return ( return (
<ThemeProvider theme={lightTheme}> <ThemeProvider theme={lightTheme}>
<TableHeader viewName="Test" viewIcon={faBuilding} /> <TableHeader
viewName="Test"
viewIcon={faBuilding}
sortsAvailable={sortsAvailable}
/>
</ThemeProvider> </ThemeProvider>
); );
}; };

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import './index.css'; import './index.css';
import App from './App'; import App from './App';
@ -14,7 +14,9 @@ const root = ReactDOM.createRoot(
root.render( root.render(
<ApolloProvider client={apiClient}> <ApolloProvider client={apiClient}>
<BrowserRouter> <BrowserRouter>
<App /> <StrictMode>
<App />
</StrictMode>
</BrowserRouter> </BrowserRouter>
</ApolloProvider>, </ApolloProvider>,
); );

View File

@ -2,9 +2,9 @@ import { faUser, faList } from '@fortawesome/pro-regular-svg-icons';
import WithTopBarContainer from '../../layout/containers/WithTopBarContainer'; import WithTopBarContainer from '../../layout/containers/WithTopBarContainer';
import Table from '../../components/table/Table'; import Table from '../../components/table/Table';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { peopleColumns } from './people-table'; import { peopleColumns, sortsAvailable } from './people-table';
import { mapPerson } from '../../interfaces/person.interface'; import { mapPerson } from '../../interfaces/person.interface';
import { useState } from 'react'; import { useCallback, useState } from 'react';
import { SortType } from '../../components/table/table-header/SortAndFilterBar'; import { SortType } from '../../components/table/table-header/SortAndFilterBar';
import { OrderBy, usePeopleQuery } from '../../services/people'; import { OrderBy, usePeopleQuery } from '../../services/people';
@ -31,10 +31,10 @@ function People() {
const [, setSorts] = useState([] as Array<SortType>); const [, setSorts] = useState([] as Array<SortType>);
const [orderBy, setOrderBy] = useState(defaultOrderBy); const [orderBy, setOrderBy] = useState(defaultOrderBy);
const updateSorts = (sorts: Array<SortType>) => { const updateSorts = useCallback((sorts: Array<SortType>) => {
setSorts(sorts); setSorts(sorts);
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy); setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
}; }, []);
const { data } = usePeopleQuery(orderBy); const { data } = usePeopleQuery(orderBy);
@ -48,6 +48,7 @@ function People() {
viewName="All People" viewName="All People"
viewIcon={faList} viewIcon={faList}
onSortsUpdate={updateSorts} onSortsUpdate={updateSorts}
sortsAvailable={sortsAvailable}
/> />
} }
</StyledPeopleContainer> </StyledPeopleContainer>

View File

@ -15,8 +15,26 @@ import Checkbox from '../../components/form/Checkbox';
import HorizontalyAlignedContainer from '../../layout/containers/HorizontalyAlignedContainer'; import HorizontalyAlignedContainer from '../../layout/containers/HorizontalyAlignedContainer';
import CompanyChip from '../../components/chips/CompanyChip'; import CompanyChip from '../../components/chips/CompanyChip';
import PersonChip from '../../components/chips/PersonChip'; import PersonChip from '../../components/chips/PersonChip';
import { Person } from '../../interfaces/person.interface'; import { GraphqlPerson, Person } from '../../interfaces/person.interface';
import PipeChip from '../../components/chips/PipeChip'; import PipeChip from '../../components/chips/PipeChip';
import { SortType } from '../../components/table/table-header/SortAndFilterBar';
export const sortsAvailable = [
{
id: 'email',
label: 'Email',
order: 'asc',
icon: faEnvelope,
},
{ id: 'phone', label: 'Phone', order: 'asc', icon: faPhone },
{
id: 'created_at',
label: 'Created at',
order: 'asc',
icon: faCalendar,
},
{ id: 'city', label: 'City', order: 'asc', icon: faMapPin },
] satisfies Array<SortType<keyof GraphqlPerson>>;
const columnHelper = createColumnHelper<Person>(); const columnHelper = createColumnHelper<Person>();
export const peopleColumns = [ export const peopleColumns = [