Sammy/t 134 i see all filters in the dropdown (#78)
* feature: add filter dropdown * test: add story for FilterDropdown * feature: display filterOperand on top of dropdown * feature: display filter operand * feature: fix index and display selected filter * refactor: set TopOption button inside dropdown file * feature: move availableFilters outside the fitler component * refactor: make the available sorts and filter optionnal * refactor: rename availableSorts * feature: add a resetState property on onOutsideClick * feature: add filters and set filters on Dropdown component * feature: set filters on click in dropdown * test: verify button is active after filters are set * feature: display sorts and filters * refactor: move SelectedFilters in SortAndFilter * refactor: move SelectedFilters in dedicated file * refactor: remove Id and use Key
This commit is contained in:
@ -9,7 +9,11 @@ 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 { SelectedSortType, SortType } from './table-header/SortAndFilterBar';
|
import {
|
||||||
|
FilterType,
|
||||||
|
SelectedSortType,
|
||||||
|
SortType,
|
||||||
|
} from './table-header/interface';
|
||||||
|
|
||||||
type OwnProps<TData, SortField> = {
|
type OwnProps<TData, SortField> = {
|
||||||
data: Array<TData>;
|
data: Array<TData>;
|
||||||
@ -17,7 +21,8 @@ type OwnProps<TData, SortField> = {
|
|||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: IconProp;
|
viewIcon?: IconProp;
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
sortsAvailable?: Array<SortType<SortField>>;
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
|
availableFilters?: FilterType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledTable = styled.table`
|
const StyledTable = styled.table`
|
||||||
@ -77,7 +82,8 @@ function Table<TData, SortField extends string>({
|
|||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
sortsAvailable,
|
availableSorts,
|
||||||
|
availableFilters,
|
||||||
}: OwnProps<TData, SortField>) {
|
}: OwnProps<TData, SortField>) {
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
@ -91,7 +97,8 @@ function Table<TData, SortField extends string>({
|
|||||||
viewName={viewName}
|
viewName={viewName}
|
||||||
viewIcon={viewIcon}
|
viewIcon={viewIcon}
|
||||||
onSortsUpdate={onSortsUpdate}
|
onSortsUpdate={onSortsUpdate}
|
||||||
sortsAvailable={sortsAvailable || []}
|
availableSorts={availableSorts}
|
||||||
|
availableFilters={availableFilters}
|
||||||
/>
|
/>
|
||||||
<StyledTableScrollableContainer>
|
<StyledTableScrollableContainer>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import styled from '@emotion/styled';
|
|||||||
import { useRef, ReactNode } from 'react';
|
import { useRef, ReactNode } from 'react';
|
||||||
import { useOutsideAlerter } from '../../../hooks/useOutsideAlerter';
|
import { useOutsideAlerter } from '../../../hooks/useOutsideAlerter';
|
||||||
import { modalBackground } from '../../../layout/styles/themes';
|
import { modalBackground } from '../../../layout/styles/themes';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faAngleDown } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -9,6 +11,7 @@ type OwnProps = {
|
|||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
isUnfolded?: boolean;
|
isUnfolded?: boolean;
|
||||||
setIsUnfolded?: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsUnfolded?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
resetState?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledDropdownButtonContainer = styled.div`
|
const StyledDropdownButtonContainer = styled.div`
|
||||||
@ -114,6 +117,7 @@ function DropdownButton({
|
|||||||
children,
|
children,
|
||||||
isUnfolded = false,
|
isUnfolded = false,
|
||||||
setIsUnfolded,
|
setIsUnfolded,
|
||||||
|
resetState,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const onButtonClick = () => {
|
const onButtonClick = () => {
|
||||||
setIsUnfolded && setIsUnfolded(!isUnfolded);
|
setIsUnfolded && setIsUnfolded(!isUnfolded);
|
||||||
@ -121,6 +125,7 @@ function DropdownButton({
|
|||||||
|
|
||||||
const onOutsideClick = () => {
|
const onOutsideClick = () => {
|
||||||
setIsUnfolded && setIsUnfolded(false);
|
setIsUnfolded && setIsUnfolded(false);
|
||||||
|
resetState && resetState();
|
||||||
};
|
};
|
||||||
|
|
||||||
const dropdownRef = useRef(null);
|
const dropdownRef = useRef(null);
|
||||||
@ -132,6 +137,7 @@ function DropdownButton({
|
|||||||
isUnfolded={isUnfolded}
|
isUnfolded={isUnfolded}
|
||||||
onClick={onButtonClick}
|
onClick={onButtonClick}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
|
aria-selected={isActive}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</StyledDropdownButton>
|
</StyledDropdownButton>
|
||||||
@ -142,8 +148,20 @@ function DropdownButton({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyleAngleDownContainer = styled.div`
|
||||||
|
margin-left: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function DropdownTopOptionAngleDown() {
|
||||||
|
return (
|
||||||
|
<StyleAngleDownContainer>
|
||||||
|
<FontAwesomeIcon icon={faAngleDown} />
|
||||||
|
</StyleAngleDownContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
DropdownButton.StyledDropdownItem = StyledDropdownItem;
|
DropdownButton.StyledDropdownItem = StyledDropdownItem;
|
||||||
DropdownButton.StyledDropdownTopOption = StyledDropdownTopOption;
|
DropdownButton.StyledDropdownTopOption = StyledDropdownTopOption;
|
||||||
|
DropdownButton.StyledDropdownTopOptionAngleDown = DropdownTopOptionAngleDown;
|
||||||
DropdownButton.StyledIcon = StyledIcon;
|
DropdownButton.StyledIcon = StyledIcon;
|
||||||
|
|
||||||
export default DropdownButton;
|
export default DropdownButton;
|
||||||
|
|||||||
134
front/src/components/table/table-header/FilterDropdownButton.tsx
Normal file
134
front/src/components/table/table-header/FilterDropdownButton.tsx
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import DropdownButton from './DropdownButton';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { FilterType, SelectedFilterType } from './interface';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
filters: SelectedFilterType[];
|
||||||
|
setFilters: (sorts: SelectedFilterType[]) => void;
|
||||||
|
availableFilters: FilterType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type FilterOperandType = { label: string; id: string };
|
||||||
|
|
||||||
|
const filterOperands: FilterOperandType[] = [
|
||||||
|
{ label: 'Include', id: 'include' },
|
||||||
|
{ label: "Doesn't include", id: 'not-include' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const someFieldRandomValue = [
|
||||||
|
'John Doe',
|
||||||
|
'Jane Doe',
|
||||||
|
'John Smith',
|
||||||
|
'Jane Smith',
|
||||||
|
'John Johnson',
|
||||||
|
'Jane Johnson',
|
||||||
|
'John Williams',
|
||||||
|
'Jane Williams',
|
||||||
|
'John Brown',
|
||||||
|
'Jane Brown',
|
||||||
|
'John Jones',
|
||||||
|
'Jane Jones',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function FilterDropdownButton({
|
||||||
|
availableFilters,
|
||||||
|
setFilters,
|
||||||
|
filters,
|
||||||
|
}: OwnProps) {
|
||||||
|
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||||
|
|
||||||
|
const [isOptionUnfolded, setIsOptionUnfolded] = useState(false);
|
||||||
|
|
||||||
|
const [selectedFilter, setSelectedFilter] = useState<FilterType | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedFilterOperand, setSelectedFilterOperand] =
|
||||||
|
useState<FilterOperandType>(filterOperands[0]);
|
||||||
|
|
||||||
|
const resetState = useCallback(() => {
|
||||||
|
setIsOptionUnfolded(false);
|
||||||
|
setSelectedFilter(undefined);
|
||||||
|
setSelectedFilterOperand(filterOperands[0]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownButton
|
||||||
|
label="Filter"
|
||||||
|
isActive={filters.length > 0}
|
||||||
|
isUnfolded={isUnfolded}
|
||||||
|
setIsUnfolded={setIsUnfolded}
|
||||||
|
resetState={resetState}
|
||||||
|
>
|
||||||
|
{selectedFilter
|
||||||
|
? isOptionUnfolded
|
||||||
|
? filterOperands.map((filterOperand, index) => (
|
||||||
|
<DropdownButton.StyledDropdownItem
|
||||||
|
key={`select-filter-operand-${index}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedFilterOperand(filterOperand);
|
||||||
|
setIsOptionUnfolded(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{filterOperand.label}
|
||||||
|
</DropdownButton.StyledDropdownItem>
|
||||||
|
))
|
||||||
|
: [
|
||||||
|
<DropdownButton.StyledDropdownTopOption
|
||||||
|
key={'selected-filter'}
|
||||||
|
onClick={() => setSelectedFilter(undefined)}
|
||||||
|
>
|
||||||
|
<DropdownButton.StyledIcon>
|
||||||
|
{selectedFilter.icon && (
|
||||||
|
<FontAwesomeIcon icon={selectedFilter.icon} />
|
||||||
|
)}
|
||||||
|
</DropdownButton.StyledIcon>
|
||||||
|
{selectedFilter.label}
|
||||||
|
<DropdownButton.StyledDropdownTopOptionAngleDown />
|
||||||
|
</DropdownButton.StyledDropdownTopOption>,
|
||||||
|
<DropdownButton.StyledDropdownTopOption
|
||||||
|
key={'selected-filter-operand'}
|
||||||
|
onClick={() => setIsOptionUnfolded(true)}
|
||||||
|
>
|
||||||
|
{selectedFilterOperand.label}
|
||||||
|
|
||||||
|
<DropdownButton.StyledDropdownTopOptionAngleDown />
|
||||||
|
</DropdownButton.StyledDropdownTopOption>,
|
||||||
|
someFieldRandomValue.map((value, index) => (
|
||||||
|
<DropdownButton.StyledDropdownItem
|
||||||
|
key={`fields-value-${index}`}
|
||||||
|
onClick={() => {
|
||||||
|
setFilters([
|
||||||
|
{
|
||||||
|
id: value,
|
||||||
|
operand: selectedFilterOperand,
|
||||||
|
label: selectedFilter.label,
|
||||||
|
value: value,
|
||||||
|
icon: selectedFilter.icon,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setIsUnfolded(false);
|
||||||
|
setSelectedFilter(undefined);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</DropdownButton.StyledDropdownItem>
|
||||||
|
)),
|
||||||
|
]
|
||||||
|
: availableFilters.map((filter, index) => (
|
||||||
|
<DropdownButton.StyledDropdownItem
|
||||||
|
key={`select-filter-${index}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedFilter(filter);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownButton.StyledIcon>
|
||||||
|
{filter.icon && <FontAwesomeIcon icon={filter.icon} />}
|
||||||
|
</DropdownButton.StyledIcon>
|
||||||
|
{filter.label}
|
||||||
|
</DropdownButton.StyledDropdownItem>
|
||||||
|
))}
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,21 +1,13 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
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';
|
||||||
|
import { SelectedFilterType, SelectedSortType } from './interface';
|
||||||
|
|
||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField> = {
|
||||||
sorts: Array<SelectedSortType<SortField>>;
|
sorts: Array<SelectedSortType<SortField>>;
|
||||||
onRemoveSort: (sortId: string) => void;
|
onRemoveSort: (sortId: SelectedSortType<SortField>['key']) => void;
|
||||||
};
|
filters: Array<SelectedFilterType>;
|
||||||
|
onRemoveFilter: (filterId: SelectedFilterType['id']) => void;
|
||||||
export type SortType<SortIds = string> = {
|
|
||||||
label: string;
|
|
||||||
id: SortIds;
|
|
||||||
icon?: IconProp;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectedSortType<SortField = string> = SortType<SortField> & {
|
|
||||||
order: 'asc' | 'desc';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledBar = styled.div`
|
const StyledBar = styled.div`
|
||||||
@ -50,24 +42,40 @@ const StyledCancelButton = styled.button`
|
|||||||
function SortAndFilterBar<SortField extends string>({
|
function SortAndFilterBar<SortField extends string>({
|
||||||
sorts,
|
sorts,
|
||||||
onRemoveSort,
|
onRemoveSort,
|
||||||
|
filters,
|
||||||
|
onRemoveFilter,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
return (
|
return (
|
||||||
<StyledBar>
|
<StyledBar>
|
||||||
{sorts.map((sort) => {
|
{sorts.map((sort) => {
|
||||||
return (
|
return (
|
||||||
<SortOrFilterChip
|
<SortOrFilterChip
|
||||||
key={sort.id}
|
key={sort.key}
|
||||||
label={sort.label}
|
label={sort.label}
|
||||||
id={sort.id}
|
id={sort.key}
|
||||||
icon={sort.order === 'asc' ? faArrowDown : faArrowUp}
|
icon={sort.order === 'asc' ? faArrowDown : faArrowUp}
|
||||||
onRemove={() => onRemoveSort(sort.id)}
|
onRemove={() => onRemoveSort(sort.key)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{sorts.length > 0 && (
|
{filters.map((filter) => {
|
||||||
|
return (
|
||||||
|
<SortOrFilterChip
|
||||||
|
key={filter.id}
|
||||||
|
label={`${filter.label}: ${filter.operand.label} ${filter.value}`}
|
||||||
|
id={filter.id}
|
||||||
|
icon={filter.icon}
|
||||||
|
onRemove={() => onRemoveFilter(filter.id)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{filters.length + sorts.length > 0 && (
|
||||||
<StyledCancelButton
|
<StyledCancelButton
|
||||||
data-testid={'cancel-button'}
|
data-testid={'cancel-button'}
|
||||||
onClick={() => sorts.forEach((i) => onRemoveSort(i.id))}
|
onClick={() => {
|
||||||
|
sorts.forEach((i) => onRemoveSort(i.key));
|
||||||
|
filters.forEach((i) => onRemoveFilter(i.id));
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</StyledCancelButton>
|
</StyledCancelButton>
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import DropdownButton from './DropdownButton';
|
import DropdownButton from './DropdownButton';
|
||||||
import { SelectedSortType, SortType } from './SortAndFilterBar';
|
import { SelectedSortType, SortType } from './interface';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faAngleDown } from '@fortawesome/pro-regular-svg-icons';
|
|
||||||
|
|
||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField> = {
|
||||||
sorts: SelectedSortType<SortField>[];
|
sorts: SelectedSortType<SortField>[];
|
||||||
setSorts: (sorts: SelectedSortType<SortField>[]) => void;
|
setSorts: (sorts: SelectedSortType<SortField>[]) => void;
|
||||||
sortsAvailable: SortType<SortField>[];
|
availableSorts: SortType<SortField>[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: Array<SelectedSortType<string>['order']> = ['asc', 'desc'];
|
const options: Array<SelectedSortType<string>['order']> = ['asc', 'desc'];
|
||||||
|
|
||||||
export function SortDropdownButton<SortField extends string>({
|
export function SortDropdownButton<SortField extends string>({
|
||||||
sortsAvailable,
|
availableSorts,
|
||||||
setSorts,
|
setSorts,
|
||||||
sorts,
|
sorts,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
@ -32,12 +31,18 @@ export function SortDropdownButton<SortField extends string>({
|
|||||||
[setSorts, selectedSortDirection],
|
[setSorts, selectedSortDirection],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resetState = useCallback(() => {
|
||||||
|
setIsOptionUnfolded(false);
|
||||||
|
setSelectedSortDirection('asc');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
label="Sort"
|
label="Sort"
|
||||||
isActive={sorts.length > 0}
|
isActive={sorts.length > 0}
|
||||||
isUnfolded={isUnfolded}
|
isUnfolded={isUnfolded}
|
||||||
setIsUnfolded={setIsUnfolded}
|
setIsUnfolded={setIsUnfolded}
|
||||||
|
resetState={resetState}
|
||||||
>
|
>
|
||||||
{isOptionUnfolded
|
{isOptionUnfolded
|
||||||
? options.map((option, index) => (
|
? options.map((option, index) => (
|
||||||
@ -58,9 +63,9 @@ export function SortDropdownButton<SortField extends string>({
|
|||||||
>
|
>
|
||||||
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||||
|
|
||||||
<FontAwesomeIcon icon={faAngleDown} />
|
<DropdownButton.StyledDropdownTopOptionAngleDown />
|
||||||
</DropdownButton.StyledDropdownTopOption>,
|
</DropdownButton.StyledDropdownTopOption>,
|
||||||
...sortsAvailable.map((sort, index) => (
|
...availableSorts.map((sort, index) => (
|
||||||
<DropdownButton.StyledDropdownItem
|
<DropdownButton.StyledDropdownItem
|
||||||
key={index + 1}
|
key={index + 1}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -2,18 +2,24 @@ 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, {
|
import {
|
||||||
|
FilterType,
|
||||||
|
SelectedFilterType,
|
||||||
SelectedSortType,
|
SelectedSortType,
|
||||||
SortType,
|
SortType,
|
||||||
} from './SortAndFilterBar';
|
} from './interface';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { SortDropdownButton } from './SortDropdownButton';
|
import { SortDropdownButton } from './SortDropdownButton';
|
||||||
|
import { FilterDropdownButton } from './FilterDropdownButton';
|
||||||
|
import SortAndFilterBar from './SortAndFilterBar';
|
||||||
|
|
||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField> = {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: IconProp;
|
viewIcon?: IconProp;
|
||||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
sortsAvailable: Array<SortType<SortField>>;
|
onFiltersUpdate?: (sorts: Array<SelectedFilterType>) => void;
|
||||||
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
|
availableFilters?: FilterType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -56,7 +62,9 @@ function TableHeader<SortField extends string>({
|
|||||||
viewName,
|
viewName,
|
||||||
viewIcon,
|
viewIcon,
|
||||||
onSortsUpdate,
|
onSortsUpdate,
|
||||||
sortsAvailable,
|
onFiltersUpdate,
|
||||||
|
availableSorts,
|
||||||
|
availableFilters,
|
||||||
}: OwnProps<SortField>) {
|
}: OwnProps<SortField>) {
|
||||||
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
||||||
[],
|
[],
|
||||||
@ -79,6 +87,25 @@ function TableHeader<SortField extends string>({
|
|||||||
[onSortsUpdate],
|
[onSortsUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [filters, innerSetFilters] = useState<Array<SelectedFilterType>>([]);
|
||||||
|
|
||||||
|
const setFilters = useCallback(
|
||||||
|
(filters: SelectedFilterType[]) => {
|
||||||
|
innerSetFilters(filters);
|
||||||
|
onFiltersUpdate && onFiltersUpdate(filters);
|
||||||
|
},
|
||||||
|
[onFiltersUpdate],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFilterItemUnSelect = useCallback(
|
||||||
|
(filterId: SelectedFilterType['id']) => {
|
||||||
|
const newFilters = [] as SelectedFilterType[];
|
||||||
|
innerSetFilters(newFilters);
|
||||||
|
onFiltersUpdate && onFiltersUpdate(newFilters);
|
||||||
|
},
|
||||||
|
[onFiltersUpdate],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledTableHeader>
|
<StyledTableHeader>
|
||||||
@ -89,18 +116,27 @@ function TableHeader<SortField extends string>({
|
|||||||
{viewName}
|
{viewName}
|
||||||
</StyledViewSection>
|
</StyledViewSection>
|
||||||
<StyledFilters>
|
<StyledFilters>
|
||||||
<DropdownButton label="Filter" isActive={false}></DropdownButton>
|
<FilterDropdownButton
|
||||||
|
filters={filters}
|
||||||
|
setFilters={setFilters}
|
||||||
|
availableFilters={availableFilters || []}
|
||||||
|
/>
|
||||||
<SortDropdownButton
|
<SortDropdownButton
|
||||||
setSorts={setSorts}
|
setSorts={setSorts}
|
||||||
sorts={sorts}
|
sorts={sorts}
|
||||||
sortsAvailable={sortsAvailable}
|
availableSorts={availableSorts || []}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DropdownButton label="Settings" isActive={false}></DropdownButton>
|
<DropdownButton label="Settings" isActive={false}></DropdownButton>
|
||||||
</StyledFilters>
|
</StyledFilters>
|
||||||
</StyledTableHeader>
|
</StyledTableHeader>
|
||||||
{sorts.length > 0 && (
|
{sorts.length + filters.length > 0 && (
|
||||||
<SortAndFilterBar sorts={sorts} onRemoveSort={onSortItemUnSelect} />
|
<SortAndFilterBar
|
||||||
|
sorts={sorts}
|
||||||
|
onRemoveSort={onSortItemUnSelect}
|
||||||
|
filters={filters}
|
||||||
|
onRemoveFilter={onFilterItemUnSelect}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
import { ThemeProvider } from '@emotion/react';
|
||||||
|
import { lightTheme } from '../../../../layout/styles/themes';
|
||||||
|
import { FilterDropdownButton } from '../FilterDropdownButton';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { FilterType, SelectedFilterType } from '../interface';
|
||||||
|
import {
|
||||||
|
faUser,
|
||||||
|
faBuildings,
|
||||||
|
faEnvelope,
|
||||||
|
faPhone,
|
||||||
|
faCalendar,
|
||||||
|
faMapPin,
|
||||||
|
} from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
const component = {
|
||||||
|
title: 'FilterDropdownButton',
|
||||||
|
component: FilterDropdownButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default component;
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
setFilters: (filters: SelectedFilterType[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const availableFilters = [
|
||||||
|
{
|
||||||
|
key: 'fullname',
|
||||||
|
label: 'People',
|
||||||
|
icon: faUser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'company_name',
|
||||||
|
label: 'Company',
|
||||||
|
icon: faBuildings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
icon: faEnvelope,
|
||||||
|
},
|
||||||
|
{ key: 'phone', label: 'Phone', icon: faPhone },
|
||||||
|
{
|
||||||
|
key: 'created_at',
|
||||||
|
label: 'Created at',
|
||||||
|
icon: faCalendar,
|
||||||
|
},
|
||||||
|
{ key: 'city', label: 'City', icon: faMapPin },
|
||||||
|
] satisfies FilterType[];
|
||||||
|
|
||||||
|
const StyleDiv = styled.div`
|
||||||
|
height: 200px;
|
||||||
|
width: 200px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RegularFilterDropdownButton = ({ setFilters }: OwnProps) => {
|
||||||
|
const [filters, innerSetFilters] = useState<SelectedFilterType[]>([]);
|
||||||
|
const outerSetFilters = useCallback(
|
||||||
|
(filters: SelectedFilterType[]) => {
|
||||||
|
innerSetFilters(filters);
|
||||||
|
setFilters(filters);
|
||||||
|
},
|
||||||
|
[setFilters],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
<StyleDiv>
|
||||||
|
<FilterDropdownButton
|
||||||
|
availableFilters={availableFilters}
|
||||||
|
filters={filters}
|
||||||
|
setFilters={outerSetFilters}
|
||||||
|
/>
|
||||||
|
</StyleDiv>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -22,17 +22,27 @@ export const RegularSortAndFilterBar = ({ removeFunction }: OwnProps) => {
|
|||||||
{
|
{
|
||||||
label: 'Test sort',
|
label: 'Test sort',
|
||||||
order: 'asc',
|
order: 'asc',
|
||||||
id: 'test_sort',
|
key: 'test_sort',
|
||||||
icon: faArrowDown,
|
icon: faArrowDown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Test sort 2',
|
label: 'Test sort 2',
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
id: 'test_sort_2',
|
key: 'test_sort_2',
|
||||||
icon: faArrowDown,
|
icon: faArrowDown,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onRemoveSort={removeFunction}
|
onRemoveSort={removeFunction}
|
||||||
|
onRemoveFilter={removeFunction}
|
||||||
|
filters={[
|
||||||
|
{
|
||||||
|
label: 'People',
|
||||||
|
operand: { id: 'include', label: 'Include' },
|
||||||
|
id: 'test_filter',
|
||||||
|
icon: faArrowDown,
|
||||||
|
value: 'John Doe',
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,8 +1,16 @@
|
|||||||
import { SelectedSortType, SortType } from '../SortAndFilterBar';
|
import { SelectedSortType, SortType } from '../interface';
|
||||||
import { ThemeProvider } from '@emotion/react';
|
import { ThemeProvider } from '@emotion/react';
|
||||||
import { lightTheme } from '../../../../layout/styles/themes';
|
import { lightTheme } from '../../../../layout/styles/themes';
|
||||||
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
|
import {
|
||||||
|
faBuildings,
|
||||||
|
faCalendar,
|
||||||
|
faEnvelope,
|
||||||
|
faMapPin,
|
||||||
|
faPhone,
|
||||||
|
faUser,
|
||||||
|
} from '@fortawesome/pro-regular-svg-icons';
|
||||||
import { SortDropdownButton } from '../SortDropdownButton';
|
import { SortDropdownButton } from '../SortDropdownButton';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
title: 'SortDropdownButton',
|
title: 'SortDropdownButton',
|
||||||
@ -19,20 +27,44 @@ const sorts = [] satisfies SelectedSortType[];
|
|||||||
|
|
||||||
const availableSorts = [
|
const availableSorts = [
|
||||||
{
|
{
|
||||||
label: 'Email',
|
key: 'fullname',
|
||||||
id: 'email',
|
label: 'People',
|
||||||
icon: faArrowDown,
|
icon: faUser,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'company_name',
|
||||||
|
label: 'Company',
|
||||||
|
icon: faBuildings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
icon: faEnvelope,
|
||||||
|
},
|
||||||
|
{ key: 'phone', label: 'Phone', icon: faPhone },
|
||||||
|
{
|
||||||
|
key: 'created_at',
|
||||||
|
label: 'Created at',
|
||||||
|
icon: faCalendar,
|
||||||
|
},
|
||||||
|
{ key: 'city', label: 'City', icon: faMapPin },
|
||||||
] satisfies SortType[];
|
] satisfies SortType[];
|
||||||
|
|
||||||
|
const StyleDiv = styled.div`
|
||||||
|
height: 200px;
|
||||||
|
width: 200px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const RegularSortDropdownButton = ({ setSorts }: OwnProps) => {
|
export const RegularSortDropdownButton = ({ setSorts }: OwnProps) => {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={lightTheme}>
|
<ThemeProvider theme={lightTheme}>
|
||||||
<SortDropdownButton
|
<StyleDiv>
|
||||||
sorts={sorts}
|
<SortDropdownButton
|
||||||
sortsAvailable={availableSorts}
|
sorts={sorts}
|
||||||
setSorts={setSorts}
|
availableSorts={availableSorts}
|
||||||
/>
|
setSorts={setSorts}
|
||||||
|
/>
|
||||||
|
</StyleDiv>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,7 @@ 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, faCalendar } from '@fortawesome/pro-regular-svg-icons';
|
import { faBuilding, faCalendar } from '@fortawesome/pro-regular-svg-icons';
|
||||||
import { SortType } from '../SortAndFilterBar';
|
import { SortType } from '../interface';
|
||||||
|
|
||||||
const component = {
|
const component = {
|
||||||
title: 'TableHeader',
|
title: 'TableHeader',
|
||||||
@ -12,9 +12,9 @@ const component = {
|
|||||||
export default component;
|
export default component;
|
||||||
|
|
||||||
export const RegularTableHeader = () => {
|
export const RegularTableHeader = () => {
|
||||||
const sortsAvailable: Array<SortType> = [
|
const availableSorts: Array<SortType> = [
|
||||||
{
|
{
|
||||||
id: 'created_at',
|
key: 'created_at',
|
||||||
label: 'Created at',
|
label: 'Created at',
|
||||||
icon: faCalendar,
|
icon: faCalendar,
|
||||||
},
|
},
|
||||||
@ -24,7 +24,7 @@ export const RegularTableHeader = () => {
|
|||||||
<TableHeader
|
<TableHeader
|
||||||
viewName="Test"
|
viewName="Test"
|
||||||
viewIcon={faBuilding}
|
viewIcon={faBuilding}
|
||||||
sortsAvailable={sortsAvailable}
|
availableSorts={availableSorts}
|
||||||
/>
|
/>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
||||||
|
import { RegularFilterDropdownButton } from '../__stories__/FilterDropdownButton.stories';
|
||||||
|
import { faEnvelope } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
|
it('Checks the default top option is Include', async () => {
|
||||||
|
const setSorts = jest.fn();
|
||||||
|
const { getByText } = render(
|
||||||
|
<RegularFilterDropdownButton setFilters={setSorts} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortDropdownButton = getByText('Filter');
|
||||||
|
fireEvent.click(sortDropdownButton);
|
||||||
|
|
||||||
|
const sortByEmail = getByText('Email');
|
||||||
|
fireEvent.click(sortByEmail);
|
||||||
|
|
||||||
|
const filterByJohn = getByText('John Doe');
|
||||||
|
fireEvent.click(filterByJohn);
|
||||||
|
|
||||||
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
|
{
|
||||||
|
id: 'John Doe',
|
||||||
|
value: 'John Doe',
|
||||||
|
label: 'Email',
|
||||||
|
operand: { id: 'include', label: 'Include' },
|
||||||
|
icon: faEnvelope,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Checks the selection of top option for Doesnot include', async () => {
|
||||||
|
const setSorts = jest.fn();
|
||||||
|
const { getByText } = render(
|
||||||
|
<RegularFilterDropdownButton setFilters={setSorts} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortDropdownButton = getByText('Filter');
|
||||||
|
fireEvent.click(sortDropdownButton);
|
||||||
|
|
||||||
|
const sortByEmail = getByText('Email');
|
||||||
|
fireEvent.click(sortByEmail);
|
||||||
|
|
||||||
|
const openOperandOptions = getByText('Include');
|
||||||
|
fireEvent.click(openOperandOptions);
|
||||||
|
|
||||||
|
const selectOperand = getByText("Doesn't include");
|
||||||
|
fireEvent.click(selectOperand);
|
||||||
|
|
||||||
|
const filterByJohn = getByText('John Doe');
|
||||||
|
fireEvent.click(filterByJohn);
|
||||||
|
|
||||||
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
|
{
|
||||||
|
id: 'John Doe',
|
||||||
|
value: 'John Doe',
|
||||||
|
label: 'Email',
|
||||||
|
operand: { id: 'not-include', label: "Doesn't include" },
|
||||||
|
icon: faEnvelope,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const blueSortDropdownButton = getByText('Filter');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(blueSortDropdownButton).toHaveAttribute('aria-selected', 'true');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { fireEvent, render } from '@testing-library/react';
|
import { fireEvent, render } from '@testing-library/react';
|
||||||
import { RegularSortDropdownButton } from '../__stories__/SortDropdownButton.stories';
|
import { RegularSortDropdownButton } from '../__stories__/SortDropdownButton.stories';
|
||||||
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
|
import { faEnvelope } from '@fortawesome/pro-regular-svg-icons';
|
||||||
|
|
||||||
it('Checks the default top option is Ascending', async () => {
|
it('Checks the default top option is Ascending', async () => {
|
||||||
const setSorts = jest.fn();
|
const setSorts = jest.fn();
|
||||||
@ -17,8 +17,8 @@ it('Checks the default top option is Ascending', async () => {
|
|||||||
expect(setSorts).toHaveBeenCalledWith([
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
{
|
{
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
id: 'email',
|
key: 'email',
|
||||||
icon: faArrowDown,
|
icon: faEnvelope,
|
||||||
order: 'asc',
|
order: 'asc',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -45,8 +45,8 @@ it('Checks the selection of Descending', async () => {
|
|||||||
expect(setSorts).toHaveBeenCalledWith([
|
expect(setSorts).toHaveBeenCalledWith([
|
||||||
{
|
{
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
id: 'email',
|
key: 'email',
|
||||||
icon: faArrowDown,
|
icon: faEnvelope,
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
25
front/src/components/table/table-header/interface.ts
Normal file
25
front/src/components/table/table-header/interface.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||||
|
|
||||||
|
export type SortType<SortKey = string> = {
|
||||||
|
label: string;
|
||||||
|
key: SortKey;
|
||||||
|
icon?: IconProp;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FilterType<FilterKey = string> = {
|
||||||
|
label: string;
|
||||||
|
key: FilterKey;
|
||||||
|
icon: IconProp;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SelectedFilterType = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
operand: { id: string; label: string };
|
||||||
|
icon: IconProp;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SelectedSortType<SortField = string> = SortType<SortField> & {
|
||||||
|
order: 'asc' | 'desc';
|
||||||
|
};
|
||||||
@ -2,7 +2,11 @@ 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, sortsAvailable } from './people-table';
|
import {
|
||||||
|
availableFilters,
|
||||||
|
peopleColumns,
|
||||||
|
availableSorts,
|
||||||
|
} 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 {
|
import {
|
||||||
@ -38,7 +42,8 @@ function People() {
|
|||||||
viewName="All People"
|
viewName="All People"
|
||||||
viewIcon={faList}
|
viewIcon={faList}
|
||||||
onSortsUpdate={updateSorts}
|
onSortsUpdate={updateSorts}
|
||||||
sortsAvailable={sortsAvailable}
|
availableSorts={availableSorts}
|
||||||
|
availableFilters={availableFilters}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</StyledPeopleContainer>
|
</StyledPeopleContainer>
|
||||||
|
|||||||
@ -17,35 +17,63 @@ 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 { 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 EditableCell from '../../components/table/EditableCell';
|
import EditableCell from '../../components/table/EditableCell';
|
||||||
import { OrderByFields, updatePerson } from '../../services/people';
|
import { OrderByFields, updatePerson } from '../../services/people';
|
||||||
|
import {
|
||||||
|
FilterType,
|
||||||
|
SortType,
|
||||||
|
} from '../../components/table/table-header/interface';
|
||||||
|
|
||||||
export const sortsAvailable = [
|
export const availableSorts = [
|
||||||
{
|
{
|
||||||
id: 'fullname',
|
key: 'fullname',
|
||||||
label: 'People',
|
label: 'People',
|
||||||
icon: faUser,
|
icon: faUser,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'company_name',
|
key: 'company_name',
|
||||||
label: 'Company',
|
label: 'Company',
|
||||||
icon: faBuildings,
|
icon: faBuildings,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'email',
|
key: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
icon: faEnvelope,
|
icon: faEnvelope,
|
||||||
},
|
},
|
||||||
{ id: 'phone', label: 'Phone', icon: faPhone },
|
{ key: 'phone', label: 'Phone', icon: faPhone },
|
||||||
{
|
{
|
||||||
id: 'created_at',
|
key: 'created_at',
|
||||||
label: 'Created at',
|
label: 'Created at',
|
||||||
icon: faCalendar,
|
icon: faCalendar,
|
||||||
},
|
},
|
||||||
{ id: 'city', label: 'City', icon: faMapPin },
|
{ key: 'city', label: 'City', icon: faMapPin },
|
||||||
] satisfies Array<SortType<OrderByFields>>;
|
] satisfies Array<SortType<OrderByFields>>;
|
||||||
|
|
||||||
|
export const availableFilters = [
|
||||||
|
{
|
||||||
|
key: 'fullname',
|
||||||
|
label: 'People',
|
||||||
|
icon: faUser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'company_name',
|
||||||
|
label: 'Company',
|
||||||
|
icon: faBuildings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
icon: faEnvelope,
|
||||||
|
},
|
||||||
|
{ key: 'phone', label: 'Phone', icon: faPhone },
|
||||||
|
{
|
||||||
|
key: 'created_at',
|
||||||
|
label: 'Created at',
|
||||||
|
icon: faCalendar,
|
||||||
|
},
|
||||||
|
{ key: 'city', label: 'City', icon: faMapPin },
|
||||||
|
] satisfies FilterType[];
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<Person>();
|
const columnHelper = createColumnHelper<Person>();
|
||||||
export const peopleColumns = [
|
export const peopleColumns = [
|
||||||
columnHelper.accessor('fullName', {
|
columnHelper.accessor('fullName', {
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import { PeopleSelectedSortType, reduceSortsToOrderBy } 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 = [
|
||||||
{ id: 'firstname', label: 'firstname', order: 'asc' },
|
{ key: 'firstname', label: 'firstname', order: 'asc' },
|
||||||
{ id: 'lastname', label: 'lastname', order: 'desc' },
|
{ key: 'lastname', label: 'lastname', order: 'desc' },
|
||||||
] 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' }]);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
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';
|
|
||||||
import { Order_By, People_Order_By } from '../../generated/graphql';
|
import { Order_By, People_Order_By } from '../../generated/graphql';
|
||||||
|
import { SelectedSortType } from '../../components/table/table-header/interface';
|
||||||
|
|
||||||
export type OrderByFields = keyof People_Order_By | 'fullname' | 'company_name';
|
export type OrderByFields = keyof People_Order_By | 'fullname' | 'company_name';
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ export const reduceSortsToOrderBy = (
|
|||||||
sorts: Array<PeopleSelectedSortType>,
|
sorts: Array<PeopleSelectedSortType>,
|
||||||
): People_Order_By[] => {
|
): People_Order_By[] => {
|
||||||
const mappedSorts = sorts.reduce((acc, sort) => {
|
const mappedSorts = sorts.reduce((acc, sort) => {
|
||||||
const id = sort.id;
|
const id = sort.key;
|
||||||
const order = mapOrder(sort.order);
|
const order = mapOrder(sort.order);
|
||||||
if (id === 'fullname') {
|
if (id === 'fullname') {
|
||||||
acc['firstname'] = order;
|
acc['firstname'] = order;
|
||||||
|
|||||||
Reference in New Issue
Block a user