Sammy/t 194 aau when i set sort back and forth the (#103)

* bugfix: use original row id in cells to make sure it rerenders

* feature: implement multiple sorts

* bugfix: recreate new array to make sure component rerenders

* feature: orderBy is an array to keep orders

* test: snapshot the searchTemplate methods

* feature: remove the console log and return undefined

* feature: use orderByTemplate instead of hardcoded orderBy

* refactor: move sort and where filters helpers out of service

* refactor: rename file helper

* refactor: move assert function in test
This commit is contained in:
Sammy Teillet 2023-05-05 16:22:47 +02:00 committed by GitHub
parent f022bf8335
commit b8cd842633
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 253 additions and 156 deletions

View File

@ -87,7 +87,7 @@ const StyledTableScrollableContainer = styled.div`
flex: 1; flex: 1;
`; `;
function Table<TData, SortField extends string, FilterProperies>({ function Table<TData extends { id: string }, SortField, FilterProperies>({
data, data,
columns, columns,
viewName, viewName,
@ -140,7 +140,7 @@ function Table<TData, SortField extends string, FilterProperies>({
<tr key={row.id} data-testid={`row-id-${row.index}`}> <tr key={row.id} data-testid={`row-id-${row.index}`}>
{row.getVisibleCells().map((cell) => { {row.getVisibleCells().map((cell) => {
return ( return (
<td key={cell.id}> <td key={cell.id + row.original.id}>
{flexRender( {flexRender(
cell.column.columnDef.cell, cell.column.columnDef.cell,
cell.getContext(), cell.getContext(),

View File

@ -84,10 +84,11 @@ export function FilterDropdownButton<FilterProperties>({
label: selectedFilter.label, label: selectedFilter.label,
value: value.displayValue, value: value.displayValue,
icon: selectedFilter.icon, icon: selectedFilter.icon,
where: selectedFilter.whereTemplate( where:
selectedFilter.whereTemplate(
selectedFilterOperand, selectedFilterOperand,
value.value, value.value,
), ) || ({} as FilterProperties),
searchResultMapper: selectedFilter.searchResultMapper, searchResultMapper: selectedFilter.searchResultMapper,
}); });
setIsUnfolded(false); setIsUnfolded(false);

View File

@ -41,7 +41,7 @@ const StyledCancelButton = styled.button`
} }
`; `;
function SortAndFilterBar<SortField extends string, FilterProperties>({ function SortAndFilterBar<SortField, FilterProperties>({
sorts, sorts,
onRemoveSort, onRemoveSort,
filters, filters,

View File

@ -8,9 +8,9 @@ type OwnProps<SortField> = {
availableSorts: SortType<SortField>[]; availableSorts: SortType<SortField>[];
}; };
const options: Array<SelectedSortType<string>['order']> = ['asc', 'desc']; const options: Array<SelectedSortType<any>['order']> = ['asc', 'desc'];
export function SortDropdownButton<SortField extends string>({ export function SortDropdownButton<SortField>({
isSortSelected, isSortSelected,
availableSorts, availableSorts,
onSortSelect, onSortSelect,

View File

@ -66,7 +66,7 @@ const StyledFilters = styled.div`
margin-right: ${(props) => props.theme.spacing(2)}; margin-right: ${(props) => props.theme.spacing(2)};
`; `;
function TableHeader<SortField extends string, FilterProperties>({ function TableHeader<SortField, FilterProperties>({
viewName, viewName,
viewIcon, viewIcon,
availableSorts, availableSorts,
@ -84,37 +84,40 @@ function TableHeader<SortField extends string, FilterProperties>({
>([]); >([]);
const sortSelect = useCallback( const sortSelect = useCallback(
(sort: SelectedSortType<SortField>) => { (newSort: SelectedSortType<SortField>) => {
innerSetSorts([sort]); const newSorts = updateSortOrFilterByKey(sorts, newSort);
onSortsUpdate && onSortsUpdate([sort]);
},
[onSortsUpdate],
);
const sortUnselect = useCallback(
(sortId: string) => {
const newSorts = [] as SelectedSortType<SortField>[];
innerSetSorts(newSorts); innerSetSorts(newSorts);
onSortsUpdate && onSortsUpdate(newSorts); onSortsUpdate && onSortsUpdate(newSorts);
}, },
[onSortsUpdate], [onSortsUpdate, sorts],
);
const sortUnselect = useCallback(
(sortKey: string) => {
const newSorts = sorts.filter((sort) => sort.key !== sortKey);
innerSetSorts(newSorts);
onSortsUpdate && onSortsUpdate(newSorts);
},
[onSortsUpdate, sorts],
); );
const filterSelect = useCallback( const filterSelect = useCallback(
(filter: SelectedFilterType<FilterProperties>) => { (filter: SelectedFilterType<FilterProperties>) => {
innerSetFilters([filter]); const newFilters = updateSortOrFilterByKey(filters, filter);
onFiltersUpdate && onFiltersUpdate([filter]);
innerSetFilters(newFilters);
onFiltersUpdate && onFiltersUpdate(newFilters);
}, },
[onFiltersUpdate], [onFiltersUpdate, filters],
); );
const filterUnselect = useCallback( const filterUnselect = useCallback(
(filterId: SelectedFilterType<FilterProperties>['key']) => { (filterId: SelectedFilterType<FilterProperties>['key']) => {
const newFilters = [] as SelectedFilterType<FilterProperties>[]; const newFilters = filters.filter((filter) => filter.key !== filterId);
innerSetFilters(newFilters); innerSetFilters(newFilters);
onFiltersUpdate && onFiltersUpdate(newFilters); onFiltersUpdate && onFiltersUpdate(newFilters);
}, },
[onFiltersUpdate], [onFiltersUpdate, filters],
); );
const filterSearch = useCallback( const filterSearch = useCallback(
@ -161,3 +164,19 @@ function TableHeader<SortField extends string, FilterProperties>({
} }
export default TableHeader; export default TableHeader;
function updateSortOrFilterByKey<SortOrFilter extends { key: string }>(
sorts: Readonly<SortOrFilter[]>,
newSort: SortOrFilter,
): SortOrFilter[] {
const newSorts = [...sorts];
const existingSortIndex = sorts.findIndex((sort) => sort.key === newSort.key);
if (existingSortIndex !== -1) {
newSorts[existingSortIndex] = newSort;
} else {
newSorts.push(newSort);
}
return newSorts;
}

View File

@ -25,12 +25,14 @@ export const RegularSortAndFilterBar = ({ removeFunction }: OwnProps) => {
order: 'asc', order: 'asc',
key: 'test_sort', key: 'test_sort',
icon: <FaArrowDown />, icon: <FaArrowDown />,
_type: 'default_sort',
}, },
{ {
label: 'Test sort 2', label: 'Test sort 2',
order: 'desc', order: 'desc',
key: 'test_sort_2', key: 'test_sort_2',
icon: <FaArrowDown />, icon: <FaArrowDown />,
_type: 'default_sort',
}, },
]} ]}
onRemoveSort={removeFunction} onRemoveSort={removeFunction}

View File

@ -11,6 +11,7 @@ import {
} from 'react-icons/fa'; } from 'react-icons/fa';
import { SortDropdownButton } from '../SortDropdownButton'; import { SortDropdownButton } from '../SortDropdownButton';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Order_By, People_Order_By } from '../../../../generated/graphql';
const component = { const component = {
title: 'SortDropdownButton', title: 'SortDropdownButton',
@ -28,25 +29,31 @@ const availableSorts = [
key: 'fullname', key: 'fullname',
label: 'People', label: 'People',
icon: <FaRegUser />, icon: <FaRegUser />,
_type: 'custom_sort',
orderByTemplate: () => ({ email: Order_By.Asc }),
}, },
{ {
key: 'company_name', key: 'company_name',
label: 'Company', label: 'Company',
icon: <FaRegBuilding />, icon: <FaRegBuilding />,
_type: 'custom_sort',
orderByTemplate: () => ({ email: Order_By.Asc }),
}, },
{ {
key: 'email', key: 'email',
label: 'Email', label: 'Email',
icon: <FaEnvelope />, icon: <FaEnvelope />,
_type: 'default_sort',
}, },
{ key: 'phone', label: 'Phone', icon: <FaPhone /> }, { key: 'phone', label: 'Phone', icon: <FaPhone />, _type: 'default_sort' },
{ {
key: 'created_at', key: 'created_at',
label: 'Created at', label: 'Created at',
icon: <FaCalendar />, icon: <FaCalendar />,
_type: 'default_sort',
}, },
{ key: 'city', label: 'City', icon: <FaMapPin /> }, { key: 'city', label: 'City', icon: <FaMapPin />, _type: 'default_sort' },
] satisfies SortType[]; ] satisfies SortType<People_Order_By>[];
const StyleDiv = styled.div` const StyleDiv = styled.div`
height: 200px; height: 200px;
@ -57,7 +64,7 @@ export const RegularSortDropdownButton = ({ setSorts }: OwnProps) => {
return ( return (
<ThemeProvider theme={lightTheme}> <ThemeProvider theme={lightTheme}>
<StyleDiv> <StyleDiv>
<SortDropdownButton <SortDropdownButton<People_Order_By>
isSortSelected={true} isSortSelected={true}
availableSorts={availableSorts} availableSorts={availableSorts}
onSortSelect={setSorts} onSortSelect={setSorts}

View File

@ -12,11 +12,12 @@ const component = {
export default component; export default component;
export const RegularTableHeader = () => { export const RegularTableHeader = () => {
const availableSorts: Array<SortType> = [ const availableSorts: Array<SortType<Record<'created_at', 'asc'>>> = [
{ {
key: 'created_at', key: 'created_at',
label: 'Created at', label: 'Created at',
icon: <FaCalendar />, icon: <FaCalendar />,
_type: 'default_sort',
}, },
]; ];
return ( return (

View File

@ -19,6 +19,7 @@ it('Checks the default top option is Ascending', async () => {
key: 'email', key: 'email',
icon: <FaEnvelope />, icon: <FaEnvelope />,
order: 'asc', order: 'asc',
_type: 'default_sort',
}); });
}); });
@ -45,5 +46,6 @@ it('Checks the selection of Descending', async () => {
key: 'email', key: 'email',
icon: <FaEnvelope />, icon: <FaEnvelope />,
order: 'desc', order: 'desc',
_type: 'default_sort',
}); });
}); });

View File

@ -0,0 +1,33 @@
import { Order_By } from '../../../generated/graphql';
import { SelectedFilterType, SelectedSortType } from './interface';
export const reduceFiltersToWhere = <T>(
filters: Array<SelectedFilterType<T>>,
): T => {
const where = filters.reduce((acc, filter) => {
const { where } = filter;
return { ...acc, ...where };
}, {} as T);
return where;
};
const mapOrderToOrder_By = (order: string) => {
if (order === 'asc') return Order_By.Asc;
return Order_By.Desc;
};
export const defaultOrderByTemplateFactory =
(key: string) => (order: string) => ({
[key]: order,
});
export const reduceSortsToOrderBy = <OrderByTemplate>(
sorts: Array<SelectedSortType<OrderByTemplate>>,
): OrderByTemplate[] => {
const mappedSorts = sorts.map((sort) => {
if (sort._type === 'custom_sort')
return sort.orderByTemplate(mapOrderToOrder_By(sort.order));
return defaultOrderByTemplateFactory(sort.key as string)(sort.order);
});
return mappedSorts as OrderByTemplate[];
};

View File

@ -2,23 +2,27 @@ import { DocumentNode } from 'graphql';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { import {
Companies_Bool_Exp, Companies_Bool_Exp,
Order_By,
People_Bool_Exp, People_Bool_Exp,
Users_Bool_Exp, Users_Bool_Exp,
} from '../../../generated/graphql'; } from '../../../generated/graphql';
import { GraphqlQueryCompany } from '../../../interfaces/company.interface';
import {
SEARCH_COMPANY_QUERY,
SEARCH_PEOPLE_QUERY,
} from '../../../services/search/search';
import { GraphqlQueryPerson } from '../../../interfaces/person.interface';
export type SortType<SortKey = string> = { export type SortType<OrderByTemplate> =
| {
_type: 'default_sort';
label: string; label: string;
key: SortKey; key: keyof OrderByTemplate & string;
icon?: ReactNode; icon?: ReactNode;
}
| {
_type: 'custom_sort';
label: string;
key: string;
icon?: ReactNode;
orderByTemplate: (order: Order_By) => OrderByTemplate;
}; };
export type SelectedSortType<SortField = string> = SortType<SortField> & { export type SelectedSortType<OrderByTemplate> = SortType<OrderByTemplate> & {
order: 'asc' | 'desc'; order: 'asc' | 'desc';
}; };
@ -30,7 +34,7 @@ export type FilterType<WhereTemplate, FilterValue = Record<string, any>> = {
whereTemplate: ( whereTemplate: (
operand: FilterOperandType, operand: FilterOperandType,
value: FilterValue, value: FilterValue,
) => WhereTemplate; ) => WhereTemplate | undefined;
searchQuery: DocumentNode; searchQuery: DocumentNode;
searchTemplate: ( searchTemplate: (
searchInput: string, searchInput: string,
@ -52,25 +56,3 @@ export type SelectedFilterType<WhereTemplate> = FilterType<WhereTemplate> & {
operand: FilterOperandType; operand: FilterOperandType;
where: WhereTemplate; where: WhereTemplate;
}; };
export function assertFilterUseCompanySearch<FilterValue>(
filter: FilterType<People_Bool_Exp>,
): filter is FilterType<People_Bool_Exp> & {
searchResultMapper: (data: GraphqlQueryCompany) => {
displayValue: string;
value: FilterValue;
};
} {
return filter.searchQuery === SEARCH_COMPANY_QUERY;
}
export function assertFilterUsePeopleSearch<FilterValue>(
filter: FilterType<People_Bool_Exp>,
): filter is FilterType<People_Bool_Exp> & {
searchResultMapper: (data: GraphqlQueryPerson) => {
displayValue: string;
value: FilterValue;
};
} {
return filter.searchQuery === SEARCH_PEOPLE_QUERY;
}

View File

@ -5,12 +5,12 @@ import { useState, useCallback } from 'react';
import { import {
CompaniesSelectedSortType, CompaniesSelectedSortType,
defaultOrderBy, defaultOrderBy,
reduceSortsToOrderBy,
useCompaniesQuery, useCompaniesQuery,
} from '../../services/companies'; } from '../../services/companies';
import Table from '../../components/table/Table'; import Table from '../../components/table/Table';
import { mapCompany } from '../../interfaces/company.interface'; import { mapCompany } from '../../interfaces/company.interface';
import { companiesColumns, sortsAvailable } from './companies-table'; import { companiesColumns, sortsAvailable } from './companies-table';
import { reduceSortsToOrderBy } from '../../components/table/table-header/helpers';
const StyledCompaniesContainer = styled.div` const StyledCompaniesContainer = styled.div`
display: flex; display: flex;

View File

@ -1,6 +1,6 @@
import { createColumnHelper } from '@tanstack/react-table'; import { createColumnHelper } from '@tanstack/react-table';
import { Company } from '../../interfaces/company.interface'; import { Company } from '../../interfaces/company.interface';
import { OrderByFields, 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 Checkbox from '../../components/form/Checkbox';
import CompanyChip from '../../components/chips/CompanyChip'; import CompanyChip from '../../components/chips/CompanyChip';
@ -17,21 +17,24 @@ import {
} from 'react-icons/fa'; } from 'react-icons/fa';
import ClickableCell from '../../components/table/ClickableCell'; import ClickableCell from '../../components/table/ClickableCell';
import PersonChip from '../../components/chips/PersonChip'; import PersonChip from '../../components/chips/PersonChip';
import { SortType } from '../../components/table/table-header/interface';
import EditableChip from '../../components/table/editable-cell/EditableChip'; import EditableChip from '../../components/table/editable-cell/EditableChip';
import { SortType } from '../../components/table/table-header/interface';
import { Companies_Order_By } from '../../generated/graphql';
export const sortsAvailable = [ export const sortsAvailable = [
{ {
key: 'name', key: 'name',
label: 'Name', label: 'Name',
icon: undefined, icon: undefined,
_type: 'default_sort',
}, },
{ {
key: 'domain_name', key: 'domain_name',
label: 'Domain', label: 'Domain',
icon: undefined, icon: undefined,
_type: 'default_sort',
}, },
] satisfies Array<SortType<OrderByFields>>; ] satisfies Array<SortType<Companies_Order_By>>;
const columnHelper = createColumnHelper<Company>(); const columnHelper = createColumnHelper<Company>();
export const companiesColumns = [ export const companiesColumns = [

View File

@ -12,13 +12,15 @@ import { useCallback, useState } from 'react';
import { import {
PeopleSelectedSortType, PeopleSelectedSortType,
defaultOrderBy, defaultOrderBy,
reduceFiltersToWhere,
reduceSortsToOrderBy,
usePeopleQuery, usePeopleQuery,
} from '../../services/people'; } from '../../services/people';
import { useSearch } from '../../services/search/search'; import { useSearch } from '../../services/search/search';
import { People_Bool_Exp } from '../../generated/graphql'; import { People_Bool_Exp } from '../../generated/graphql';
import { SelectedFilterType } from '../../components/table/table-header/interface'; import { SelectedFilterType } from '../../components/table/table-header/interface';
import {
reduceFiltersToWhere,
reduceSortsToOrderBy,
} from '../../components/table/table-header/helpers';
const StyledPeopleContainer = styled.div` const StyledPeopleContainer = styled.div`
display: flex; display: flex;

View File

@ -93,3 +93,44 @@ Object {
}, },
} }
`; `;
exports[`PeopleFilter should render the serch city with the searchValue 1`] = `
Object {
"city": Object {
"_ilike": "%Search value%",
},
}
`;
exports[`PeopleFilter should render the serch company_name with the searchValue 1`] = `
Object {
"name": Object {
"_ilike": "%Search value%",
},
}
`;
exports[`PeopleFilter should render the serch email with the searchValue 1`] = `
Object {
"email": Object {
"_ilike": "%Search value%",
},
}
`;
exports[`PeopleFilter should render the serch fullname with the searchValue 1`] = `
Object {
"_or": Array [
Object {
"firstname": Object {
"_ilike": "%Search value%",
},
},
Object {
"lastname": Object {
"_ilike": "%Search value%",
},
},
],
}
`;

View File

@ -1,12 +1,37 @@
import { import { FilterType } from '../../../components/table/table-header/interface';
assertFilterUseCompanySearch, import { People_Bool_Exp } from '../../../generated/graphql';
assertFilterUsePeopleSearch, import { GraphqlQueryCompany } from '../../../interfaces/company.interface';
} from '../../../components/table/table-header/interface';
import { GraphqlQueryPerson } from '../../../interfaces/person.interface'; import { GraphqlQueryPerson } from '../../../interfaces/person.interface';
import {
SEARCH_COMPANY_QUERY,
SEARCH_PEOPLE_QUERY,
} from '../../../services/search/search';
import { mockCompanyData } from '../../companies/__stories__/mock-data'; import { mockCompanyData } from '../../companies/__stories__/mock-data';
import { defaultData } from '../default-data'; import { defaultData } from '../default-data';
import { availableFilters } from '../people-table'; import { availableFilters } from '../people-table';
function assertFilterUseCompanySearch<FilterValue>(
filter: FilterType<People_Bool_Exp>,
): filter is FilterType<People_Bool_Exp> & {
searchResultMapper: (data: GraphqlQueryCompany) => {
displayValue: string;
value: FilterValue;
};
} {
return filter.searchQuery === SEARCH_COMPANY_QUERY;
}
function assertFilterUsePeopleSearch<FilterValue>(
filter: FilterType<People_Bool_Exp>,
): filter is FilterType<People_Bool_Exp> & {
searchResultMapper: (data: GraphqlQueryPerson) => {
displayValue: string;
value: FilterValue;
};
} {
return filter.searchQuery === SEARCH_PEOPLE_QUERY;
}
const JohnDoeUser = defaultData.find( const JohnDoeUser = defaultData.find(
(user) => user.email === 'john@linkedin.com', (user) => user.email === 'john@linkedin.com',
) as GraphqlQueryPerson; ) as GraphqlQueryPerson;
@ -33,5 +58,8 @@ describe('PeopleFilter', () => {
} }
} }
}); });
it(`should render the serch ${filter.key} with the searchValue`, () => {
expect(filter.searchTemplate('Search value')).toMatchSnapshot();
});
} }
}); });

View File

@ -17,12 +17,16 @@ import CompanyChip from '../../components/chips/CompanyChip';
import { GraphqlQueryPerson, Person } from '../../interfaces/person.interface'; import { GraphqlQueryPerson, Person } from '../../interfaces/person.interface';
import PipeChip from '../../components/chips/PipeChip'; import PipeChip from '../../components/chips/PipeChip';
import EditableText from '../../components/table/editable-cell/EditableText'; import EditableText from '../../components/table/editable-cell/EditableText';
import { OrderByFields, updatePerson } from '../../services/people'; import { updatePerson } from '../../services/people';
import { import {
FilterType, FilterType,
SortType, SortType,
} from '../../components/table/table-header/interface'; } from '../../components/table/table-header/interface';
import { People_Bool_Exp } from '../../generated/graphql'; import {
Order_By,
People_Bool_Exp,
People_Order_By,
} from '../../generated/graphql';
import { import {
SEARCH_COMPANY_QUERY, SEARCH_COMPANY_QUERY,
SEARCH_PEOPLE_QUERY, SEARCH_PEOPLE_QUERY,
@ -36,25 +40,44 @@ export const availableSorts = [
key: 'fullname', key: 'fullname',
label: 'People', label: 'People',
icon: <FaRegUser />, icon: <FaRegUser />,
_type: 'custom_sort',
orderByTemplate: (order: Order_By) => ({
firstname: order,
lastname: order,
}),
}, },
{ {
key: 'company_name', key: 'company_name',
label: 'Company', label: 'Company',
icon: <FaRegBuilding />, icon: <FaRegBuilding />,
_type: 'custom_sort',
orderByTemplate: (order: Order_By) => ({ company: { name: order } }),
}, },
{ {
key: 'email', key: 'email',
label: 'Email', label: 'Email',
icon: <FaEnvelope />, icon: <FaEnvelope />,
_type: 'default_sort',
},
{
key: 'phone',
label: 'Phone',
icon: <FaPhone />,
_type: 'default_sort',
}, },
{ key: 'phone', label: 'Phone', icon: <FaPhone /> },
{ {
key: 'created_at', key: 'created_at',
label: 'Created at', label: 'Created at',
icon: <FaCalendar />, icon: <FaCalendar />,
_type: 'default_sort',
}, },
{ key: 'city', label: 'City', icon: <FaMapPin /> }, {
] satisfies Array<SortType<OrderByFields>>; key: 'city',
label: 'City',
icon: <FaMapPin />,
_type: 'default_sort',
},
] satisfies Array<SortType<People_Order_By>>;
const fullnameFilter = { const fullnameFilter = {
key: 'fullname', key: 'fullname',
@ -80,8 +103,6 @@ const fullnameFilter = {
}, },
}; };
} }
console.error(Error(`Unhandled operand: ${operand.keyWord}`));
return {};
}, },
searchQuery: SEARCH_PEOPLE_QUERY, searchQuery: SEARCH_PEOPLE_QUERY,
searchTemplate: (searchInput: string) => ({ searchTemplate: (searchInput: string) => ({
@ -116,8 +137,6 @@ const companyFilter = {
_not: { company: { name: { _eq: companyName } } }, _not: { company: { name: { _eq: companyName } } },
}; };
} }
console.error(Error(`Unhandled operand: ${operand.keyWord}`));
return {};
}, },
searchQuery: SEARCH_COMPANY_QUERY, searchQuery: SEARCH_COMPANY_QUERY,
searchTemplate: (searchInput: string) => ({ searchTemplate: (searchInput: string) => ({
@ -149,8 +168,6 @@ const emailFilter = {
_not: { email: { _eq: email } }, _not: { email: { _eq: email } },
}; };
} }
console.error(Error(`Unhandled operand: ${operand.keyWord}`));
return {};
}, },
searchQuery: SEARCH_PEOPLE_QUERY, searchQuery: SEARCH_PEOPLE_QUERY,
searchTemplate: (searchInput: string) => ({ searchTemplate: (searchInput: string) => ({
@ -182,8 +199,6 @@ const cityFilter = {
_not: { city: { _eq: city } }, _not: { city: { _eq: city } },
}; };
} }
console.error(Error(`Unhandled operand: ${operand.keyWord}`));
return {};
}, },
searchQuery: SEARCH_PEOPLE_QUERY, searchQuery: SEARCH_PEOPLE_QUERY,
searchTemplate: (searchInput: string) => ({ searchTemplate: (searchInput: string) => ({

View File

@ -1,12 +1,18 @@
import { CompaniesSelectedSortType, reduceSortsToOrderBy } from './select'; import { reduceSortsToOrderBy } from '../../components/table/table-header/helpers';
import { CompaniesSelectedSortType } from './select';
describe('reduceSortsToOrderBy', () => { describe('reduceSortsToOrderBy', () => {
it('should return an array of objects with the id as key and the order as value', () => { it('should return an array of objects with the id as key and the order as value', () => {
const sorts = [ const sorts = [
{ key: 'name', label: 'name', order: 'asc' }, { key: 'name', label: 'name', order: 'asc', _type: 'default_sort' },
{ key: 'domain_name', label: 'domain_name', order: 'desc' }, {
key: 'domain_name',
label: 'domain_name',
order: 'desc',
_type: 'default_sort',
},
] satisfies CompaniesSelectedSortType[]; ] satisfies CompaniesSelectedSortType[];
const result = reduceSortsToOrderBy(sorts); const result = reduceSortsToOrderBy(sorts);
expect(result).toEqual([{ name: 'asc', domain_name: 'desc' }]); expect(result).toEqual([{ name: 'asc' }, { domain_name: 'desc' }]);
}); });
}); });

View File

@ -3,25 +3,7 @@ import { Order_By, Companies_Order_By } from '../../generated/graphql';
import { GraphqlQueryCompany } from '../../interfaces/company.interface'; import { GraphqlQueryCompany } from '../../interfaces/company.interface';
import { SelectedSortType } from '../../components/table/table-header/interface'; import { SelectedSortType } from '../../components/table/table-header/interface';
export type OrderByFields = keyof Companies_Order_By | 'domain_name' | 'name'; export type CompaniesSelectedSortType = SelectedSortType<Companies_Order_By>;
export type CompaniesSelectedSortType = SelectedSortType<OrderByFields>;
const mapOrder = (order: 'asc' | 'desc'): Order_By => {
return order === 'asc' ? Order_By.Asc : Order_By.Desc;
};
export const reduceSortsToOrderBy = (
sorts: Array<CompaniesSelectedSortType>,
): Companies_Order_By[] => {
const mappedSorts = sorts.reduce((acc, sort) => {
const id = sort.key;
const order = mapOrder(sort.order);
acc[id] = order;
return acc;
}, {} as Companies_Order_By);
return [mappedSorts];
};
export const GET_COMPANIES = gql` export const GET_COMPANIES = gql`
query GetCompanies($orderBy: [companies_order_by!]) { query GetCompanies($orderBy: [companies_order_by!]) {

View File

@ -1,12 +1,23 @@
import { PeopleSelectedSortType, reduceSortsToOrderBy } from './select'; import { reduceSortsToOrderBy } from '../../components/table/table-header/helpers';
import { PeopleSelectedSortType } from './select';
describe('reduceSortsToOrderBy', () => { describe('reduceSortsToOrderBy', () => {
it('should return an array of objects with the id as key and the order as value', () => { it('should return an array of objects with the id as key and the order as value', () => {
const sorts = [ const sorts = [
{ key: 'firstname', label: 'firstname', order: 'asc' }, {
{ key: 'lastname', label: 'lastname', order: 'desc' }, key: 'firstname',
label: 'firstname',
order: 'asc',
_type: 'default_sort',
},
{
key: 'lastname',
label: 'lastname',
order: 'desc',
_type: 'default_sort',
},
] satisfies PeopleSelectedSortType[]; ] satisfies PeopleSelectedSortType[];
const result = reduceSortsToOrderBy(sorts); const result = reduceSortsToOrderBy(sorts);
expect(result).toEqual([{ firstname: 'asc', lastname: 'desc' }]); expect(result).toEqual([{ firstname: 'asc' }, { lastname: 'desc' }]);
}); });
}); });

View File

@ -5,47 +5,9 @@ import {
People_Bool_Exp, People_Bool_Exp,
People_Order_By, People_Order_By,
} from '../../generated/graphql'; } from '../../generated/graphql';
import { import { SelectedSortType } from '../../components/table/table-header/interface';
SelectedFilterType,
SelectedSortType,
} from '../../components/table/table-header/interface';
export type OrderByFields = keyof People_Order_By | 'fullname' | 'company_name'; export type PeopleSelectedSortType = SelectedSortType<People_Order_By>;
export type PeopleSelectedSortType = SelectedSortType<OrderByFields>;
const mapOrder = (order: 'asc' | 'desc'): Order_By => {
return order === 'asc' ? Order_By.Asc : Order_By.Desc;
};
export const reduceFiltersToWhere = <T>(
filters: Array<SelectedFilterType<T>>,
): T => {
const where = filters.reduce((acc, filter) => {
const { where } = filter;
return { ...acc, ...where };
}, {} as T);
return where;
};
export const reduceSortsToOrderBy = (
sorts: Array<PeopleSelectedSortType>,
): People_Order_By[] => {
const mappedSorts = sorts.reduce((acc, sort) => {
const id = sort.key;
const order = mapOrder(sort.order);
if (id === 'fullname') {
acc['firstname'] = order;
acc['lastname'] = order;
} else if (id === 'company_name') {
acc['company'] = { name: order };
} else {
acc[id] = order;
}
return acc;
}, {} as People_Order_By);
return [mappedSorts];
};
export const GET_PEOPLE = gql` export const GET_PEOPLE = gql`
query GetPeople( query GetPeople(