Allow freeform entries for enum terms

Summary: Solves https://fb.workplace.com/groups/flippersupport/posts/1726178417862809/?comment_id=1726429847837666&reply_comment_id=1730206487460002

Reviewed By: passy

Differential Revision: D51497924

fbshipit-source-id: d0737b2b82f29ff8ae654e7cad2ef1daa8244756
This commit is contained in:
Andrey Goncharov
2023-11-21 11:40:23 -08:00
committed by Facebook GitHub Bot
parent 864e296f35
commit 294f39eceb
6 changed files with 59 additions and 12 deletions

View File

@@ -64,6 +64,7 @@ export type EnumOperatorConfig = {
key: string;
label: string;
enumLabels: EnumLabels;
allowFreeform?: boolean;
};
export type AbsoluteDateOperatorConfig = {

View File

@@ -16,6 +16,7 @@ type PowerSearchEnumSetTermProps = {
onChange: (value: string[]) => void;
enumLabels: EnumLabels;
defaultValue?: string[];
allowFreeform?: boolean;
};
export const PowerSearchEnumSetTerm: React.FC<PowerSearchEnumSetTermProps> = ({
@@ -23,6 +24,7 @@ export const PowerSearchEnumSetTerm: React.FC<PowerSearchEnumSetTermProps> = ({
onChange,
enumLabels,
defaultValue,
allowFreeform,
}) => {
const options = React.useMemo(() => {
return Object.entries(enumLabels).map(([key, label]) => ({
@@ -38,7 +40,7 @@ export const PowerSearchEnumSetTerm: React.FC<PowerSearchEnumSetTermProps> = ({
return (
<Select
mode="multiple"
mode={allowFreeform ? 'tags' : 'multiple'}
autoFocus={!defaultValue}
style={{minWidth: 100}}
placeholder="..."

View File

@@ -16,6 +16,7 @@ type PowerSearchEnumTermProps = {
onChange: (value: string) => void;
enumLabels: EnumLabels;
defaultValue?: string;
allowFreeform?: boolean;
};
export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
@@ -23,6 +24,7 @@ export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
onChange,
enumLabels,
defaultValue,
allowFreeform,
}) => {
const [editing, setEditing] = React.useState(!defaultValue);
@@ -72,6 +74,7 @@ export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
if (editing) {
return (
<Select
mode={allowFreeform ? 'tags' : undefined}
autoFocus
style={{width}}
placeholder="..."
@@ -100,7 +103,7 @@ export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
return (
<Button onClick={() => setEditing(true)}>
{/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */}
{enumLabels[defaultValue!]}
{enumLabels[defaultValue!] ?? defaultValue}
</Button>
);
};

View File

@@ -115,6 +115,7 @@ export const PowerSearchTerm: React.FC<PowerSearchTermProps> = ({
});
}}
enumLabels={searchTerm.operator.enumLabels}
allowFreeform={searchTerm.operator.allowFreeform}
defaultValue={searchTerm.searchValue}
/>
);
@@ -131,6 +132,7 @@ export const PowerSearchTerm: React.FC<PowerSearchTermProps> = ({
});
}}
enumLabels={searchTerm.operator.enumLabels}
allowFreeform={searchTerm.operator.allowFreeform}
defaultValue={searchTerm.searchValue}
/>
);

View File

@@ -123,42 +123,51 @@ export const dataTablePowerSearchOperators = {
valueType: 'FLOAT',
}),
// { [enumValue]: enumLabel }
enum_is: (enumLabels: EnumLabels) => ({
enum_is: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({
label: 'is',
key: 'enum_is',
valueType: 'ENUM',
enumLabels,
allowFreeform,
}),
enum_is_nullish_or: (enumLabels: EnumLabels) => ({
enum_is_nullish_or: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({
label: 'is nullish or',
key: 'enum_is_nullish_or',
valueType: 'ENUM',
enumLabels,
allowFreeform,
}),
enum_is_not: (enumLabels: EnumLabels) => ({
enum_is_not: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({
label: 'is not',
key: 'enum_is_not',
valueType: 'ENUM',
enumLabels,
allowFreeform,
}),
// TODO: Support logical operations (AND, OR, NOT) to combine primitive operators instead of adding new complex operators!
enum_set_is_nullish_or_any_of: (enumLabels: EnumLabels) => ({
enum_set_is_nullish_or_any_of: (
enumLabels: EnumLabels,
allowFreeform?: boolean,
) => ({
label: 'is nullish or any of',
key: 'enum_set_is_nullish_or_any_of',
valueType: 'ENUM_SET',
enumLabels,
allowFreeform,
}),
enum_set_is_any_of: (enumLabels: EnumLabels) => ({
enum_set_is_any_of: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({
label: 'is any of',
key: 'enum_set_is_any_of',
valueType: 'ENUM_SET',
enumLabels,
allowFreeform,
}),
enum_set_is_none_of: (enumLabels: EnumLabels) => ({
enum_set_is_none_of: (enumLabels: EnumLabels, allowFreeform?: boolean) => ({
label: 'is none of',
key: 'enum_set_is_none_of',
valueType: 'ENUM_SET',
enumLabels,
allowFreeform,
}),
is_nullish: () => ({
label: 'is nullish',

View File

@@ -146,8 +146,18 @@ type DataTableInput<T = any> =
};
type PowerSearchSimplifiedConfig =
| {type: 'enum'; enumLabels: EnumLabels; inferEnumOptionsFromData?: false}
| {type: 'enum'; enumLabels?: never; inferEnumOptionsFromData: true}
| {
type: 'enum';
enumLabels: EnumLabels;
inferEnumOptionsFromData?: false;
allowFreeform?: boolean;
}
| {
type: 'enum';
enumLabels?: never;
inferEnumOptionsFromData: true;
allowFreeform?: boolean;
}
| {type: 'int'}
| {type: 'float'}
| {type: 'string'}
@@ -163,6 +173,12 @@ type PowerSearchExtendedConfig = {
* See https://fburl.com/code/0waicx6p
*/
inferEnumOptionsFromData?: boolean;
/**
* Allows freeform entries for enum column types. Makes most sense together with `inferEnumOptionsFromData`.
* If `inferEnumOptionsFromData=true`, then it is `true` by default.
* See use-case https://fburl.com/workplace/0kx6fkhm
*/
allowFreeform?: boolean;
};
const powerSearchConfigIsExtendedConfig = (
@@ -418,10 +434,12 @@ export function DataTable<T extends object>(
inferredPowerSearchEnumLabelsForColumn &&
column.powerSearchConfig.inferEnumOptionsFromData
) {
const allowFreeform = column.powerSearchConfig.allowFreeform ?? true;
columnPowerSearchOperators = columnPowerSearchOperators.map(
(operator) => ({
...operator,
enumLabels: inferredPowerSearchEnumLabelsForColumn,
allowFreeform,
}),
);
}
@@ -474,18 +492,30 @@ export function DataTable<T extends object>(
}
case 'enum': {
let enumLabels: EnumLabels;
let allowFreeform = column.powerSearchConfig.allowFreeform;
if (column.powerSearchConfig.inferEnumOptionsFromData) {
enumLabels = inferredPowerSearchEnumLabels[column.key] ?? {};
// Fallback to `true` by default when we use inferred labels
if (allowFreeform === undefined) {
allowFreeform = true;
}
} else {
enumLabels = column.powerSearchConfig.enumLabels;
}
columnPowerSearchOperators = [
dataTablePowerSearchOperators.enum_set_is_any_of(enumLabels),
dataTablePowerSearchOperators.enum_set_is_none_of(enumLabels),
dataTablePowerSearchOperators.enum_set_is_any_of(
enumLabels,
allowFreeform,
),
dataTablePowerSearchOperators.enum_set_is_none_of(
enumLabels,
allowFreeform,
),
dataTablePowerSearchOperators.enum_set_is_nullish_or_any_of(
enumLabels,
allowFreeform,
),
];
break;