Infer enum labels
Reviewed By: LukeDefeo Differential Revision: D51067952 fbshipit-source-id: ed39d3ab037a2169120187bf20bf4a023488c025
This commit is contained in:
committed by
Facebook GitHub Bot
parent
701ae01501
commit
a8f5fecc2b
@@ -121,6 +121,7 @@ test('Correct top level API exposed', () => {
|
|||||||
"ElementSearchResultSet",
|
"ElementSearchResultSet",
|
||||||
"ElementsInspectorElement",
|
"ElementsInspectorElement",
|
||||||
"ElementsInspectorProps",
|
"ElementsInspectorProps",
|
||||||
|
"EnumLabels",
|
||||||
"FieldConfig",
|
"FieldConfig",
|
||||||
"FileDescriptor",
|
"FileDescriptor",
|
||||||
"FileEncoding",
|
"FileEncoding",
|
||||||
|
|||||||
@@ -55,11 +55,16 @@ export type FloatOperatorConfig = {
|
|||||||
precision?: number;
|
precision?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* { value: label }
|
||||||
|
*/
|
||||||
|
export type EnumLabels = {[key: string | number]: string | number};
|
||||||
|
|
||||||
export type EnumOperatorConfig = {
|
export type EnumOperatorConfig = {
|
||||||
valueType: EnumFilterValueType;
|
valueType: EnumFilterValueType;
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
enumLabels: {[key: string]: string};
|
enumLabels: EnumLabels;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AbsoluteDateOperatorConfig = {
|
export type AbsoluteDateOperatorConfig = {
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
import {Select} from 'antd';
|
import {Select} from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {EnumLabels} from './PowerSearchConfig';
|
||||||
|
|
||||||
type PowerSearchEnumSetTermProps = {
|
type PowerSearchEnumSetTermProps = {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onChange: (value: string[]) => void;
|
onChange: (value: string[]) => void;
|
||||||
enumLabels: {[key: string]: string};
|
enumLabels: EnumLabels;
|
||||||
defaultValue?: string[];
|
defaultValue?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
import {Button, Select} from 'antd';
|
import {Button, Select} from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {EnumLabels} from './PowerSearchConfig';
|
||||||
|
|
||||||
type PowerSearchEnumTermProps = {
|
type PowerSearchEnumTermProps = {
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
enumLabels: {[key: string]: string};
|
enumLabels: EnumLabels;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,8 +39,8 @@ export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
|
|||||||
|
|
||||||
let longestOptionLabelWidth = 0;
|
let longestOptionLabelWidth = 0;
|
||||||
Object.values(enumLabels).forEach((label) => {
|
Object.values(enumLabels).forEach((label) => {
|
||||||
if (label.length > longestOptionLabelWidth) {
|
if (label.toString().length > longestOptionLabelWidth) {
|
||||||
longestOptionLabelWidth = label.length;
|
longestOptionLabelWidth = label.toString().length;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
PowerSearchConfig,
|
PowerSearchConfig,
|
||||||
FieldConfig,
|
FieldConfig,
|
||||||
OperatorConfig,
|
OperatorConfig,
|
||||||
|
EnumLabels,
|
||||||
} from './PowerSearchConfig';
|
} from './PowerSearchConfig';
|
||||||
import {PowerSearchContainer} from './PowerSearchContainer';
|
import {PowerSearchContainer} from './PowerSearchContainer';
|
||||||
import {
|
import {
|
||||||
@@ -31,7 +32,13 @@ import {theme} from '../theme';
|
|||||||
import {SearchOutlined} from '@ant-design/icons';
|
import {SearchOutlined} from '@ant-design/icons';
|
||||||
import {getFlipperLib} from 'flipper-plugin-core';
|
import {getFlipperLib} from 'flipper-plugin-core';
|
||||||
|
|
||||||
export {PowerSearchConfig, OperatorConfig, FieldConfig, SearchExpressionTerm};
|
export {
|
||||||
|
PowerSearchConfig,
|
||||||
|
EnumLabels,
|
||||||
|
OperatorConfig,
|
||||||
|
FieldConfig,
|
||||||
|
SearchExpressionTerm,
|
||||||
|
};
|
||||||
|
|
||||||
type PowerSearchProps = {
|
type PowerSearchProps = {
|
||||||
config: PowerSearchConfig;
|
config: PowerSearchConfig;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import {
|
|||||||
FieldConfig,
|
FieldConfig,
|
||||||
OperatorConfig,
|
OperatorConfig,
|
||||||
SearchExpressionTerm,
|
SearchExpressionTerm,
|
||||||
|
EnumLabels,
|
||||||
} from '../PowerSearch';
|
} from '../PowerSearch';
|
||||||
import {
|
import {
|
||||||
dataTablePowerSearchOperatorProcessorConfig,
|
dataTablePowerSearchOperatorProcessorConfig,
|
||||||
@@ -154,7 +155,16 @@ export type DataTableColumn<T = any> = {
|
|||||||
powerSearchConfig?:
|
powerSearchConfig?:
|
||||||
| OperatorConfig[]
|
| OperatorConfig[]
|
||||||
| false
|
| false
|
||||||
| {operators: OperatorConfig[]; useWholeRow?: boolean};
|
| {
|
||||||
|
operators: OperatorConfig[];
|
||||||
|
useWholeRow?: boolean;
|
||||||
|
/**
|
||||||
|
* Auto-generate enum options based on the data.
|
||||||
|
* Requires the column to be set as a secondary "index" (single column, not a compound multi-column index).
|
||||||
|
* See https://fburl.com/code/0waicx6p
|
||||||
|
*/
|
||||||
|
inferEnumOptionsFromData?: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface TableRowRenderContext<T = any> {
|
export interface TableRowRenderContext<T = any> {
|
||||||
@@ -263,6 +273,73 @@ export function DataTable<T extends object>(
|
|||||||
[columns],
|
[columns],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Collecting a hashmap of unique values for every column we infer the power search enum labels for (hashmap of hashmaps).
|
||||||
|
// It could be a hashmap of sets, but then we would need to convert a set to a hashpmap when rendering enum power search term, so it is just more convenient to make it a hashmap of hashmaps
|
||||||
|
const [inferredPowerSearchEnumLabels, setInferredPowerSearchEnumLabels] =
|
||||||
|
React.useState<Record<DataTableColumn['key'], EnumLabels>>({});
|
||||||
|
React.useEffect(() => {
|
||||||
|
const columnKeysToInferOptionsFor: string[] = [];
|
||||||
|
const secondaryIndeciesKeys = new Set(dataSource.secondaryIndicesKeys());
|
||||||
|
|
||||||
|
for (const column of columns) {
|
||||||
|
if (
|
||||||
|
typeof column.powerSearchConfig === 'object' &&
|
||||||
|
!Array.isArray(column.powerSearchConfig) &&
|
||||||
|
column.powerSearchConfig.inferEnumOptionsFromData
|
||||||
|
) {
|
||||||
|
if (!secondaryIndeciesKeys.has(column.key)) {
|
||||||
|
console.warn(
|
||||||
|
'inferEnumOptionsFromData work only if the same column key is specified as a DataSource secondary index! See https://fburl.com/code/0waicx6p. Missing index definition!',
|
||||||
|
column.key,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
columnKeysToInferOptionsFor.push(column.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columnKeysToInferOptionsFor.length > 0) {
|
||||||
|
const getInferredLabels = () => {
|
||||||
|
const newInferredLabels: Record<DataTableColumn['key'], EnumLabels> =
|
||||||
|
{};
|
||||||
|
|
||||||
|
for (const key of columnKeysToInferOptionsFor) {
|
||||||
|
newInferredLabels[key] = {};
|
||||||
|
for (const indexValue of dataSource.getAllIndexValues([
|
||||||
|
key as keyof T,
|
||||||
|
]) ?? []) {
|
||||||
|
// `indexValue` is a stringified JSON in a format of { key: value }
|
||||||
|
const value = Object.values(JSON.parse(indexValue))[0] as string;
|
||||||
|
newInferredLabels[key][value] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newInferredLabels;
|
||||||
|
};
|
||||||
|
setInferredPowerSearchEnumLabels(getInferredLabels());
|
||||||
|
|
||||||
|
const unsubscribeIndexUpdates = dataSource.addDataListener(
|
||||||
|
'siNewIndexValue',
|
||||||
|
({firstOfKind}) => {
|
||||||
|
if (firstOfKind) {
|
||||||
|
setInferredPowerSearchEnumLabels(getInferredLabels());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const unsubscribeDataSourceClear = dataSource.addDataListener(
|
||||||
|
'clear',
|
||||||
|
() => {
|
||||||
|
setInferredPowerSearchEnumLabels(getInferredLabels());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribeIndexUpdates();
|
||||||
|
unsubscribeDataSourceClear();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [columns, dataSource]);
|
||||||
|
|
||||||
const powerSearchConfig: PowerSearchConfig = useMemo(() => {
|
const powerSearchConfig: PowerSearchConfig = useMemo(() => {
|
||||||
const res: PowerSearchConfig = {fields: {}};
|
const res: PowerSearchConfig = {fields: {}};
|
||||||
|
|
||||||
@@ -290,6 +367,20 @@ export function DataTable<T extends object>(
|
|||||||
} else {
|
} else {
|
||||||
columnPowerSearchOperators = column.powerSearchConfig.operators;
|
columnPowerSearchOperators = column.powerSearchConfig.operators;
|
||||||
useWholeRow = !!column.powerSearchConfig.useWholeRow;
|
useWholeRow = !!column.powerSearchConfig.useWholeRow;
|
||||||
|
|
||||||
|
const inferredPowerSearchEnumLabelsForColumn =
|
||||||
|
inferredPowerSearchEnumLabels[column.key];
|
||||||
|
if (
|
||||||
|
inferredPowerSearchEnumLabelsForColumn &&
|
||||||
|
column.powerSearchConfig.inferEnumOptionsFromData
|
||||||
|
) {
|
||||||
|
columnPowerSearchOperators = columnPowerSearchOperators.map(
|
||||||
|
(operator) => ({
|
||||||
|
...operator,
|
||||||
|
enumLabels: inferredPowerSearchEnumLabelsForColumn,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnFieldConfig: FieldConfig = {
|
const columnFieldConfig: FieldConfig = {
|
||||||
@@ -305,7 +396,11 @@ export function DataTable<T extends object>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}, [columns, props.enablePowerSearchWholeRowSearch]);
|
}, [
|
||||||
|
columns,
|
||||||
|
props.enablePowerSearchWholeRowSearch,
|
||||||
|
inferredPowerSearchEnumLabels,
|
||||||
|
]);
|
||||||
|
|
||||||
const renderingConfig = useMemo<TableRowRenderContext<T>>(() => {
|
const renderingConfig = useMemo<TableRowRenderContext<T>>(() => {
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user