Files
Josh Hunt 2f2be24e01 Chore: Remove unnecessary type assertions (#78009)
* Chore: Remove unnecessary type assertions

* betterer
2023-11-14 09:17:29 +00:00

327 lines
8.1 KiB
TypeScript

import {
AnyObject,
BasicConfig,
Config,
JsonTree,
Operator,
Settings,
SimpleField,
SqlFormatOperator,
Utils,
ValueSource,
Widgets,
} from '@react-awesome-query-builder/ui';
import { List } from 'immutable';
import { isString } from 'lodash';
import React from 'react';
import { dateTime, toOption } from '@grafana/data';
import { Button, DateTimePicker, Input, Select } from '@grafana/ui';
const buttonLabels = {
add: 'Add',
remove: 'Remove',
};
export const emptyInitTree: JsonTree = {
id: Utils.uuid(),
type: 'group',
};
const TIME_FILTER = 'timeFilter';
const macros = [TIME_FILTER];
export const widgets: Widgets = {
...BasicConfig.widgets,
text: {
...BasicConfig.widgets.text,
factory: function TextInput(props) {
return (
<Input
value={props?.value || ''}
placeholder={props?.placeholder}
onChange={(e) => props?.setValue(e.currentTarget.value)}
/>
);
},
},
number: {
...BasicConfig.widgets.number,
factory: function NumberInput(props) {
return (
<Input
value={props?.value}
placeholder={props?.placeholder}
type="number"
onChange={(e) => props?.setValue(Number.parseInt(e.currentTarget.value, 10))}
/>
);
},
},
datetime: {
...BasicConfig.widgets.datetime,
factory: function DateTimeInput(props) {
if (props?.operator === Op.MACROS) {
return (
<Select
id={props.id}
aria-label="Macros value selector"
menuShouldPortal
options={macros.map(toOption)}
value={props?.value}
onChange={(val) => props.setValue(val.value)}
/>
);
}
const dateValue = dateTime(props?.value).isValid() ? dateTime(props?.value).utc() : undefined;
return (
<DateTimePicker
onChange={(e) => {
props?.setValue(e.format(BasicConfig.widgets.datetime.valueFormat));
}}
date={dateValue}
/>
);
},
sqlFormatValue: (val, field, widget, operator, operatorDefinition, rightFieldDef) => {
if (operator === Op.MACROS) {
if (macros.includes(val)) {
return val;
}
return undefined;
}
// This is just satisfying the type checker, this should never happen
if (
typeof BasicConfig.widgets.datetime.sqlFormatValue === 'string' ||
typeof BasicConfig.widgets.datetime.sqlFormatValue === 'object'
) {
return undefined;
}
const func = BasicConfig.widgets.datetime.sqlFormatValue;
// We need to pass the ctx to this function this way so *this* is correct
return func?.call(BasicConfig.ctx, val, field, widget, operator, operatorDefinition, rightFieldDef) || '';
},
},
};
export const settings: Settings = {
...BasicConfig.settings,
canRegroup: false,
maxNesting: 1,
canReorder: false,
showNot: false,
addRuleLabel: buttonLabels.add,
deleteLabel: buttonLabels.remove,
renderConjs: function Conjunctions(conjProps) {
return (
<Select
id={conjProps?.id}
aria-label="Conjunction"
menuShouldPortal
options={conjProps?.conjunctionOptions ? Object.keys(conjProps?.conjunctionOptions).map(toOption) : undefined}
value={conjProps?.selectedConjunction}
onChange={(val) => conjProps?.setConjunction(val.value!)}
/>
);
},
renderField: function Field(fieldProps) {
const fields = fieldProps?.config?.fields || {};
return (
<Select
id={fieldProps?.id}
width={25}
aria-label="Field"
menuShouldPortal
options={fieldProps?.items.map((f) => {
// @ts-ignore
const icon = fields[f.key].mainWidgetProps?.customProps?.icon;
return {
label: f.label,
value: f.key,
icon,
};
})}
value={fieldProps?.selectedKey}
onChange={(val) => {
fieldProps?.setField(val.label!);
}}
/>
);
},
renderButton: function RAQBButton(buttonProps) {
return (
<Button
type="button"
title={`${buttonProps?.label} filter`}
onClick={buttonProps?.onClick}
variant="secondary"
size="md"
icon={buttonProps?.label === buttonLabels.add ? 'plus' : 'times'}
/>
);
},
renderOperator: function Operator(operatorProps) {
return (
<Select
options={operatorProps?.items.map((op) => ({ label: op.label, value: op.key }))}
aria-label="Operator"
menuShouldPortal
value={operatorProps?.selectedKey}
onChange={(val) => {
operatorProps?.setField(val.value || '');
}}
/>
);
},
};
// add IN / NOT IN operators to text to support multi-value variables
const enum Op {
IN = 'select_any_in',
NOT_IN = 'select_not_any_in',
MACROS = 'macros',
}
// eslint-ignore
const customOperators = getCustomOperators(BasicConfig);
const textWidget = BasicConfig.types.text.widgets.text;
const opers = [...(textWidget.operators || []), Op.IN, Op.NOT_IN];
const customTextWidget = {
...textWidget,
operators: opers,
};
const customTypes = {
...BasicConfig.types,
text: {
...BasicConfig.types.text,
widgets: {
...BasicConfig.types.text.widgets,
text: customTextWidget,
},
},
datetime: {
...BasicConfig.types.datetime,
widgets: {
...BasicConfig.types.datetime.widgets,
datetime: {
...BasicConfig.types.datetime.widgets.datetime,
operators: [Op.MACROS, ...(BasicConfig.types.datetime.widgets.datetime.operators || [])],
},
},
},
};
export const raqbConfig: Config = {
...BasicConfig,
widgets,
settings,
operators: customOperators,
types: customTypes,
};
export type { Config };
const noop = () => '';
const isSqlFormatOp = (func: unknown): func is SqlFormatOperator => {
return typeof func === 'function';
};
function getCustomOperators(config: BasicConfig) {
const { ...supportedOperators } = config.operators;
// IN operator expects array, override IN formatter for multi-value variables
const sqlFormatInOpOrNoop = () => {
const sqlFormatOp = supportedOperators[Op.IN].sqlFormatOp;
if (isSqlFormatOp(sqlFormatOp)) {
return sqlFormatOp;
}
return noop;
};
const customSqlInFormatter = (
field: string,
op: string,
value: string | List<string>,
valueSrc: ValueSource,
valueType: string,
opDef: Operator,
operatorOptions: AnyObject,
fieldDef: SimpleField
) => {
return sqlFormatInOpOrNoop()(
field,
op,
splitIfString(value),
valueSrc,
valueType,
opDef,
operatorOptions,
fieldDef
);
};
// NOT IN operator expects array, override NOT IN formatter for multi-value variables
const sqlFormatNotInOpOrNoop = () => {
const sqlFormatOp = supportedOperators[Op.NOT_IN].sqlFormatOp;
if (isSqlFormatOp(sqlFormatOp)) {
return sqlFormatOp;
}
return noop;
};
const customSqlNotInFormatter = (
field: string,
op: string,
value: string | List<string>,
valueSrc: ValueSource,
valueType: string,
opDef: Operator,
operatorOptions: AnyObject,
fieldDef: SimpleField
) => {
return sqlFormatNotInOpOrNoop()(
field,
op,
splitIfString(value),
valueSrc,
valueType,
opDef,
operatorOptions,
fieldDef
);
};
const customOperators = {
...supportedOperators,
[Op.IN]: {
...supportedOperators[Op.IN],
sqlFormatOp: customSqlInFormatter,
},
[Op.NOT_IN]: {
...supportedOperators[Op.NOT_IN],
sqlFormatOp: customSqlNotInFormatter,
},
[Op.MACROS]: {
label: 'Macros',
sqlFormatOp: (field: string, _operator: string, value: string | List<string>) => {
if (value === TIME_FILTER) {
return `$__timeFilter(${field})`;
}
return value;
},
},
};
return customOperators;
}
// value: string | List<string> but AQB uses a different version of Immutable
// eslint-ignore
function splitIfString(value: any) {
if (isString(value)) {
return value.split(',');
}
return value;
}