fix: Order of Select items when unselecting (#17169)

* fix: Order of Select items when unselecting

* Adds a property comparator that supports strings and numbers

* Uses a named export for propertyComparator
This commit is contained in:
Michael S. Molina 2021-10-25 08:47:01 -03:00 committed by GitHub
parent ef3afbde82
commit 55be249870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 216 additions and 112 deletions

View File

@ -256,15 +256,11 @@ export default class AddSliceContainer extends React.PureComponent<
customLabel: ReactNode;
label: string;
value: string;
}[] = response.json.result
.map((item: Dataset) => ({
value: `${item.id}__${item.datasource_type}`,
customLabel: this.newLabel(item),
label: item.table_name,
}))
.sort((a: { label: string }, b: { label: string }) =>
a.label.localeCompare(b.label),
);
}[] = response.json.result.map((item: Dataset) => ({
value: `${item.id}__${item.datasource_type}`,
customLabel: this.newLabel(item),
label: item.table_name,
}));
return {
data: list,
totalCount: response.json.count,

View File

@ -201,22 +201,18 @@ export default function DatabaseSelector({
// TODO: Would be nice to add pagination in a follow-up. Needs endpoint changes.
SupersetClient.get({ endpoint })
.then(({ json }) => {
const options = json.result
.map((s: string) => ({
value: s,
label: s,
title: s,
}))
.sort((a: { label: string }, b: { label: string }) =>
a.label.localeCompare(b.label),
);
const options = json.result.map((s: string) => ({
value: s,
label: s,
title: s,
}));
if (onSchemasLoad) {
onSchemasLoad(options);
}
setSchemaOptions(options);
setLoadingSchemas(false);
})
.catch(e => {
.catch(() => {
setLoadingSchemas(false);
handleError(t('There was an error loading the schemas'));
});

View File

@ -313,7 +313,7 @@ const USERS = [
'Claire',
'Benedetta',
'Ilenia',
];
].sort();
export const AsyncSelect = ({
fetchOnlyOnSearch,
@ -429,6 +429,7 @@ export const AsyncSelect = ({
};
AsyncSelect.args = {
allowClear: false,
allowNewOptions: false,
fetchOnlyOnSearch: false,
pageSize: 10,

View File

@ -52,7 +52,7 @@ const OPTIONS = [
{ label: 'Irfan', value: 18, gender: 'Male' },
{ label: 'George', value: 19, gender: 'Male' },
{ label: 'Ashfaq', value: 20, gender: 'Male' },
];
].sort((option1, option2) => option1.label.localeCompare(option2.label));
const loadOptions = async (search: string, page: number, pageSize: number) => {
const totalCount = OPTIONS.length;
@ -100,6 +100,8 @@ const findSelectValue = () =>
const findAllSelectValues = () =>
waitFor(() => getElementsByClassName('.ant-select-selection-item'));
const clearAll = () => userEvent.click(screen.getByLabelText('close-circle'));
const type = (text: string) => {
const select = getSelect();
userEvent.clear(select);
@ -127,6 +129,37 @@ test('inverts the selection', async () => {
expect(await screen.findByLabelText('stop')).toBeInTheDocument();
});
test('sort the options by label if no sort comparator is provided', async () => {
const unsortedOptions = [...OPTIONS].sort(() => Math.random());
render(<Select {...defaultProps} options={unsortedOptions} />);
await open();
const options = await findAllSelectOptions();
options.forEach((option, key) =>
expect(option).toHaveTextContent(OPTIONS[key].label),
);
});
test('sort the options using a custom sort comparator', async () => {
const sortComparator = (
option1: typeof OPTIONS[0],
option2: typeof OPTIONS[0],
) => option1.gender.localeCompare(option2.gender);
render(
<Select
{...defaultProps}
options={loadOptions}
sortComparator={sortComparator}
/>,
);
await open();
const options = await findAllSelectOptions();
const optionsPage = OPTIONS.slice(0, defaultProps.pageSize);
const sortedOptions = optionsPage.sort(sortComparator);
options.forEach((option, key) =>
expect(option).toHaveTextContent(sortedOptions[key].label),
);
});
test('displays the selected values first', async () => {
render(<Select {...defaultProps} mode="multiple" />);
const option3 = OPTIONS[2].label;
@ -141,6 +174,22 @@ test('displays the selected values first', async () => {
expect(sortedOptions[1]).toHaveTextContent(option8);
});
test('displays the original order when unselecting', async () => {
render(<Select {...defaultProps} mode="multiple" />);
const option3 = OPTIONS[2].label;
const option8 = OPTIONS[7].label;
await open();
userEvent.click(await findSelectOption(option3));
userEvent.click(await findSelectOption(option8));
await type('{esc}');
clearAll();
await open();
const options = await findAllSelectOptions();
options.forEach((option, key) =>
expect(option).toHaveTextContent(OPTIONS[key].label),
);
});
test('searches for label or value', async () => {
const option = OPTIONS[11];
render(<Select {...defaultProps} />);
@ -172,11 +221,11 @@ test('searches for custom fields', async () => {
await type('Female');
options = await findAllSelectOptions();
expect(options.length).toBe(5);
expect(options[0]).toHaveTextContent('Olivia');
expect(options[1]).toHaveTextContent('Emma');
expect(options[2]).toHaveTextContent('Ava');
expect(options[3]).toHaveTextContent('Charlotte');
expect(options[4]).toHaveTextContent('Nikole');
expect(options[0]).toHaveTextContent('Ava');
expect(options[1]).toHaveTextContent('Charlotte');
expect(options[2]).toHaveTextContent('Emma');
expect(options[3]).toHaveTextContent('Nikole');
expect(options[4]).toHaveTextContent('Olivia');
await type('1');
expect(screen.getByText(NO_DATA)).toBeInTheDocument();
});
@ -227,7 +276,7 @@ test('clear all the values', async () => {
onClear={onClear}
/>,
);
userEvent.click(screen.getByLabelText('close-circle'));
clearAll();
expect(onClear).toHaveBeenCalled();
const values = await findAllSelectValues();
expect(values.length).toBe(0);
@ -378,8 +427,8 @@ test('static - searches for an item', async () => {
await type(search);
const options = await findAllSelectOptions();
expect(options.length).toBe(2);
expect(options[0]).toHaveTextContent('Olivia');
expect(options[1]).toHaveTextContent('Oliver');
expect(options[0]).toHaveTextContent('Oliver');
expect(options[1]).toHaveTextContent('Olivia');
});
test('async - renders the select with default props', () => {
@ -546,8 +595,8 @@ test('async - searches for an item already loaded', async () => {
await waitForElementToBeRemoved(screen.getByText(LOADING));
const options = await findAllSelectOptions();
expect(options.length).toBe(2);
expect(options[0]).toHaveTextContent('Olivia');
expect(options[1]).toHaveTextContent('Oliver');
expect(options[0]).toHaveTextContent('Oliver');
expect(options[1]).toHaveTextContent('Olivia');
});
test('async - searches for an item in a page not loaded', async () => {
@ -599,7 +648,7 @@ test('async - does not fire a new request for the same search input', async () =
await type('search');
expect(await screen.findByText(NO_DATA)).toBeInTheDocument();
expect(loadOptions).toHaveBeenCalledTimes(1);
userEvent.click(screen.getByLabelText('close-circle'));
clearAll();
await type('search');
expect(await screen.findByText(NO_DATA)).toBeInTheDocument();
expect(loadOptions).toHaveBeenCalledTimes(1);

View File

@ -94,6 +94,7 @@ export interface SelectProps extends PickedSelectProps {
invertSelection?: boolean;
fetchOnlyOnSearch?: boolean;
onError?: (error: string) => void;
sortComparator?: (a: AntdLabeledValue, b: AntdLabeledValue) => number;
}
const StyledContainer = styled.div`
@ -168,6 +169,30 @@ const Error = ({ error }: { error: string }) => (
</StyledError>
);
const defaultSortComparator = (a: AntdLabeledValue, b: AntdLabeledValue) => {
if (typeof a.label === 'string' && typeof b.label === 'string') {
return a.label.localeCompare(b.label);
}
if (typeof a.value === 'string' && typeof b.value === 'string') {
return a.value.localeCompare(b.value);
}
return (a.value as number) - (b.value as number);
};
/**
* It creates a comparator to check for a specific property.
* Can be used with string and number property values.
* */
export const propertyComparator = (property: string) => (
a: AntdLabeledValue,
b: AntdLabeledValue,
) => {
if (typeof a[property] === 'string' && typeof b[property] === 'string') {
return a[property].localeCompare(b[property]);
}
return (a[property] as number) - (b[property] as number);
};
const Select = ({
allowNewOptions = false,
ariaLabel,
@ -189,6 +214,7 @@ const Select = ({
pageSize = DEFAULT_PAGE_SIZE,
placeholder = t('Select ...'),
showSearch = true,
sortComparator = defaultSortComparator,
value,
...props
}: SelectProps) => {
@ -277,14 +303,21 @@ const Select = ({
}
});
}
const sortedOptions = [...topOptions, ...otherOptions];
const sortedOptions = [
...topOptions.sort(sortComparator),
...otherOptions.sort(sortComparator),
];
if (!isEqual(sortedOptions, selectOptions)) {
setSelectOptions(sortedOptions);
}
} else {
const sortedOptions = [...selectOptions].sort(sortComparator);
if (!isEqual(sortedOptions, selectOptions)) {
setSelectOptions(sortedOptions);
}
}
},
[isAsync, isSingleMode, labelInValue, selectOptions],
[isAsync, isSingleMode, labelInValue, selectOptions, sortComparator],
);
const handleOnSelect = (
@ -342,28 +375,34 @@ const Select = ({
[onError],
);
const handleData = (data: OptionsType) => {
let mergedData: OptionsType = [];
if (data && Array.isArray(data) && data.length) {
const dataValues = new Set();
data.forEach(option =>
dataValues.add(String(option.value).toLocaleLowerCase()),
);
const handleData = useCallback(
(data: OptionsType) => {
let mergedData: OptionsType = [];
if (data && Array.isArray(data) && data.length) {
const dataValues = new Set();
data.forEach(option =>
dataValues.add(String(option.value).toLocaleLowerCase()),
);
// merges with existing and creates unique options
setSelectOptions(prevOptions => {
mergedData = [
...prevOptions.filter(
previousOption =>
!dataValues.has(String(previousOption.value).toLocaleLowerCase()),
),
...data,
];
return mergedData;
});
}
return mergedData;
};
// merges with existing and creates unique options
setSelectOptions(prevOptions => {
mergedData = [
...prevOptions.filter(
previousOption =>
!dataValues.has(
String(previousOption.value).toLocaleLowerCase(),
),
),
...data,
];
mergedData.sort(sortComparator);
return mergedData;
});
}
return mergedData;
},
[sortComparator],
);
const handlePaginatedFetch = useMemo(
() => (value: string, page: number, pageSize: number) => {
@ -401,7 +440,7 @@ const Select = ({
setIsTyping(false);
});
},
[allValuesLoaded, fetchOnlyOnSearch, internalOnError, options],
[allValuesLoaded, fetchOnlyOnSearch, handleData, internalOnError, options],
);
const handleOnSearch = useMemo(
@ -474,8 +513,8 @@ const Select = ({
}
// multiple or tags mode keep the dropdown visible while selecting options
// this waits for the dropdown to be closed before sorting the top options
if (!isSingleMode && !isDropdownVisible) {
// this waits for the dropdown to be opened before sorting the top options
if (!isSingleMode && isDropdownVisible) {
handleTopOptions(selectValue);
}
};

View File

@ -209,11 +209,7 @@ const TableSelector: FunctionComponent<TableSelectorProps> = ({
if (onTablesLoad) {
onTablesLoad(json.options);
}
setTableOptions(
options.sort((a: { text: string }, b: { text: string }) =>
a.text.localeCompare(b.text),
),
);
setTableOptions(options);
setCurrentTable(currentTable);
setLoadingTables(false);
})

View File

@ -83,17 +83,13 @@ ALL_ZONES.forEach(zone => {
}
});
const TIMEZONE_OPTIONS = TIMEZONES.sort(
// sort by offset
(a, b) =>
moment.tz(currentDate, a.name).utcOffset() -
moment.tz(currentDate, b.name).utcOffset(),
).map(zone => ({
const TIMEZONE_OPTIONS = TIMEZONES.map(zone => ({
label: `GMT ${moment
.tz(currentDate, zone.name)
.format('Z')} (${getTimezoneName(zone.name)})`,
value: zone.name,
offsets: getOffsetKey(zone.name),
timezoneName: zone.name,
}));
const TimezoneSelector = ({ onTimezoneChange, timezone }: TimezoneProps) => {
@ -126,6 +122,13 @@ const TimezoneSelector = ({ onTimezoneChange, timezone }: TimezoneProps) => {
onChange={onTimezoneChange}
value={timezone || DEFAULT_TIMEZONE.value}
options={TIMEZONE_OPTIONS}
sortComparator={(
a: typeof TIMEZONE_OPTIONS[number],
b: typeof TIMEZONE_OPTIONS[number],
) =>
moment.tz(currentDate, a.timezoneName).utcOffset() -
moment.tz(currentDate, b.timezoneName).utcOffset()
}
/>
);
};

View File

@ -17,7 +17,7 @@
* under the License.
*/
import React, { RefObject } from 'react';
import { Select } from 'src/components';
import Select, { propertyComparator } from 'src/components/Select/Select';
import { t, styled } from '@superset-ui/core';
import Alert from 'src/components/Alert';
import Button from 'src/components/Button';
@ -120,6 +120,7 @@ class RefreshIntervalModal extends React.PureComponent<
options={options}
value={refreshFrequency}
onChange={this.handleFrequencyChange}
sortComparator={propertyComparator('value')}
/>
{showRefreshWarning && (
<RefreshWarningContainer>

View File

@ -73,7 +73,6 @@ export function ColumnSelect({
ensureIsArray(columns)
.filter(filterValues)
.map((col: Column) => col.column_name)
.sort((a: string, b: string) => a.localeCompare(b))
.map((column: string) => ({ label: column, value: column })),
[columns, filterValues],
);

View File

@ -72,11 +72,7 @@ const DatasetSelect = ({ onChange, value }: DatasetSelectProps) => {
const data: {
label: string;
value: string | number;
}[] = response.json.result
.map(datasetToSelectOption)
.sort((a: { label: string }, b: { label: string }) =>
a.label.localeCompare(b.label),
);
}[] = response.json.result.map(datasetToSelectOption);
return {
data,
totalCount: response.json.count,

View File

@ -19,7 +19,7 @@
import React from 'react';
import { styled, t } from '@superset-ui/core';
import { Form, FormItem, FormProps } from 'src/components/Form';
import { Select } from 'src/components';
import Select, { propertyComparator } from 'src/components/Select/Select';
import { Col, InputNumber, Row } from 'src/common/components';
import Button from 'src/components/Button';
import {
@ -44,17 +44,17 @@ const colorSchemeOptions = [
];
const operatorOptions = [
{ value: COMPARATOR.NONE, label: 'None' },
{ value: COMPARATOR.GREATER_THAN, label: '>' },
{ value: COMPARATOR.LESS_THAN, label: '<' },
{ value: COMPARATOR.GREATER_OR_EQUAL, label: '≥' },
{ value: COMPARATOR.LESS_OR_EQUAL, label: '≤' },
{ value: COMPARATOR.EQUAL, label: '=' },
{ value: COMPARATOR.NOT_EQUAL, label: '≠' },
{ value: COMPARATOR.BETWEEN, label: '< x <' },
{ value: COMPARATOR.BETWEEN_OR_EQUAL, label: '≤ x ≤' },
{ value: COMPARATOR.BETWEEN_OR_LEFT_EQUAL, label: '≤ x <' },
{ value: COMPARATOR.BETWEEN_OR_RIGHT_EQUAL, label: '< x ≤' },
{ value: COMPARATOR.NONE, label: 'None', order: 0 },
{ value: COMPARATOR.GREATER_THAN, label: '>', order: 1 },
{ value: COMPARATOR.LESS_THAN, label: '<', order: 2 },
{ value: COMPARATOR.GREATER_OR_EQUAL, label: '≥', order: 3 },
{ value: COMPARATOR.LESS_OR_EQUAL, label: '≤', order: 4 },
{ value: COMPARATOR.EQUAL, label: '=', order: 5 },
{ value: COMPARATOR.NOT_EQUAL, label: '≠', order: 6 },
{ value: COMPARATOR.BETWEEN, label: '< x <', order: 7 },
{ value: COMPARATOR.BETWEEN_OR_EQUAL, label: '≤ x ≤', order: 8 },
{ value: COMPARATOR.BETWEEN_OR_LEFT_EQUAL, label: '≤ x <', order: 9 },
{ value: COMPARATOR.BETWEEN_OR_RIGHT_EQUAL, label: '< x ≤', order: 10 },
];
const targetValueValidator = (
@ -126,7 +126,11 @@ const operatorField = (
rules={rulesRequired}
initialValue={operatorOptions[0].value}
>
<Select ariaLabel={t('Operator')} options={operatorOptions} />
<Select
ariaLabel={t('Operator')}
options={operatorOptions}
sortComparator={propertyComparator('order')}
/>
</FormItem>
);

View File

@ -40,7 +40,7 @@ import Label, { Type } from 'src/components/Label';
import Popover from 'src/components/Popover';
import { Divider } from 'src/common/components';
import Icons from 'src/components/Icons';
import { Select } from 'src/components';
import Select, { propertyComparator } from 'src/components/Select/Select';
import { Tooltip } from 'src/components/Tooltip';
import { DEFAULT_TIME_RANGE } from 'src/explore/constants';
import { useDebouncedEffect } from 'src/explore/exploreUtils';
@ -295,6 +295,7 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
options={FRAME_OPTIONS}
value={frame}
onChange={onChangeFrame}
sortComparator={propertyComparator('order')}
/>
{frame !== 'No filter' && <Divider />}
{frame === 'Common' && (

View File

@ -23,7 +23,7 @@ import { isInteger } from 'lodash';
import { Col, InputNumber, Row } from 'src/common/components';
import { DatePicker } from 'src/components/DatePicker';
import { Radio } from 'src/components/Radio';
import { Select } from 'src/components';
import Select, { propertyComparator } from 'src/components/Select/Select';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import {
SINCE_GRAIN_OPTIONS,
@ -41,6 +41,8 @@ import {
FrameComponentProps,
} from 'src/explore/components/controls/DateFilterControl/types';
const sortComparator = propertyComparator('order');
export function CustomFrame(props: FrameComponentProps) {
const { customRange, matchedFlag } = customTimeRangeDecode(props.value);
if (!matchedFlag) {
@ -121,6 +123,7 @@ export function CustomFrame(props: FrameComponentProps) {
options={SINCE_MODE_OPTIONS}
value={sinceMode}
onChange={(value: string) => onChange('sinceMode', value)}
sortComparator={sortComparator}
/>
{sinceMode === 'specific' && (
<Row>
@ -155,6 +158,7 @@ export function CustomFrame(props: FrameComponentProps) {
options={SINCE_GRAIN_OPTIONS}
value={sinceGrain}
onChange={(value: string) => onChange('sinceGrain', value)}
sortComparator={sortComparator}
/>
</Col>
</Row>
@ -173,6 +177,7 @@ export function CustomFrame(props: FrameComponentProps) {
options={UNTIL_MODE_OPTIONS}
value={untilMode}
onChange={(value: string) => onChange('untilMode', value)}
sortComparator={sortComparator}
/>
{untilMode === 'specific' && (
<Row>
@ -206,6 +211,7 @@ export function CustomFrame(props: FrameComponentProps) {
options={UNTIL_GRAIN_OPTIONS}
value={untilGrain}
onChange={(value: string) => onChange('untilGrain', value)}
sortComparator={sortComparator}
/>
</Col>
</Row>

View File

@ -19,6 +19,7 @@
export type SelectOptionType = {
value: string;
label: string;
order: number;
};
export type FrameType =

View File

@ -28,28 +28,32 @@ import {
} from 'src/explore/components/controls/DateFilterControl/types';
export const FRAME_OPTIONS: SelectOptionType[] = [
{ value: 'Common', label: t('Last') },
{ value: 'Calendar', label: t('Previous') },
{ value: 'Custom', label: t('Custom') },
{ value: 'Advanced', label: t('Advanced') },
{ value: 'No filter', label: t('No filter') },
{ value: 'Common', label: t('Last'), order: 0 },
{ value: 'Calendar', label: t('Previous'), order: 1 },
{ value: 'Custom', label: t('Custom'), order: 2 },
{ value: 'Advanced', label: t('Advanced'), order: 3 },
{ value: 'No filter', label: t('No filter'), order: 4 },
];
export const COMMON_RANGE_OPTIONS: SelectOptionType[] = [
{ value: 'Last day', label: t('last day') },
{ value: 'Last week', label: t('last week') },
{ value: 'Last month', label: t('last month') },
{ value: 'Last quarter', label: t('last quarter') },
{ value: 'Last year', label: t('last year') },
{ value: 'Last day', label: t('last day'), order: 0 },
{ value: 'Last week', label: t('last week'), order: 1 },
{ value: 'Last month', label: t('last month'), order: 2 },
{ value: 'Last quarter', label: t('last quarter'), order: 3 },
{ value: 'Last year', label: t('last year'), order: 4 },
];
export const COMMON_RANGE_VALUES_SET = new Set(
COMMON_RANGE_OPTIONS.map(({ value }) => value),
);
export const CALENDAR_RANGE_OPTIONS: SelectOptionType[] = [
{ value: PreviousCalendarWeek, label: t('previous calendar week') },
{ value: PreviousCalendarMonth, label: t('previous calendar month') },
{ value: PreviousCalendarYear, label: t('previous calendar year') },
{ value: PreviousCalendarWeek, label: t('previous calendar week'), order: 0 },
{
value: PreviousCalendarMonth,
label: t('previous calendar month'),
order: 1,
},
{ value: PreviousCalendarYear, label: t('previous calendar year'), order: 2 },
];
export const CALENDAR_RANGE_VALUES_SET = new Set(
CALENDAR_RANGE_OPTIONS.map(({ value }) => value),
@ -67,24 +71,26 @@ const GRAIN_OPTIONS = [
];
export const SINCE_GRAIN_OPTIONS: SelectOptionType[] = GRAIN_OPTIONS.map(
item => ({
(item, index) => ({
value: item.value,
label: item.label(t('Before')),
order: index,
}),
);
export const UNTIL_GRAIN_OPTIONS: SelectOptionType[] = GRAIN_OPTIONS.map(
item => ({
(item, index) => ({
value: item.value,
label: item.label(t('After')),
order: index,
}),
);
export const SINCE_MODE_OPTIONS: SelectOptionType[] = [
{ value: 'specific', label: t('Specific Date/Time') },
{ value: 'relative', label: t('Relative Date/Time') },
{ value: 'now', label: t('Now') },
{ value: 'today', label: t('Midnight') },
{ value: 'specific', label: t('Specific Date/Time'), order: 0 },
{ value: 'relative', label: t('Relative Date/Time'), order: 1 },
{ value: 'now', label: t('Now'), order: 2 },
{ value: 'today', label: t('Midnight'), order: 3 },
];
export const UNTIL_MODE_OPTIONS: SelectOptionType[] = SINCE_MODE_OPTIONS.slice();

View File

@ -44,6 +44,7 @@ jest.mock('src/components/Select/Select', () => ({
</button>
</div>
),
propertyComparator: jest.fn(),
}));
fetchMock.get(datasetsOwnersEndpoint, {

View File

@ -38,7 +38,7 @@ import { Switch } from 'src/components/Switch';
import Modal from 'src/components/Modal';
import TimezoneSelector from 'src/components/TimezoneSelector';
import { Radio } from 'src/components/Radio';
import { Select } from 'src/components';
import Select, { propertyComparator } from 'src/components/Select/Select';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import withToasts from 'src/components/MessageToasts/withToasts';
import Owner from 'src/types/Owner';
@ -85,30 +85,37 @@ const CONDITIONS = [
{
label: t('< (Smaller than)'),
value: '<',
order: 0,
},
{
label: t('> (Larger than)'),
value: '>',
order: 1,
},
{
label: t('<= (Smaller or equal)'),
value: '<=',
order: 2,
},
{
label: t('>= (Larger or equal)'),
value: '>=',
order: 3,
},
{
label: t('== (Is equal)'),
value: '==',
order: 4,
},
{
label: t('!= (Is not equal)'),
value: '!=',
order: 5,
},
{
label: t('Not null'),
value: 'not null',
order: 6,
},
];
@ -1147,6 +1154,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
currentAlert?.validator_config_json?.op || undefined
}
options={CONDITIONS}
sortComparator={propertyComparator('order')}
/>
</div>
</StyledInputContainer>
@ -1214,6 +1222,7 @@ const AlertReportModal: FunctionComponent<AlertReportModalProps> = ({
onChange={onLogRetentionChange}
value={currentAlert?.log_retention || DEFAULT_RETENTION}
options={RETENTION_OPTIONS}
sortComparator={propertyComparator('value')}
/>
</div>
</StyledInputContainer>