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