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",
|
||||
"ElementsInspectorElement",
|
||||
"ElementsInspectorProps",
|
||||
"EnumLabels",
|
||||
"FieldConfig",
|
||||
"FileDescriptor",
|
||||
"FileEncoding",
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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[];
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user