Skip to content

Commit

Permalink
Configure filtering to support multiselect
Browse files Browse the repository at this point in the history
  • Loading branch information
norbye committed Jan 27, 2024
1 parent 7117e5c commit 7c7067e
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 48 deletions.
8 changes: 4 additions & 4 deletions app/components/Table/BodyCell.tsx
@@ -1,16 +1,16 @@
import { get } from 'lodash';
import type { ColumnProps, ShowColumn } from '.';
import type { ColumnProps, ShowColumn, TableData } from '.';

type CellProps = {
column: ColumnProps;
data: Record<string, any>;
data: TableData;
index: number;
showColumn?: ShowColumn;
showColumn: ShowColumn;
};

const BodyCell: React.FC<CellProps> = ({ column, data, index, showColumn }) => {
if (column.columnChoices) {
if (!showColumn) {
if (Object.keys(showColumn).length === 0) {
return null;
}
const columnIndex: number = showColumn[column.dataIndex];
Expand Down
72 changes: 49 additions & 23 deletions app/components/Table/HeadCell.tsx
Expand Up @@ -2,7 +2,7 @@ import { Button, Flex, Icon } from '@webkom/lego-bricks';
import cx from 'classnames';
import { useEffect, useRef } from 'react';
import Dropdown from 'app/components/Dropdown';
import { TextInput, RadioButton } from 'app/components/Form';
import { TextInput, RadioButton, CheckBox } from 'app/components/Form';
import styles from './Table.css';
import type { ColumnProps, Filters, IsShown, ShowColumn, Sort } from '.';

Expand Down Expand Up @@ -43,8 +43,26 @@ const HeadCell: React.FC<HeadCellProps> = ({
});
};

const changeFilter = (filterIndex: string, value: string) =>
setFilters({ ...filters, [filterIndex]: value });
const toggleFilter = (filterIndex: string, value: string) =>
setFilters((prevFilters) => {
const filter = prevFilters[filterIndex];
const updatedFilters = { ...prevFilters };
if (filter === undefined) {
updatedFilters[filterIndex] = [value];
return updatedFilters;
}
if (!column.filterOptions?.multiSelect) {
return filter[0] === value
? { ...filters, [filterIndex]: undefined }
: { ...filters, [filterIndex]: [value] };
}
console.log(updatedFilters);
updatedFilters[filterIndex] = filter.includes(value)
? filter.filter((filter) => filter !== value)
: [...filter, value];
console.log(updatedFilters);
return updatedFilters;
});

const toggleIsShown = (filterIndex: string) =>
setIsShown({
Expand Down Expand Up @@ -72,6 +90,7 @@ const HeadCell: React.FC<HeadCellProps> = ({
title,
sorter,
filter,
filterOptions,
search,
filterMessage,
} = chosenProps;
Expand Down Expand Up @@ -108,7 +127,7 @@ const HeadCell: React.FC<HeadCellProps> = ({
name="search"
size={16}
className={cx(
(filters[filterIndex] && filters[filterIndex].length) ||
(filters[filterIndex] && filters[filterIndex]?.length) ||
isShown[filterIndex]
? styles.iconActive
: styles.icon
Expand All @@ -123,7 +142,7 @@ const HeadCell: React.FC<HeadCellProps> = ({
removeBorder
placeholder={filterMessage}
value={filters[filterIndex]}
onChange={(e) => changeFilter(filterIndex, e.target.value)}
onChange={(e) => toggleFilter(filterIndex, e.target.value)}
onKeyDown={({ keyCode }) => {
if (keyCode === 13) {
toggleIsShown(filterIndex);
Expand All @@ -141,7 +160,8 @@ const HeadCell: React.FC<HeadCellProps> = ({
name="funnel"
size={16}
className={cx(
filters[filterIndex] !== undefined || isShown[filterIndex]
(filters[filterIndex] ?? []).length > 0 ||
isShown[filterIndex]
? styles.iconActive
: styles.icon
)}
Expand All @@ -150,22 +170,29 @@ const HeadCell: React.FC<HeadCellProps> = ({
contentClassName={styles.checkbox}
rootClose
>
{filter.map(({ label, value }) => (
<div key={label}>
<RadioButton
id={filterIndex + value}
name={filterIndex}
label={label}
checked={value === filters[filterIndex]}
onChange={() =>
changeFilter(
filterIndex,
filters[filterIndex] === value ? undefined : value
)
}
/>
</div>
))}
{filter.map(({ label, value }) =>
filterOptions?.multiSelect ? (
<div
key={label}
onClick={() => toggleFilter(filterIndex, value)}
>
<CheckBox
label={label}
checked={filters[filterIndex]?.includes(value)}
/>
</div>
) : (
<div key={label}>
<RadioButton
id={filterIndex + value}
name={filterIndex}
label={label}
checked={filters[filterIndex]?.includes(value)}
onClick={() => toggleFilter(filterIndex, value)}
/>
</div>
)
)}
<Button
flat
onClick={() => {
Expand Down Expand Up @@ -205,7 +232,6 @@ const HeadCell: React.FC<HeadCellProps> = ({
<RadioButton
id={dataIndexColumnChoices + index}
name={dataIndexColumnChoices}
inputValue={showColumn[dataIndexColumnChoices]}
value={index}
label={title}
checked={showColumn[dataIndexColumnChoices] === index}
Expand Down
68 changes: 47 additions & 21 deletions app/components/Table/index.tsx
Expand Up @@ -6,16 +6,20 @@ import InfiniteScroll from 'react-infinite-scroller';
import BodyCell from './BodyCell';
import HeadCell from './HeadCell';
import styles from './Table.css';
import type { ID } from 'app/store/models';
import type { ReactNode } from 'react';

export type Sort = {
direction?: 'asc' | 'desc';
dataIndex?: string;
sorter?: boolean | ((arg0: any, arg1: any) => number);
};
export type Filters = Record<string, any>;
export type IsShown = Record<string, any>;
export type ShowColumn = Record<string, any>;
export type Filters = Record<string, string[] | undefined>;
type QueryFilters = Record<string, string | undefined>;
export type IsShown = Record<string, boolean>;
export type ShowColumn = Record<string, number>;

export type TableData = object & { id: ID };

type CheckFilter = {
label: string;
Expand All @@ -28,6 +32,9 @@ export type ColumnProps = {
title?: string;
sorter?: boolean | ((arg0: any, arg1: any) => number);
filter?: Array<CheckFilter>;
filterOptions?: {
multiSelect?: boolean;
};

/*
* Map the value to to "another" value to use
Expand All @@ -44,23 +51,43 @@ export type ColumnProps = {
centered?: boolean;
inlineFiltering?: boolean;
filterMessage?: string;
columnChoices?: Array<ColumnProps>;
columnChoices?: ColumnProps[];
};

type TableProps = {
rowKey?: string;
columns: Array<ColumnProps>;
data: Array<Record<string, any>>;
columns: ColumnProps[];
data: TableData[];
hasMore: boolean;
loading: boolean;
onChange?: (filters: Record<string, any>, sort: Sort) => void;
onLoad?: (filters: Record<string, any>, sort: Sort) => void;
filters?: Record<string, any>;
onChange?: (queryFilters: QueryFilters, querySort: Sort) => void;
onLoad?: (queryFilters: QueryFilters, querySort: Sort) => void;
filters?: QueryFilters;
className?: string;
};

const isVisible = ({ visible = true }: ColumnProps) => visible;

const filtersToQueryFilters: (filters: Filters) => QueryFilters = (filters) => {
const queryFilters: QueryFilters = {};
Object.entries(filters).forEach(
([key, filter]) =>
(queryFilters[key] = filter?.length ? filter?.join(',') : undefined)
);
return queryFilters;
};

const queryFiltersToFilters: (queryFilters?: QueryFilters) => Filters = (
queryFilters
) => {
if (!queryFilters) return {};
const filters: Filters = {};
Object.entries(queryFilters).forEach(
([key, queryFilter]) => (filters[key] = queryFilter?.split(','))
);
return filters;
};

const Table: React.FC<TableProps> = ({
columns,
data,
Expand All @@ -73,9 +100,11 @@ const Table: React.FC<TableProps> = ({
...props
}) => {
const [sort, setSort] = useState<Sort>({});
const [filters, setFilters] = useState<Filters>(props.filters || {});
const [filters, setFilters] = useState<Filters>(
queryFiltersToFilters(props.filters)
);
const [isShown, setIsShown] = useState<IsShown>({});
const [showColumn, setShowColumn] = useState<ShowColumn>();
const [showColumn, setShowColumn] = useState<ShowColumn>({});

useEffect(() => {
const initialShowColumn = {};
Expand All @@ -90,7 +119,7 @@ const Table: React.FC<TableProps> = ({
useEffect(() => {
debounce(() => {
if (onChange) {
onChange(filters, sort);
onChange(filtersToQueryFilters(filters), sort);
}
}, 170)();
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -111,7 +140,7 @@ const Table: React.FC<TableProps> = ({
return sortedData;
}, [sort, data]);

const filter = (item: Record<string, any>) => {
const filter: (item: Record<string, any>) => boolean = (item) => {
if (isEmpty(filters)) {
return true;
}
Expand All @@ -134,20 +163,17 @@ const Table: React.FC<TableProps> = ({
return filters[key] === get(item, key);
}

const filter = filters[key].toLowerCase();

if (!filter.length) {
return true;
}

return filterMapping(get(item, dataIndex)).toLowerCase().includes(filter);
const arrayFilters = filters[key] as string[];
return arrayFilters.some((arrayFilter) =>
filterMapping(get(item, dataIndex)).toLowerCase().includes(arrayFilter)
);
}).length;
return match > 0;
};

const loadMore = () => {
if (onLoad && !loading) {
onLoad(filters, sort);
onLoad(filtersToQueryFilters(filters), sort);
}
};

Expand Down
3 changes: 3 additions & 0 deletions app/routes/admin/groups/components/GroupMembersList.tsx
Expand Up @@ -156,6 +156,9 @@ const GroupMembersList = ({
title: 'Rolle',
dataIndex: 'role',
filter: roleOptions,
filterOptions: {
multiSelect: true,
},
search: false,
inlineFiltering: false,
filterMapping: (role: RoleType) =>
Expand Down

0 comments on commit 7c7067e

Please sign in to comment.