Sammy/t 131 i see a ascending descending option in (#73)
* refactor: use safe css selector * feature: onclick update the order option * feature: set option in the dropdown * feature: display icon for sort options * refactor: indent key so react does not complain of conflicting keys * feature: align icon and text * feature: fix size of icon to align text * feature: create design for TopOption in dropdown * refactor: set font weight in style * feature: finalise design of TopOption * refactor: rename option to sort * refactor: remove order from the sortType * refactor: move sort mapper in service * test: selection of Descending in SortDropdownButton * refactor: fix styme-component warning * feature: add sorting by people * refactor: set SortFields types for tables * feature: add sort by company * refactor: rename sortFields to singular * refactor: rename option to SortDirection
This commit is contained in:
@ -9,15 +9,15 @@ import {
|
|||||||
import TableHeader from './table-header/TableHeader';
|
import TableHeader from './table-header/TableHeader';
|
||||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { SortType } from './table-header/SortAndFilterBar';
|
import { SelectedSortType, SortType } from './table-header/SortAndFilterBar';
|
||||||
|
|
||||||
type OwnProps<TData> = {
|
type OwnProps<TData, SortField> = {
|
||||||
data: Array<TData>;
|
data: Array<TData>;
|
||||||
columns: Array<ColumnDef<TData, any>>;
|
columns: Array<ColumnDef<TData, any>>;
|
||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: IconProp;
|
viewIcon?: IconProp;
|
||||||
onSortsUpdate?: (sorts: Array<SortType>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
sortsAvailable?: Array<SortType>;
|
sortsAvailable?: Array<SortType<SortField>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledTable = styled.table`
|
const StyledTable = styled.table`
|
||||||
@ -71,14 +71,14 @@ const StyledTableScrollableContainer = styled.div`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function Table<TData>({
|
function Table<TData, SortField extends string>({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
sortsAvailable,
|
sortsAvailable,
|
||||||
}: OwnProps<TData>) {
|
}: OwnProps<TData, SortField>) {
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
|||||||
@ -57,11 +57,11 @@ const StyledDropdown = styled.ul`
|
|||||||
li {
|
li {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
&:first-child {
|
&:first-of-type {
|
||||||
border-top-left-radius: var(--outer-border-radius);
|
border-top-left-radius: var(--outer-border-radius);
|
||||||
border-top-right-radius: var(--outer-border-radius);
|
border-top-right-radius: var(--outer-border-radius);
|
||||||
}
|
}
|
||||||
&:last-child {
|
&:last-of-type {
|
||||||
border-bottom-left-radius: var(--outer-border-radius);
|
border-bottom-left-radius: var(--outer-border-radius);
|
||||||
border-bottom-right-radius: var(--outer-border-radius);
|
border-bottom-right-radius: var(--outer-border-radius);
|
||||||
}
|
}
|
||||||
@ -70,6 +70,7 @@ const StyledDropdown = styled.ul`
|
|||||||
|
|
||||||
const StyledDropdownItem = styled.li`
|
const StyledDropdownItem = styled.li`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
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;
|
||||||
@ -82,9 +83,29 @@ const StyledDropdownItem = styled.li`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledDropdownTopOption = styled.li`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: calc(${(props) => props.theme.spacing(2)} + 2px)
|
||||||
|
calc(${(props) => props.theme.spacing(2)});
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${(props) => props.theme.text60};
|
||||||
|
font-weight: ${(props) => props.theme.fontWeightBold};
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
border-radius: 0%;
|
||||||
|
border-bottom: 1px solid ${(props) => props.theme.primaryBorder};
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledIcon = styled.div`
|
const StyledIcon = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-right: ${(props) => props.theme.spacing(1)};
|
margin-right: ${(props) => props.theme.spacing(1)};
|
||||||
|
min-width: ${(props) => props.theme.spacing(4)};
|
||||||
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function DropdownButton({
|
function DropdownButton({
|
||||||
@ -122,6 +143,7 @@ function DropdownButton({
|
|||||||
}
|
}
|
||||||
|
|
||||||
DropdownButton.StyledDropdownItem = StyledDropdownItem;
|
DropdownButton.StyledDropdownItem = StyledDropdownItem;
|
||||||
|
DropdownButton.StyledDropdownTopOption = StyledDropdownTopOption;
|
||||||
DropdownButton.StyledIcon = StyledIcon;
|
DropdownButton.StyledIcon = StyledIcon;
|
||||||
|
|
||||||
export default DropdownButton;
|
export default DropdownButton;
|
||||||
|
|||||||
@ -3,18 +3,21 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
|||||||
import SortOrFilterChip from './SortOrFilterChip';
|
import SortOrFilterChip from './SortOrFilterChip';
|
||||||
import { faArrowDown, faArrowUp } from '@fortawesome/pro-regular-svg-icons';
|
import { faArrowDown, faArrowUp } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps<SortField> = {
|
||||||
sorts: Array<SortType>;
|
sorts: Array<SelectedSortType<SortField>>;
|
||||||
onRemoveSort: (sortId: string) => void;
|
onRemoveSort: (sortId: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SortType<SortIds = string> = {
|
export type SortType<SortIds = string> = {
|
||||||
label: string;
|
label: string;
|
||||||
order: 'asc' | 'desc';
|
|
||||||
id: SortIds;
|
id: SortIds;
|
||||||
icon?: IconProp;
|
icon?: IconProp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SelectedSortType<SortField = string> = SortType<SortField> & {
|
||||||
|
order: 'asc' | 'desc';
|
||||||
|
};
|
||||||
|
|
||||||
const StyledBar = styled.div`
|
const StyledBar = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -44,7 +47,10 @@ const StyledCancelButton = styled.button`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function SortAndFilterBar({ sorts, onRemoveSort }: OwnProps) {
|
function SortAndFilterBar<SortField extends string>({
|
||||||
|
sorts,
|
||||||
|
onRemoveSort,
|
||||||
|
}: OwnProps<SortField>) {
|
||||||
return (
|
return (
|
||||||
<StyledBar>
|
<StyledBar>
|
||||||
{sorts.map((sort) => {
|
{sorts.map((sort) => {
|
||||||
|
|||||||
@ -1,27 +1,35 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import DropdownButton from './DropdownButton';
|
import DropdownButton from './DropdownButton';
|
||||||
import { SortType } from './SortAndFilterBar';
|
import { SelectedSortType, SortType } from './SortAndFilterBar';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faAngleDown } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps<SortField> = {
|
||||||
sorts: SortType[];
|
sorts: SelectedSortType<SortField>[];
|
||||||
setSorts: (sorts: SortType[]) => void;
|
setSorts: (sorts: SelectedSortType<SortField>[]) => void;
|
||||||
sortsAvailable: SortType[];
|
sortsAvailable: SortType<SortField>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SortDropdownButton({
|
const options: Array<SelectedSortType<string>['order']> = ['asc', 'desc'];
|
||||||
|
|
||||||
|
export function SortDropdownButton<SortField extends string>({
|
||||||
sortsAvailable,
|
sortsAvailable,
|
||||||
setSorts,
|
setSorts,
|
||||||
sorts,
|
sorts,
|
||||||
}: OwnProps) {
|
}: OwnProps<SortField>) {
|
||||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||||
|
|
||||||
|
const [isOptionUnfolded, setIsOptionUnfolded] = useState(false);
|
||||||
|
|
||||||
|
const [selectedSortDirection, setSelectedSortDirection] =
|
||||||
|
useState<SelectedSortType<SortField>['order']>('asc');
|
||||||
|
|
||||||
const onSortItemSelect = useCallback(
|
const onSortItemSelect = useCallback(
|
||||||
(sort: SortType) => {
|
(sort: SortType<SortField>) => {
|
||||||
const newSorts = [sort];
|
const newSorts = [{ ...sort, order: selectedSortDirection }];
|
||||||
setSorts(newSorts);
|
setSorts(newSorts);
|
||||||
},
|
},
|
||||||
[setSorts],
|
[setSorts, selectedSortDirection],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -31,20 +39,42 @@ export function SortDropdownButton({
|
|||||||
isUnfolded={isUnfolded}
|
isUnfolded={isUnfolded}
|
||||||
setIsUnfolded={setIsUnfolded}
|
setIsUnfolded={setIsUnfolded}
|
||||||
>
|
>
|
||||||
{sortsAvailable.map((option, index) => (
|
{isOptionUnfolded
|
||||||
<DropdownButton.StyledDropdownItem
|
? options.map((option, index) => (
|
||||||
key={index}
|
<DropdownButton.StyledDropdownItem
|
||||||
onClick={() => {
|
key={index}
|
||||||
setIsUnfolded(false);
|
onClick={() => {
|
||||||
onSortItemSelect(option);
|
setSelectedSortDirection(option);
|
||||||
}}
|
setIsOptionUnfolded(false);
|
||||||
>
|
}}
|
||||||
<DropdownButton.StyledIcon>
|
>
|
||||||
{option.icon && <FontAwesomeIcon icon={option.icon} />}
|
{option === 'asc' ? 'Ascending' : 'Descending'}
|
||||||
</DropdownButton.StyledIcon>
|
</DropdownButton.StyledDropdownItem>
|
||||||
{option.label}
|
))
|
||||||
</DropdownButton.StyledDropdownItem>
|
: [
|
||||||
))}
|
<DropdownButton.StyledDropdownTopOption
|
||||||
|
key={0}
|
||||||
|
onClick={() => setIsOptionUnfolded(true)}
|
||||||
|
>
|
||||||
|
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||||
|
|
||||||
|
<FontAwesomeIcon icon={faAngleDown} />
|
||||||
|
</DropdownButton.StyledDropdownTopOption>,
|
||||||
|
...sortsAvailable.map((sort, index) => (
|
||||||
|
<DropdownButton.StyledDropdownItem
|
||||||
|
key={index + 1}
|
||||||
|
onClick={() => {
|
||||||
|
setIsUnfolded(false);
|
||||||
|
onSortItemSelect(sort);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownButton.StyledIcon>
|
||||||
|
{sort.icon && <FontAwesomeIcon icon={sort.icon} />}
|
||||||
|
</DropdownButton.StyledIcon>
|
||||||
|
{sort.label}
|
||||||
|
</DropdownButton.StyledDropdownItem>
|
||||||
|
)),
|
||||||
|
]}
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,10 +17,9 @@ const StyledChip = styled.div`
|
|||||||
background-color: ${(props) => props.theme.blueHighTransparency};
|
background-color: ${(props) => props.theme.blueHighTransparency};
|
||||||
border: 1px solid ${(props) => props.theme.blueLowTransparency};
|
border: 1px solid ${(props) => props.theme.blueLowTransparency};
|
||||||
color: ${(props) => props.theme.blue};
|
color: ${(props) => props.theme.blue};
|
||||||
padding: ${(props) => props.theme.spacing(1)}
|
padding: ${(props) => props.theme.spacing(1) + ' ' + props.theme.spacing(2)};
|
||||||
${(props) => props.theme.spacing(2)};
|
|
||||||
margin-left: ${(props) => props.theme.spacing(2)};
|
margin-left: ${(props) => props.theme.spacing(2)};
|
||||||
fontsize: ${(props) => props.theme.fontSizeSmall};
|
font-size: ${(props) => props.theme.fontSizeSmall};
|
||||||
`;
|
`;
|
||||||
const StyledIcon = styled.div`
|
const StyledIcon = styled.div`
|
||||||
margin-right: ${(props) => props.theme.spacing(1)};
|
margin-right: ${(props) => props.theme.spacing(1)};
|
||||||
|
|||||||
@ -2,15 +2,18 @@ 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 SortAndFilterBar, { SortType } from './SortAndFilterBar';
|
import SortAndFilterBar, {
|
||||||
|
SelectedSortType,
|
||||||
|
SortType,
|
||||||
|
} from './SortAndFilterBar';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { SortDropdownButton } from './SortDropdownButton';
|
import { SortDropdownButton } from './SortDropdownButton';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps<SortField> = {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: IconProp;
|
viewIcon?: IconProp;
|
||||||
onSortsUpdate?: (sorts: Array<SortType>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
sortsAvailable: Array<SortType>;
|
sortsAvailable: Array<SortType<SortField>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -49,16 +52,18 @@ const StyledFilters = styled.div`
|
|||||||
margin-right: ${(props) => props.theme.spacing(2)};
|
margin-right: ${(props) => props.theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function TableHeader({
|
function TableHeader<SortField extends string>({
|
||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
sortsAvailable,
|
sortsAvailable,
|
||||||
}: OwnProps) {
|
}: OwnProps<SortField>) {
|
||||||
const [sorts, innerSetSorts] = useState([] as Array<SortType>);
|
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const setSorts = useCallback(
|
const setSorts = useCallback(
|
||||||
(sorts: SortType[]) => {
|
(sorts: SelectedSortType<SortField>[]) => {
|
||||||
innerSetSorts(sorts);
|
innerSetSorts(sorts);
|
||||||
onSortsUpdate && onSortsUpdate(sorts);
|
onSortsUpdate && onSortsUpdate(sorts);
|
||||||
},
|
},
|
||||||
@ -67,7 +72,7 @@ function TableHeader({
|
|||||||
|
|
||||||
const onSortItemUnSelect = useCallback(
|
const onSortItemUnSelect = useCallback(
|
||||||
(sortId: string) => {
|
(sortId: string) => {
|
||||||
const newSorts = [] as SortType[];
|
const newSorts = [] as SelectedSortType<SortField>[];
|
||||||
innerSetSorts(newSorts);
|
innerSetSorts(newSorts);
|
||||||
onSortsUpdate && onSortsUpdate(newSorts);
|
onSortsUpdate && onSortsUpdate(newSorts);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { SelectedSortType, SortType } from '../SortAndFilterBar';
|
||||||
|
import { ThemeProvider } from '@emotion/react';
|
||||||
|
import { lightTheme } from '../../../../layout/styles/themes';
|
||||||
|
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
import { SortDropdownButton } from '../SortDropdownButton';
|
||||||
|
|
||||||
|
const component = {
|
||||||
|
title: 'SortDropdownButton',
|
||||||
|
component: SortDropdownButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default component;
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
setSorts: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sorts = [] satisfies SelectedSortType[];
|
||||||
|
|
||||||
|
const availableSorts = [
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
id: 'email',
|
||||||
|
icon: faArrowDown,
|
||||||
|
},
|
||||||
|
] satisfies SortType[];
|
||||||
|
|
||||||
|
export const RegularSortDropdownButton = ({ setSorts }: OwnProps) => {
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
<SortDropdownButton
|
||||||
|
sorts={sorts}
|
||||||
|
sortsAvailable={availableSorts}
|
||||||
|
setSorts={setSorts}
|
||||||
|
/>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -16,7 +16,6 @@ export const RegularTableHeader = () => {
|
|||||||
{
|
{
|
||||||
id: 'created_at',
|
id: 'created_at',
|
||||||
label: 'Created at',
|
label: 'Created at',
|
||||||
order: 'asc',
|
|
||||||
icon: faCalendar,
|
icon: faCalendar,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
import { fireEvent, render } from '@testing-library/react';
|
||||||
|
import { RegularSortDropdownButton } from '../__stories__/SortDropdownButton.stories';
|
||||||
|
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
|
it('Checks the default top option is Ascending', async () => {
|
||||||
|
const setSorts = jest.fn();
|
||||||
|
const { getByText } = render(
|
||||||
|
<RegularSortDropdownButton setSorts={setSorts} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortDropdownButton = getByText('Sort');
|
||||||
|
fireEvent.click(sortDropdownButton);
|
||||||
|
|
||||||
|
const sortByEmail = getByText('Email');
|
||||||
|
fireEvent.click(sortByEmail);
|
||||||
|
|
||||||
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
id: 'email',
|
||||||
|
icon: faArrowDown,
|
||||||
|
order: 'asc',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Checks the selection of Descending', async () => {
|
||||||
|
const setSorts = jest.fn();
|
||||||
|
const { getByText } = render(
|
||||||
|
<RegularSortDropdownButton setSorts={setSorts} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortDropdownButton = getByText('Sort');
|
||||||
|
fireEvent.click(sortDropdownButton);
|
||||||
|
|
||||||
|
const openTopOption = getByText('Ascending');
|
||||||
|
fireEvent.click(openTopOption);
|
||||||
|
|
||||||
|
const selectDescending = getByText('Descending');
|
||||||
|
fireEvent.click(selectDescending);
|
||||||
|
|
||||||
|
const sortByEmail = getByText('Email');
|
||||||
|
fireEvent.click(sortByEmail);
|
||||||
|
|
||||||
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
|
{
|
||||||
|
label: 'Email',
|
||||||
|
id: 'email',
|
||||||
|
icon: faArrowDown,
|
||||||
|
order: 'desc',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
@ -12,6 +12,8 @@ const commonTheme = {
|
|||||||
iconSizeMedium: '1.08rem',
|
iconSizeMedium: '1.08rem',
|
||||||
iconSizeLarge: '1.23rem',
|
iconSizeLarge: '1.23rem',
|
||||||
|
|
||||||
|
fontWeightBold: 500,
|
||||||
|
|
||||||
spacing: (multiplicator: number) => `${multiplicator * 4}px`,
|
spacing: (multiplicator: number) => `${multiplicator * 4}px`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,12 @@ import styled from '@emotion/styled';
|
|||||||
import { peopleColumns, sortsAvailable } from './people-table';
|
import { peopleColumns, sortsAvailable } from './people-table';
|
||||||
import { mapPerson } from '../../interfaces/person.interface';
|
import { mapPerson } from '../../interfaces/person.interface';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { SortType } from '../../components/table/table-header/SortAndFilterBar';
|
import {
|
||||||
import { OrderBy, usePeopleQuery } from '../../services/people';
|
OrderBy,
|
||||||
|
PeopleSelectedSortType,
|
||||||
|
reduceSortsToOrderBy,
|
||||||
|
usePeopleQuery,
|
||||||
|
} from '../../services/people';
|
||||||
|
|
||||||
const StyledPeopleContainer = styled.div`
|
const StyledPeopleContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -19,19 +23,11 @@ const defaultOrderBy: OrderBy[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const reduceSortsToOrderBy = (sorts: Array<SortType>): OrderBy[] => {
|
|
||||||
const mappedSorts = sorts.reduce((acc, sort) => {
|
|
||||||
acc[sort.id] = sort.order;
|
|
||||||
return acc;
|
|
||||||
}, {} as OrderBy);
|
|
||||||
return [mappedSorts];
|
|
||||||
};
|
|
||||||
|
|
||||||
function People() {
|
function People() {
|
||||||
const [, setSorts] = useState([] as Array<SortType>);
|
const [, setSorts] = useState([] as Array<PeopleSelectedSortType>);
|
||||||
const [orderBy, setOrderBy] = useState(defaultOrderBy);
|
const [orderBy, setOrderBy] = useState(defaultOrderBy);
|
||||||
|
|
||||||
const updateSorts = useCallback((sorts: Array<SortType>) => {
|
const updateSorts = useCallback((sorts: Array<PeopleSelectedSortType>) => {
|
||||||
setSorts(sorts);
|
setSorts(sorts);
|
||||||
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
|
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@ -15,28 +15,36 @@ 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 { GraphqlQueryPerson, Person } from '../../interfaces/person.interface';
|
import { 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';
|
import { SortType } from '../../components/table/table-header/SortAndFilterBar';
|
||||||
import EditableCell from '../../components/table/EditableCell';
|
import EditableCell from '../../components/table/EditableCell';
|
||||||
import { updatePerson } from '../../services/people';
|
import { OrderByFields, updatePerson } from '../../services/people';
|
||||||
|
|
||||||
export const sortsAvailable = [
|
export const sortsAvailable = [
|
||||||
|
{
|
||||||
|
id: 'fullname',
|
||||||
|
label: 'People',
|
||||||
|
icon: faUser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'company_name',
|
||||||
|
label: 'Company',
|
||||||
|
icon: faBuildings,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'email',
|
id: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
order: 'asc',
|
|
||||||
icon: faEnvelope,
|
icon: faEnvelope,
|
||||||
},
|
},
|
||||||
{ id: 'phone', label: 'Phone', order: 'asc', icon: faPhone },
|
{ id: 'phone', label: 'Phone', icon: faPhone },
|
||||||
{
|
{
|
||||||
id: 'created_at',
|
id: 'created_at',
|
||||||
label: 'Created at',
|
label: 'Created at',
|
||||||
order: 'asc',
|
|
||||||
icon: faCalendar,
|
icon: faCalendar,
|
||||||
},
|
},
|
||||||
{ id: 'city', label: 'City', order: 'asc', icon: faMapPin },
|
{ id: 'city', label: 'City', icon: faMapPin },
|
||||||
] satisfies Array<SortType<keyof GraphqlQueryPerson>>;
|
] satisfies Array<SortType<OrderByFields>>;
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Person>();
|
const columnHelper = createColumnHelper<Person>();
|
||||||
export const peopleColumns = [
|
export const peopleColumns = [
|
||||||
|
|||||||
12
front/src/services/people/select.test.ts
Normal file
12
front/src/services/people/select.test.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { PeopleSelectedSortType, reduceSortsToOrderBy } from './select';
|
||||||
|
|
||||||
|
describe('reduceSortsToOrderBy', () => {
|
||||||
|
it('should return an array of objects with the id as key and the order as value', () => {
|
||||||
|
const sorts = [
|
||||||
|
{ id: 'firstname', label: 'firstname', order: 'asc' },
|
||||||
|
{ id: 'lastname', label: 'lastname', order: 'desc' },
|
||||||
|
] satisfies PeopleSelectedSortType[];
|
||||||
|
const result = reduceSortsToOrderBy(sorts);
|
||||||
|
expect(result).toEqual([{ firstname: 'asc', lastname: 'desc' }]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,7 +1,38 @@
|
|||||||
import { QueryResult, gql, useQuery } from '@apollo/client';
|
import { QueryResult, gql, useQuery } from '@apollo/client';
|
||||||
import { GraphqlQueryPerson } from '../../interfaces/person.interface';
|
import { GraphqlQueryPerson } from '../../interfaces/person.interface';
|
||||||
|
import { SelectedSortType } from '../../components/table/table-header/SortAndFilterBar';
|
||||||
|
|
||||||
export type OrderBy = Record<string, 'asc' | 'desc'>;
|
export type OrderByFields =
|
||||||
|
| keyof GraphqlQueryPerson
|
||||||
|
| 'fullname'
|
||||||
|
| 'company_name';
|
||||||
|
|
||||||
|
export type OrderBy = Partial<{
|
||||||
|
[key in keyof GraphqlQueryPerson]:
|
||||||
|
| 'asc'
|
||||||
|
| 'desc'
|
||||||
|
| { [key in string]: 'asc' | 'desc' };
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type PeopleSelectedSortType = SelectedSortType<OrderByFields>;
|
||||||
|
|
||||||
|
export const reduceSortsToOrderBy = (
|
||||||
|
sorts: Array<PeopleSelectedSortType>,
|
||||||
|
): OrderBy[] => {
|
||||||
|
const mappedSorts = sorts.reduce((acc, sort) => {
|
||||||
|
const id = sort.id;
|
||||||
|
if (id === 'fullname') {
|
||||||
|
acc['firstname'] = sort.order;
|
||||||
|
acc['lastname'] = sort.order;
|
||||||
|
} else if (id === 'company_name') {
|
||||||
|
acc['company'] = { company_name: sort.order };
|
||||||
|
} else {
|
||||||
|
acc[id] = sort.order;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {} as OrderBy);
|
||||||
|
return [mappedSorts];
|
||||||
|
};
|
||||||
|
|
||||||
export const GET_PEOPLE = gql`
|
export const GET_PEOPLE = gql`
|
||||||
query GetPeople($orderBy: [people_order_by!]) {
|
query GetPeople($orderBy: [people_order_by!]) {
|
||||||
|
|||||||
Reference in New Issue
Block a user