mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
feat: segments filter (#5558)
Co-authored-by: sjaanus <sellinjaanus@gmail.com>
This commit is contained in:
parent
eda4186a6c
commit
87ebbb0fa2
@ -1,23 +1,34 @@
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { FilterItem } from './FilterItem';
|
||||
import { FilterItem, IFilterItemProps } from './FilterItem';
|
||||
|
||||
const getOption = (option: string) =>
|
||||
screen.getByText(option).closest('li')!.querySelector('input')!;
|
||||
|
||||
const setup = (initialState: FilterItem) => {
|
||||
const recordedChanges: FilterItem[] = [];
|
||||
const mockProps = {
|
||||
const mockProps: IFilterItemProps = {
|
||||
label: 'Test Label',
|
||||
options: [
|
||||
{ label: 'Option 1', value: '1' },
|
||||
{ label: 'Option 2', value: '2' },
|
||||
{ label: 'Option 3', value: '3' },
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
onChange: (value: FilterItem) => {
|
||||
recordedChanges.push(value);
|
||||
},
|
||||
onChipClose: () => {},
|
||||
singularOperators: ['IS', 'IS_NOT'],
|
||||
pluralOperators: ['IS_ANY_OF', 'IS_NONE_OF'],
|
||||
state: initialState,
|
||||
};
|
||||
|
||||
@ -49,7 +60,10 @@ describe('FilterItem Component', () => {
|
||||
getOption('Option 2').click();
|
||||
|
||||
expect(recordedChanges).toEqual([
|
||||
{ operator: 'IS_ANY_OF', values: ['1', '3', '2'] },
|
||||
{
|
||||
operator: 'IS_ANY_OF',
|
||||
values: ['1', '3', '2'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@ -61,7 +75,12 @@ describe('FilterItem Component', () => {
|
||||
|
||||
const recordedChanges = setup(mockState);
|
||||
|
||||
expect(recordedChanges).toEqual([{ operator: 'IS', values: ['1'] }]);
|
||||
expect(recordedChanges).toEqual([
|
||||
{
|
||||
operator: 'IS',
|
||||
values: ['1'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('adjusts operator to match plural items', async () => {
|
||||
@ -73,7 +92,10 @@ describe('FilterItem Component', () => {
|
||||
const recordedChanges = setup(mockState);
|
||||
|
||||
expect(recordedChanges).toEqual([
|
||||
{ operator: 'IS_ANY_OF', values: ['1', '2'] },
|
||||
{
|
||||
operator: 'IS_ANY_OF',
|
||||
values: ['1', '2'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@ -93,7 +115,10 @@ describe('FilterItem Component', () => {
|
||||
newOperator.click();
|
||||
|
||||
expect(recordedChanges).toEqual([
|
||||
{ operator: 'IS_NONE_OF', values: ['1', '3'] },
|
||||
{
|
||||
operator: 'IS_NONE_OF',
|
||||
values: ['1', '3'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@ -109,6 +134,11 @@ describe('FilterItem Component', () => {
|
||||
|
||||
deleteElement.click();
|
||||
|
||||
expect(recordedChanges).toEqual([{ operator: 'IS', values: [] }]);
|
||||
expect(recordedChanges).toEqual([
|
||||
{
|
||||
operator: 'IS',
|
||||
values: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Search } from '@mui/icons-material';
|
||||
import { List, ListItemText, Box, InputAdornment } from '@mui/material';
|
||||
import { Box, InputAdornment, List, ListItemText } from '@mui/material';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
StyledCheckbox,
|
||||
@ -9,22 +9,17 @@ import {
|
||||
StyledTextField,
|
||||
} from './FilterItem.styles';
|
||||
import { FilterItemChip } from './FilterItemChip/FilterItemChip';
|
||||
import {
|
||||
FeatureTogglesListFilters,
|
||||
IFilterItem,
|
||||
} from '../../feature/FeatureToggleList/FeatureToggleFilters/FeatureToggleFilters';
|
||||
|
||||
interface IFilterItemProps {
|
||||
export interface IFilterItemProps {
|
||||
label: string;
|
||||
options: Array<{ label: string; value: string }>;
|
||||
onChange: (value: FilterItem) => void;
|
||||
onChipClose: () => void;
|
||||
state: FilterItem | null | undefined;
|
||||
singularOperators: [string, ...string[]];
|
||||
pluralOperators: [string, ...string[]];
|
||||
}
|
||||
|
||||
const singularOperators = ['IS', 'IS_NOT'];
|
||||
const pluralOperators = ['IS_ANY_OF', 'IS_NONE_OF'];
|
||||
|
||||
export type FilterItem = {
|
||||
operator: string;
|
||||
values: string[];
|
||||
@ -36,6 +31,8 @@ export const FilterItem: FC<IFilterItemProps> = ({
|
||||
onChange,
|
||||
onChipClose,
|
||||
state,
|
||||
singularOperators,
|
||||
pluralOperators,
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
|
||||
|
@ -4,6 +4,7 @@ import { FilterItem } from 'component/common/FilterItem/FilterItem';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AddFilterButton from './AddFilterButton';
|
||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||
|
||||
const StyledBox = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -14,6 +15,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
|
||||
export type FeatureTogglesListFilters = {
|
||||
project?: FilterItem | null | undefined;
|
||||
state?: FilterItem | null | undefined;
|
||||
segment?: FilterItem | null | undefined;
|
||||
};
|
||||
|
||||
interface IFeatureToggleFiltersProps {
|
||||
@ -36,6 +38,7 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
|
||||
onChange,
|
||||
}) => {
|
||||
const { projects } = useProjects();
|
||||
const { segments } = useSegments();
|
||||
|
||||
const stateOptions = [
|
||||
{
|
||||
@ -66,6 +69,10 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
|
||||
label: project.name,
|
||||
value: project.id,
|
||||
}));
|
||||
const segmentsOptions = (segments || []).map((segment) => ({
|
||||
label: segment.name,
|
||||
value: segment.name,
|
||||
}));
|
||||
|
||||
const newFilterItems: IFilterItem[] = [
|
||||
{
|
||||
@ -80,10 +87,19 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
|
||||
filterKey: 'project',
|
||||
enabled: Boolean(state.project),
|
||||
} as const,
|
||||
{
|
||||
label: 'Segment',
|
||||
options: segmentsOptions,
|
||||
filterKey: 'segment',
|
||||
} as const,
|
||||
];
|
||||
|
||||
setAvailableFilters(newFilterItems);
|
||||
}, [JSON.stringify(projects), JSON.stringify(state)]);
|
||||
}, [
|
||||
JSON.stringify(projects),
|
||||
JSON.stringify(state),
|
||||
JSON.stringify(segments),
|
||||
]);
|
||||
|
||||
return (
|
||||
<StyledBox>
|
||||
@ -98,6 +114,8 @@ export const FeatureToggleFilters: VFC<IFeatureToggleFiltersProps> = ({
|
||||
onChange={(value) =>
|
||||
onChange({ [filter.filterKey]: value })
|
||||
}
|
||||
singularOperators={['IS', 'IS_NOT']}
|
||||
pluralOperators={['IS_ANY_OF', 'IS_NONE_OF']}
|
||||
onChipClose={() => removeFilter(filter.label)}
|
||||
/>
|
||||
),
|
||||
|
@ -84,6 +84,7 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
sortOrder: withDefault(StringParam, 'desc'),
|
||||
project: FilterItemParam,
|
||||
state: FilterItemParam,
|
||||
segment: FilterItemParam,
|
||||
};
|
||||
const [tableState, setTableState] = usePersistentTableState(
|
||||
'features-list-table',
|
||||
|
Loading…
Reference in New Issue
Block a user