Skip to content

Commit

Permalink
Add table filters
Browse files Browse the repository at this point in the history
  • Loading branch information
vesnushka committed Dec 18, 2024
1 parent 2499307 commit 083ad27
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 7 deletions.
8 changes: 7 additions & 1 deletion src/components/SearchBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import { Button, Row } from 'antd';
import { S } from './SearchBar.styles';
import { SearchBarColumn } from './SearchBarColumn';
import { SearchBarData } from './types';
import { isSearchBarFilter } from './utils';
import { useMemo } from 'react';

export function SearchBar(props: SearchBarData) {
const { columnsFilterValues, onChangeColumnFilter, onResetFilters } = props;
const searchBarFilterValues = useMemo(
() => columnsFilterValues.filter((filter) => isSearchBarFilter(filter)),
[JSON.stringify(columnsFilterValues)],
);

return (
<S.Container>
<Row gutter={[32, 16]}>
{columnsFilterValues.map((columnFilterValue) => (
{searchBarFilterValues.map((columnFilterValue) => (
<SearchBarColumn
key={`search-bar-column-${columnFilterValue.column.id}`}
columnFilterValue={columnFilterValue}
Expand Down
3 changes: 3 additions & 0 deletions src/components/SearchBar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ export interface SearchBarProps {
}

type SearchBarColumnBase = {
// if placement is table then id should be matched with table column key
id: string;
searchParam?: string;
// placement = 'search-bar' by default
placement?: Array<'search-bar' | 'table'>;
};

export type SearchBarStringColumn = SearchBarColumnBase & {
Expand Down
12 changes: 12 additions & 0 deletions src/components/SearchBar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,15 @@ export function getSearchBarColumnFilterValue(filterValue: ColumnFilterValue) {

throw new Error('Unsupported column type');
}

export function isSearchBarFilter(filter: ColumnFilterValue) {
const { placement = ['search-bar'] } = filter.column;

return placement.includes('search-bar');
}

export function isTableFilter(filter: ColumnFilterValue) {
const { placement = ['search-bar'] } = filter.column;

return placement.includes('table');
}
25 changes: 25 additions & 0 deletions src/components/Table/TableFilter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ColumnFilterValue } from 'src/components/SearchBar/types';
import { SearchBarColumn } from 'src/components/SearchBar/SearchBarColumn';
import { S } from './styles';
import { FilterDropdownProps } from 'antd/lib/table/interface';

interface Props extends FilterDropdownProps {
filter: ColumnFilterValue;
onChange: (value: ColumnFilterValue['value'], key: string) => void;
}

export function TableFilter(props: Props) {
const { filter, onChange, close } = props;

return (
<S.Filter>
<SearchBarColumn
columnFilterValue={filter}
onChange={(value, key) => {
onChange(value, key);
close();
}}
/>
</S.Filter>
);
}
38 changes: 38 additions & 0 deletions src/components/Table/TableFilter/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled from 'styled-components';

export const S = {
Filter: styled.div`
display: flex;
flex-direction: column;
padding: 8px;
border-radius: 8px;
box-shadow:
0px 6px 16px 0px ${({ theme }) => theme.neutral.dividers},
0px 3px 6px -4px ${({ theme }) => theme.neutral.border},
0px 9px 28px 8px ${({ theme }) => theme.neutral.background};
min-width: 240px;
.ant-input-search,
.ant-picker,
.react-select__control {
width: 100%;
}
.react-select__menu {
position: static;
}
.react-select__menu {
box-shadow: none;
padding: 8px 4px 4px;
margin: 8px -8px -8px;
border-top: 1px solid ${({ theme }) => theme.neutral.dividers};
border-radius: 0;
width: auto;
}
.react-select__menu-list {
padding: 0;
}
`,
};
35 changes: 35 additions & 0 deletions src/components/Table/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Bundle, Resource } from 'fhir/r4b';
import { ColumnsType, ColumnType } from 'antd/lib/table';
import { ColumnFilterValue } from 'src/components/SearchBar/types';
import { TableFilter } from './TableFilter';
import { FilterDropdownProps } from 'antd/lib/table/interface';
import _ from 'lodash';

export type RecordType<R extends Resource> = { resource: R; bundle: Bundle };

interface Props<R extends Resource> {
tableColumns: ColumnsType<RecordType<R>>;
filters: ColumnFilterValue[];
onChange: (value: ColumnFilterValue['value'], key: string) => void;
}

export function populateTableColumnsWithFiltersAndSorts<R extends Resource>(
props: Props<R>,
): ColumnsType<RecordType<R>> {
const { tableColumns, filters, onChange } = props;
const result = tableColumns.map((column) => {
const filter = filters.find((f) => f.column.id === column.key);

const updatedColumn: ColumnType<RecordType<R>> = {
...column,
filtered: !_.isUndefined(filter?.value) && !_.isNull(filter?.value) && filter?.value === '',
filterDropdown: filter
? (props: FilterDropdownProps) => <TableFilter {...props} filter={filter} onChange={onChange} />
: undefined,
};

return updatedColumn;
});

return result;
}
2 changes: 2 additions & 0 deletions src/containers/PatientResourceListExample/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function PatientResourceListExample() {
dataIndex: 'name',
key: 'name',
render: (_text, { resource }) => renderHumanName(resource.name?.[0]),
width: 250,
},
{
title: <Trans>Birth date</Trans>,
Expand All @@ -65,6 +66,7 @@ export function PatientResourceListExample() {
searchParam: 'name',
type: SearchBarColumnType.STRING,
placeholder: t`Find patient`,
placement: ['search-bar'],
},
]}
getRecordActions={(record) => [
Expand Down
25 changes: 19 additions & 6 deletions src/uberComponents/ResourceListPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import {
export { navigationAction, customAction, questionnaireAction } from './actions';
import { useResourceListPage } from './hooks';
import { SearchBarColumn } from '../../components/SearchBar/types';
import { populateTableColumnsWithFiltersAndSorts } from 'src/components/Table/utils';
import { isTableFilter } from 'src/components/SearchBar/utils';
import { useMemo } from 'react';

type RecordType<R extends Resource> = { resource: R; bundle: Bundle };

Expand Down Expand Up @@ -102,12 +105,15 @@ export function ResourceListPage<R extends Resource>({
defaultLaunchContext,
}: ResourceListPageProps<R>) {
const allFilters = getFilters?.() ?? [];
// TODO: filter out column filters
const searchBarColumns = allFilters;

const { columnsFilterValues, onChangeColumnFilter, onResetFilters } = useSearchBar({
columns: searchBarColumns ?? [],
});
const { columnsFilterValues, onChangeColumnFilter, onResetFilters } =
useSearchBar({
columns: allFilters ?? [],
});
const tableFilterValues = useMemo(
() => columnsFilterValues.filter((filter) => isTableFilter(filter)),
[JSON.stringify(columnsFilterValues)],
);

const {
recordResponse,
Expand All @@ -119,6 +125,13 @@ export function ResourceListPage<R extends Resource>({
selectedResourcesBundle,
} = useResourceListPage(resourceType, extractPrimaryResources, columnsFilterValues, searchParams ?? {});

// TODO: move to hooks
const initialTableColumns = getTableColumns({ reload });
const tableColumns = populateTableColumnsWithFiltersAndSorts({
tableColumns: initialTableColumns,
filters: tableFilterValues,
onChange: onChangeColumnFilter,
});
const headerActions = getHeaderActions?.() ?? [];
const batchActions = getBatchActions?.() ?? [];

Expand Down Expand Up @@ -203,7 +216,7 @@ export function ResourceListPage<R extends Resource>({
rowKey={(p) => p.resource.id!}
dataSource={isSuccess(recordResponse) ? recordResponse.data : []}
columns={[
...getTableColumns({ reload }),
...tableColumns,
...(getRecordActions
? [
getRecordActionsColumn({
Expand Down

0 comments on commit 083ad27

Please sign in to comment.