mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	refactor: filter abstraction (#5625)
This commit is contained in:
		
							parent
							
								
									17b747ea8f
								
							
						
					
					
						commit
						ed4a182e7e
					
				@ -1,6 +1,6 @@
 | 
				
			|||||||
import { screen } from '@testing-library/react';
 | 
					import { screen } from '@testing-library/react';
 | 
				
			||||||
import { render } from 'utils/testRenderer';
 | 
					import { render } from 'utils/testRenderer';
 | 
				
			||||||
import { FilterItemParams } from '../FilterItem/FilterItem';
 | 
					import { FilterItemParams } from 'component/filter/FilterItem/FilterItem';
 | 
				
			||||||
import { FilterDateItem, IFilterDateItemProps } from './FilterDateItem';
 | 
					import { FilterDateItem, IFilterDateItemProps } from './FilterDateItem';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getDate = (option: string) => screen.getByText(option);
 | 
					const getDate = (option: string) => screen.getByText(option);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
import { Box } from '@mui/material';
 | 
					import { Box } from '@mui/material';
 | 
				
			||||||
import React, { FC, useEffect, useRef, useState } from 'react';
 | 
					import React, { FC, useEffect, useRef, useState } from 'react';
 | 
				
			||||||
import { StyledPopover } from '../FilterItem/FilterItem.styles';
 | 
					import { StyledPopover } from 'component/filter/FilterItem/FilterItem.styles';
 | 
				
			||||||
import { FilterItemChip } from '../FilterItem/FilterItemChip/FilterItemChip';
 | 
					import { FilterItemChip } from 'component/filter/FilterItem/FilterItemChip/FilterItemChip';
 | 
				
			||||||
import { DateCalendar, LocalizationProvider } from '@mui/x-date-pickers';
 | 
					import { DateCalendar, LocalizationProvider } from '@mui/x-date-pickers';
 | 
				
			||||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
 | 
					import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
 | 
				
			||||||
import { format } from 'date-fns';
 | 
					import { format } from 'date-fns';
 | 
				
			||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
 | 
					import { useLocationSettings } from 'hooks/useLocationSettings';
 | 
				
			||||||
import { getLocalizedDateString } from '../util';
 | 
					import { getLocalizedDateString } from '../util';
 | 
				
			||||||
import { FilterItemParams } from '../FilterItem/FilterItem';
 | 
					import { FilterItemParams } from 'component/filter/FilterItem/FilterItem';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IFilterDateItemProps {
 | 
					export interface IFilterDateItemProps {
 | 
				
			||||||
    label: string;
 | 
					    label: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,56 +1,18 @@
 | 
				
			|||||||
import { useEffect, useState, VFC } from 'react';
 | 
					import { useEffect, useState, VFC } from 'react';
 | 
				
			||||||
import { Box, styled } from '@mui/material';
 | 
					 | 
				
			||||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
 | 
					import useProjects from 'hooks/api/getters/useProjects/useProjects';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					 | 
				
			||||||
import AddFilterButton from './AddFilterButton/AddFilterButton';
 | 
					 | 
				
			||||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
 | 
					import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
 | 
				
			||||||
import { FilterDateItem } from 'component/common/FilterDateItem/FilterDateItem';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    FilterItem,
 | 
					 | 
				
			||||||
    FilterItemParams,
 | 
					 | 
				
			||||||
} from 'component/common/FilterItem/FilterItem';
 | 
					 | 
				
			||||||
import useAllTags from 'hooks/api/getters/useAllTags/useAllTags';
 | 
					import useAllTags from 'hooks/api/getters/useAllTags/useAllTags';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
const StyledBox = styled(Box)(({ theme }) => ({
 | 
					    FilterItemParamHolder,
 | 
				
			||||||
    display: 'flex',
 | 
					    Filters,
 | 
				
			||||||
    padding: theme.spacing(2, 3),
 | 
					    IFilterItem,
 | 
				
			||||||
    gap: theme.spacing(1),
 | 
					} from 'component/filter/Filters';
 | 
				
			||||||
    flexWrap: 'wrap',
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type FeatureTogglesListFilters = {
 | 
					 | 
				
			||||||
    project?: FilterItemParams | null | undefined;
 | 
					 | 
				
			||||||
    tag?: FilterItemParams | null | undefined;
 | 
					 | 
				
			||||||
    state?: FilterItemParams | null | undefined;
 | 
					 | 
				
			||||||
    segment?: FilterItemParams | null | undefined;
 | 
					 | 
				
			||||||
    createdAt?: FilterItemParams | null | undefined;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IFeatureToggleFiltersProps {
 | 
					interface IFeatureToggleFiltersProps {
 | 
				
			||||||
    state: FeatureTogglesListFilters;
 | 
					    state: FilterItemParamHolder;
 | 
				
			||||||
    onChange: (value: FeatureTogglesListFilters) => void;
 | 
					    onChange: (value: FilterItemParamHolder) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IBaseFilterItem = {
 | 
					 | 
				
			||||||
    label: string;
 | 
					 | 
				
			||||||
    options: {
 | 
					 | 
				
			||||||
        label: string;
 | 
					 | 
				
			||||||
        value: string;
 | 
					 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
    filterKey: keyof FeatureTogglesListFilters;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ITextFilterItem = IBaseFilterItem & {
 | 
					 | 
				
			||||||
    singularOperators: [string, ...string[]];
 | 
					 | 
				
			||||||
    pluralOperators: [string, ...string[]];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type IDateFilterItem = IBaseFilterItem & {
 | 
					 | 
				
			||||||
    dateOperators: [string, ...string[]];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type IFilterItem = ITextFilterItem | IDateFilterItem;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
 | 
					export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
 | 
				
			||||||
    state,
 | 
					    state,
 | 
				
			||||||
    onChange,
 | 
					    onChange,
 | 
				
			||||||
@ -71,31 +33,6 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
 | 
				
			|||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
 | 
					    const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
 | 
				
			||||||
    const [unselectedFilters, setUnselectedFilters] = useState<string[]>([]);
 | 
					 | 
				
			||||||
    const [selectedFilters, setSelectedFilters] = useState<string[]>([]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const deselectFilter = (label: string) => {
 | 
					 | 
				
			||||||
        const newSelectedFilters = selectedFilters.filter((f) => f !== label);
 | 
					 | 
				
			||||||
        const newUnselectedFilters = [...unselectedFilters, label].sort();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        setSelectedFilters(newSelectedFilters);
 | 
					 | 
				
			||||||
        setUnselectedFilters(newUnselectedFilters);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const mergeArraysKeepingOrder = (
 | 
					 | 
				
			||||||
        firstArray: string[],
 | 
					 | 
				
			||||||
        secondArray: string[],
 | 
					 | 
				
			||||||
    ): string[] => {
 | 
					 | 
				
			||||||
        const elementsSet = new Set(firstArray);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        secondArray.forEach((element) => {
 | 
					 | 
				
			||||||
            if (!elementsSet.has(element)) {
 | 
					 | 
				
			||||||
                firstArray.push(element);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return firstArray;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        const projectsOptions = (projects || []).map((project) => ({
 | 
					        const projectsOptions = (projects || []).map((project) => ({
 | 
				
			||||||
@ -171,81 +108,11 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
 | 
				
			|||||||
        JSON.stringify(tags),
 | 
					        JSON.stringify(tags),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
        const newSelectedFilters = availableFilters
 | 
					 | 
				
			||||||
            .filter((field) =>
 | 
					 | 
				
			||||||
                Boolean(
 | 
					 | 
				
			||||||
                    state[field.filterKey as keyof FeatureTogglesListFilters],
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .map((field) => field.label);
 | 
					 | 
				
			||||||
        const newUnselectedFilters = availableFilters
 | 
					 | 
				
			||||||
            .filter(
 | 
					 | 
				
			||||||
                (field) =>
 | 
					 | 
				
			||||||
                    !state[field.filterKey as keyof FeatureTogglesListFilters],
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .map((field) => field.label)
 | 
					 | 
				
			||||||
            .sort();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        setSelectedFilters(
 | 
					 | 
				
			||||||
            mergeArraysKeepingOrder(selectedFilters, newSelectedFilters),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        setUnselectedFilters(newUnselectedFilters);
 | 
					 | 
				
			||||||
    }, [JSON.stringify(state), JSON.stringify(availableFilters)]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const hasAvailableFilters = unselectedFilters.length > 0;
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <StyledBox>
 | 
					        <Filters
 | 
				
			||||||
            {selectedFilters.map((selectedFilter) => {
 | 
					            availableFilters={availableFilters}
 | 
				
			||||||
                const filter = availableFilters.find(
 | 
					            state={state}
 | 
				
			||||||
                    (filter) => filter.label === selectedFilter,
 | 
					            onChange={onChange}
 | 
				
			||||||
                );
 | 
					        />
 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (!filter) {
 | 
					 | 
				
			||||||
                    return null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ('dateOperators' in filter) {
 | 
					 | 
				
			||||||
                    return (
 | 
					 | 
				
			||||||
                        <FilterDateItem
 | 
					 | 
				
			||||||
                            label={filter.label}
 | 
					 | 
				
			||||||
                            state={state[filter.filterKey]}
 | 
					 | 
				
			||||||
                            onChange={(value) =>
 | 
					 | 
				
			||||||
                                onChange({ [filter.filterKey]: value })
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            operators={filter.dateOperators}
 | 
					 | 
				
			||||||
                            onChipClose={() => deselectFilter(filter.label)}
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return (
 | 
					 | 
				
			||||||
                    <FilterItem
 | 
					 | 
				
			||||||
                        key={filter.label}
 | 
					 | 
				
			||||||
                        label={filter.label}
 | 
					 | 
				
			||||||
                        state={state[filter.filterKey]}
 | 
					 | 
				
			||||||
                        options={filter.options}
 | 
					 | 
				
			||||||
                        onChange={(value) =>
 | 
					 | 
				
			||||||
                            onChange({ [filter.filterKey]: value })
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        singularOperators={filter.singularOperators}
 | 
					 | 
				
			||||||
                        pluralOperators={filter.pluralOperators}
 | 
					 | 
				
			||||||
                        onChipClose={() => deselectFilter(filter.label)}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            })}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ConditionallyRender
 | 
					 | 
				
			||||||
                condition={hasAvailableFilters}
 | 
					 | 
				
			||||||
                show={
 | 
					 | 
				
			||||||
                    <AddFilterButton
 | 
					 | 
				
			||||||
                        visibleOptions={unselectedFilters}
 | 
					 | 
				
			||||||
                        setVisibleOptions={setUnselectedFilters}
 | 
					 | 
				
			||||||
                        hiddenOptions={selectedFilters}
 | 
					 | 
				
			||||||
                        setHiddenOptions={setSelectedFilters}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </StyledBox>
 | 
					 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -93,6 +93,14 @@ export const FeatureToggleListTable: VFC = () => {
 | 
				
			|||||||
        stateConfig,
 | 
					        stateConfig,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const filterState = {
 | 
				
			||||||
 | 
					        project: tableState.project,
 | 
				
			||||||
 | 
					        tag: tableState.tag,
 | 
				
			||||||
 | 
					        state: tableState.state,
 | 
				
			||||||
 | 
					        segment: tableState.segment,
 | 
				
			||||||
 | 
					        createdAt: tableState.createdAt,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
        features = [],
 | 
					        features = [],
 | 
				
			||||||
        total,
 | 
					        total,
 | 
				
			||||||
@ -327,7 +335,10 @@ export const FeatureToggleListTable: VFC = () => {
 | 
				
			|||||||
                </PageHeader>
 | 
					                </PageHeader>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <FeatureToggleFilters onChange={setTableState} state={tableState} />
 | 
					            <FeatureToggleFilters
 | 
				
			||||||
 | 
					                onChange={setTableState}
 | 
				
			||||||
 | 
					                state={filterState}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
            <SearchHighlightProvider value={tableState.query || ''}>
 | 
					            <SearchHighlightProvider value={tableState.query || ''}>
 | 
				
			||||||
                <PaginatedTable tableInstance={table} totalItems={total} />
 | 
					                <PaginatedTable tableInstance={table} totalItems={total} />
 | 
				
			||||||
            </SearchHighlightProvider>
 | 
					            </SearchHighlightProvider>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { ComponentProps, FC } from 'react';
 | 
					import { ComponentProps, FC } from 'react';
 | 
				
			||||||
import { ArrowDropDown, Close, TopicOutlined } from '@mui/icons-material';
 | 
					import { ArrowDropDown, Close, TopicOutlined } from '@mui/icons-material';
 | 
				
			||||||
import { ConditionallyRender } from '../../ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { Chip, IconButton, styled } from '@mui/material';
 | 
					import { Chip, IconButton, styled } from '@mui/material';
 | 
				
			||||||
import { FilterItemOperator } from './FilterItemOperator/FilterItemOperator';
 | 
					import { FilterItemOperator } from './FilterItemOperator/FilterItemOperator';
 | 
				
			||||||
import { FILTER_ITEM } from 'utils/testIds';
 | 
					import { FILTER_ITEM } from 'utils/testIds';
 | 
				
			||||||
							
								
								
									
										147
									
								
								frontend/src/component/filter/Filters.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								frontend/src/component/filter/Filters.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,147 @@
 | 
				
			|||||||
 | 
					import { useEffect, useState, VFC } from 'react';
 | 
				
			||||||
 | 
					import { Box, styled } from '@mui/material';
 | 
				
			||||||
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
 | 
					import AddFilterButton from './AddFilterButton';
 | 
				
			||||||
 | 
					import { FilterDateItem } from 'component/common/FilterDateItem/FilterDateItem';
 | 
				
			||||||
 | 
					import { FilterItem, FilterItemParams } from './FilterItem/FilterItem';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledBox = styled(Box)(({ theme }) => ({
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    padding: theme.spacing(2, 3),
 | 
				
			||||||
 | 
					    gap: theme.spacing(1),
 | 
				
			||||||
 | 
					    flexWrap: 'wrap',
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FilterItemParamHolder = Record<
 | 
				
			||||||
 | 
					    string,
 | 
				
			||||||
 | 
					    FilterItemParams | null | undefined
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IFilterProps {
 | 
				
			||||||
 | 
					    state: FilterItemParamHolder;
 | 
				
			||||||
 | 
					    onChange: (value: FilterItemParamHolder) => void;
 | 
				
			||||||
 | 
					    availableFilters: IFilterItem[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IBaseFilterItem = {
 | 
				
			||||||
 | 
					    label: string;
 | 
				
			||||||
 | 
					    options: {
 | 
				
			||||||
 | 
					        label: string;
 | 
				
			||||||
 | 
					        value: string;
 | 
				
			||||||
 | 
					    }[];
 | 
				
			||||||
 | 
					    filterKey: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ITextFilterItem = IBaseFilterItem & {
 | 
				
			||||||
 | 
					    singularOperators: [string, ...string[]];
 | 
				
			||||||
 | 
					    pluralOperators: [string, ...string[]];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IDateFilterItem = IBaseFilterItem & {
 | 
				
			||||||
 | 
					    dateOperators: [string, ...string[]];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type IFilterItem = ITextFilterItem | IDateFilterItem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Filters: VFC<IFilterProps> = ({
 | 
				
			||||||
 | 
					    state,
 | 
				
			||||||
 | 
					    onChange,
 | 
				
			||||||
 | 
					    availableFilters,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					    const [unselectedFilters, setUnselectedFilters] = useState<string[]>([]);
 | 
				
			||||||
 | 
					    const [selectedFilters, setSelectedFilters] = useState<string[]>([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const deselectFilter = (label: string) => {
 | 
				
			||||||
 | 
					        const newSelectedFilters = selectedFilters.filter((f) => f !== label);
 | 
				
			||||||
 | 
					        const newUnselectedFilters = [...unselectedFilters, label].sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setSelectedFilters(newSelectedFilters);
 | 
				
			||||||
 | 
					        setUnselectedFilters(newUnselectedFilters);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mergeArraysKeepingOrder = (
 | 
				
			||||||
 | 
					        firstArray: string[],
 | 
				
			||||||
 | 
					        secondArray: string[],
 | 
				
			||||||
 | 
					    ): string[] => {
 | 
				
			||||||
 | 
					        const elementsSet = new Set(firstArray);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        secondArray.forEach((element) => {
 | 
				
			||||||
 | 
					            if (!elementsSet.has(element)) {
 | 
				
			||||||
 | 
					                firstArray.push(element);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return firstArray;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    useEffect(() => {
 | 
				
			||||||
 | 
					        const newSelectedFilters = availableFilters
 | 
				
			||||||
 | 
					            .filter((field) => Boolean(state[field.filterKey]))
 | 
				
			||||||
 | 
					            .map((field) => field.label);
 | 
				
			||||||
 | 
					        const newUnselectedFilters = availableFilters
 | 
				
			||||||
 | 
					            .filter((field) => !state[field.filterKey])
 | 
				
			||||||
 | 
					            .map((field) => field.label)
 | 
				
			||||||
 | 
					            .sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setSelectedFilters(
 | 
				
			||||||
 | 
					            mergeArraysKeepingOrder(selectedFilters, newSelectedFilters),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        setUnselectedFilters(newUnselectedFilters);
 | 
				
			||||||
 | 
					    }, [JSON.stringify(state), JSON.stringify(availableFilters)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const hasAvailableFilters = unselectedFilters.length > 0;
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <StyledBox>
 | 
				
			||||||
 | 
					            {selectedFilters.map((selectedFilter) => {
 | 
				
			||||||
 | 
					                const filter = availableFilters.find(
 | 
				
			||||||
 | 
					                    (filter) => filter.label === selectedFilter,
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!filter) {
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ('dateOperators' in filter) {
 | 
				
			||||||
 | 
					                    return (
 | 
				
			||||||
 | 
					                        <FilterDateItem
 | 
				
			||||||
 | 
					                            label={filter.label}
 | 
				
			||||||
 | 
					                            state={state[filter.filterKey]}
 | 
				
			||||||
 | 
					                            onChange={(value) =>
 | 
				
			||||||
 | 
					                                onChange({ [filter.filterKey]: value })
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            operators={filter.dateOperators}
 | 
				
			||||||
 | 
					                            onChipClose={() => deselectFilter(filter.label)}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                    <FilterItem
 | 
				
			||||||
 | 
					                        key={filter.label}
 | 
				
			||||||
 | 
					                        label={filter.label}
 | 
				
			||||||
 | 
					                        state={state[filter.filterKey]}
 | 
				
			||||||
 | 
					                        options={filter.options}
 | 
				
			||||||
 | 
					                        onChange={(value) =>
 | 
				
			||||||
 | 
					                            onChange({ [filter.filterKey]: value })
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        singularOperators={filter.singularOperators}
 | 
				
			||||||
 | 
					                        pluralOperators={filter.pluralOperators}
 | 
				
			||||||
 | 
					                        onChipClose={() => deselectFilter(filter.label)}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <ConditionallyRender
 | 
				
			||||||
 | 
					                condition={hasAvailableFilters}
 | 
				
			||||||
 | 
					                show={
 | 
				
			||||||
 | 
					                    <AddFilterButton
 | 
				
			||||||
 | 
					                        visibleOptions={unselectedFilters}
 | 
				
			||||||
 | 
					                        setVisibleOptions={setUnselectedFilters}
 | 
				
			||||||
 | 
					                        hiddenOptions={selectedFilters}
 | 
				
			||||||
 | 
					                        setHiddenOptions={setSelectedFilters}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </StyledBox>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -5,9 +5,9 @@ import { FilterDateItem } from 'component/common/FilterDateItem/FilterDateItem';
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    FilterItem,
 | 
					    FilterItem,
 | 
				
			||||||
    FilterItemParams,
 | 
					    FilterItemParams,
 | 
				
			||||||
} from 'component/common/FilterItem/FilterItem';
 | 
					} from 'component/filter/FilterItem/FilterItem';
 | 
				
			||||||
import useAllTags from 'hooks/api/getters/useAllTags/useAllTags';
 | 
					import useAllTags from 'hooks/api/getters/useAllTags/useAllTags';
 | 
				
			||||||
import AddFilterButton from 'component/feature/FeatureToggleList/FeatureToggleFilters/AddFilterButton/AddFilterButton';
 | 
					import AddFilterButton from 'component/filter/AddFilterButton';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledBox = styled(Box)(({ theme }) => ({
 | 
					const StyledBox = styled(Box)(({ theme }) => ({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user