Infer enum labels

Reviewed By: LukeDefeo

Differential Revision: D51067952

fbshipit-source-id: ed39d3ab037a2169120187bf20bf4a023488c025
This commit is contained in:
Andrey Goncharov
2023-11-08 02:08:25 -08:00
committed by Facebook GitHub Bot
parent 701ae01501
commit a8f5fecc2b
6 changed files with 118 additions and 8 deletions

View File

@@ -121,6 +121,7 @@ test('Correct top level API exposed', () => {
"ElementSearchResultSet",
"ElementsInspectorElement",
"ElementsInspectorProps",
"EnumLabels",
"FieldConfig",
"FileDescriptor",
"FileEncoding",

View File

@@ -55,11 +55,16 @@ export type FloatOperatorConfig = {
precision?: number;
};
/**
* { value: label }
*/
export type EnumLabels = {[key: string | number]: string | number};
export type EnumOperatorConfig = {
valueType: EnumFilterValueType;
key: string;
label: string;
enumLabels: {[key: string]: string};
enumLabels: EnumLabels;
};
export type AbsoluteDateOperatorConfig = {

View File

@@ -9,11 +9,12 @@
import {Select} from 'antd';
import React from 'react';
import {EnumLabels} from './PowerSearchConfig';
type PowerSearchEnumSetTermProps = {
onCancel: () => void;
onChange: (value: string[]) => void;
enumLabels: {[key: string]: string};
enumLabels: EnumLabels;
defaultValue?: string[];
};

View File

@@ -9,11 +9,12 @@
import {Button, Select} from 'antd';
import React from 'react';
import {EnumLabels} from './PowerSearchConfig';
type PowerSearchEnumTermProps = {
onCancel: () => void;
onChange: (value: string) => void;
enumLabels: {[key: string]: string};
enumLabels: EnumLabels;
defaultValue?: string;
};
@@ -38,8 +39,8 @@ export const PowerSearchEnumTerm: React.FC<PowerSearchEnumTermProps> = ({
let longestOptionLabelWidth = 0;
Object.values(enumLabels).forEach((label) => {
if (label.length > longestOptionLabelWidth) {
longestOptionLabelWidth = label.length;
if (label.toString().length > longestOptionLabelWidth) {
longestOptionLabelWidth = label.toString().length;
}
});

View File

@@ -13,6 +13,7 @@ import {
PowerSearchConfig,
FieldConfig,
OperatorConfig,
EnumLabels,
} from './PowerSearchConfig';
import {PowerSearchContainer} from './PowerSearchContainer';
import {
@@ -31,7 +32,13 @@ import {theme} from '../theme';
import {SearchOutlined} from '@ant-design/icons';
import {getFlipperLib} from 'flipper-plugin-core';
export {PowerSearchConfig, OperatorConfig, FieldConfig, SearchExpressionTerm};
export {
PowerSearchConfig,
EnumLabels,
OperatorConfig,
FieldConfig,
SearchExpressionTerm,
};
type PowerSearchProps = {
config: PowerSearchConfig;

View File

@@ -67,6 +67,7 @@ import {
FieldConfig,
OperatorConfig,
SearchExpressionTerm,
EnumLabels,
} from '../PowerSearch';
import {
dataTablePowerSearchOperatorProcessorConfig,
@@ -154,7 +155,16 @@ export type DataTableColumn<T = any> = {
powerSearchConfig?:
| OperatorConfig[]
| 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> {
@@ -263,6 +273,73 @@ export function DataTable<T extends object>(
[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 res: PowerSearchConfig = {fields: {}};
@@ -290,6 +367,20 @@ export function DataTable<T extends object>(
} else {
columnPowerSearchOperators = column.powerSearchConfig.operators;
useWholeRow = !!column.powerSearchConfig.useWholeRow;
const inferredPowerSearchEnumLabelsForColumn =
inferredPowerSearchEnumLabels[column.key];
if (
inferredPowerSearchEnumLabelsForColumn &&
column.powerSearchConfig.inferEnumOptionsFromData
) {
columnPowerSearchOperators = columnPowerSearchOperators.map(
(operator) => ({
...operator,
enumLabels: inferredPowerSearchEnumLabelsForColumn,
}),
);
}
}
const columnFieldConfig: FieldConfig = {
@@ -305,7 +396,11 @@ export function DataTable<T extends object>(
}
return res;
}, [columns, props.enablePowerSearchWholeRowSearch]);
}, [
columns,
props.enablePowerSearchWholeRowSearch,
inferredPowerSearchEnumLabels,
]);
const renderingConfig = useMemo<TableRowRenderContext<T>>(() => {
let startIndex = 0;