From c14f0ae182e60afa50e7988185aca0da0b3e7308 Mon Sep 17 00:00:00 2001 From: Denys Oblohin Date: Fri, 21 Jul 2023 14:24:17 +0300 Subject: [PATCH] LHS by rhallerman1 #896 deformatted (#900) * LHS rhallerman1 #896 deformatted * fix lint & test * . * fix * fix sh * renderFieldSources for all ui * remove valueSources from func root * restore * wip * seamless change func to compatible one * fix * highlight fields/funcs matching fieldType * fieldSourcesPopupTitle * fix #929 * mui vs * wip import from jl/spel * ref * support now() * . * ctor args * spelImport, spelFunc * spelImportFuncs, spelImportValue for func -> value * datetime spel works perfectly * getFieldParts, getFieldPath * ' actionmate * support funcs for mongo, sql, es, string * fix tests * lint fix * chlog, readme * test #923 * fix #929 * nit * freeze config, isFieldDescendantOfField * lint and test fix * fix for #949 * fix fieldNane #609 * isOptional, defaultValue * . * optimize render; todo: filter edge cases? * remove fieldSrc from funcs * fix logs * test "change field source to func, and vice versa" * css fixes * nit * fix del group ext * lint-fix --- .eslintrc.js | 4 +- CHANGELOG.md | 5 + CONFIG.adoc | 7 +- README.md | 13 +- packages/antd/modules/config/index.jsx | 7 +- .../modules/widgets/core/FieldCascader.jsx | 45 +- .../modules/widgets/core/FieldDropdown.jsx | 16 +- .../antd/modules/widgets/core/FieldSelect.jsx | 10 +- .../modules/widgets/core/FieldTreeSelect.jsx | 11 +- .../antd/modules/widgets/value/Boolean.jsx | 2 +- packages/antd/modules/widgets/value/Date.jsx | 2 +- .../antd/modules/widgets/value/DateTime.jsx | 2 +- .../modules/widgets/value/MultiSelect.jsx | 2 +- .../antd/modules/widgets/value/Number.jsx | 2 +- packages/antd/modules/widgets/value/Range.jsx | 2 +- .../antd/modules/widgets/value/Select.jsx | 2 +- .../antd/modules/widgets/value/Slider.jsx | 2 +- packages/antd/modules/widgets/value/Text.jsx | 2 +- .../antd/modules/widgets/value/TextArea.jsx | 2 +- packages/antd/modules/widgets/value/Time.jsx | 2 +- .../antd/modules/widgets/value/TreeSelect.jsx | 2 +- packages/antd/scripts/build-npm.sh | 5 +- packages/antd/scripts/fix-antd.js | 38 + packages/antd/styles/fixes.scss | 24 +- packages/antd/styles/vars.scss | 2 +- packages/bootstrap/modules/config/index.jsx | 1 + .../widgets/core/BootstrapFieldSelect.jsx | 17 +- packages/core/modules/actions/rule.js | 18 +- packages/core/modules/config/default.js | 3 + packages/core/modules/config/funcs.js | 54 +- packages/core/modules/config/index.js | 137 +- packages/core/modules/export/elasticSearch.js | 3 +- packages/core/modules/export/jsonLogic.js | 91 +- packages/core/modules/export/mongoDb.js | 105 +- packages/core/modules/export/queryBuilder.js | 3 +- packages/core/modules/export/queryString.js | 82 +- packages/core/modules/export/spel.js | 157 ++- packages/core/modules/export/sql.js | 72 +- packages/core/modules/import/jsonLogic.js | 306 +++-- packages/core/modules/import/spel.js | 1119 +++++++++++------ packages/core/modules/index.d.ts | 75 +- packages/core/modules/stores/constants.js | 1 + packages/core/modules/stores/tree.js | 150 ++- packages/core/modules/utils/autocomplete.js | 6 +- .../core/modules/utils/configSerialize.js | 30 +- packages/core/modules/utils/configUtils.js | 286 ++++- packages/core/modules/utils/defaultUtils.js | 20 +- packages/core/modules/utils/funcUtils.js | 229 ++-- packages/core/modules/utils/ruleUtils.js | 219 +++- packages/core/modules/utils/stuff.js | 49 + packages/core/modules/utils/validation.js | 77 +- packages/examples/demo/config.tsx | 40 +- packages/examples/demo/index.tsx | 1 + packages/fluent/modules/config/index.jsx | 1 + .../widgets/core/FluentUIFieldSelect.jsx | 11 +- packages/material/modules/config/index.jsx | 1 + .../core/MaterialFieldAutocomplete.jsx | 23 +- .../widgets/core/MaterialFieldSelect.jsx | 7 +- .../widgets/value/MaterialAutocomplete.jsx | 3 +- packages/mui/modules/config/index.jsx | 1 + .../widgets/core/MuiFieldAutocomplete.jsx | 23 +- .../modules/widgets/core/MuiFieldSelect.jsx | 7 +- .../modules/widgets/core/MuiValueSources.jsx | 67 +- .../modules/widgets/value/MuiAutocomplete.jsx | 5 +- .../mui/modules/widgets/value/MuiText.jsx | 20 +- packages/sandbox/src/demo/config.tsx | 1 + packages/sandbox/src/demo/demo.tsx | 2 +- .../components/demo/config_ctx.tsx | 2 +- packages/sandbox_next/lib/config_base.ts | 2 + packages/sandbox_simple/src/demo/config.jsx | 1 + packages/tests/specs/Basic.test.ts | 17 + packages/tests/specs/Compress.test.ts | 6 + packages/tests/specs/FuncAtLhs.test.ts | 378 ++++++ .../tests/specs/InteractionsVanilla.test.js | 2 +- packages/tests/specs/QueryWithFunc.test.js | 6 +- packages/tests/specs/RareFeatures.test.js | 110 ++ packages/tests/specs/SwitchCase.test.ts | 8 +- packages/tests/specs/WidgetsMaterial.test.ts | 8 + packages/tests/specs/WidgetsMui.test.ts | 8 + packages/tests/support/configs.js | 100 +- packages/tests/support/inits.js | 82 +- packages/tests/support/utils.tsx | 34 +- packages/tests/support/zipConfigs.tsx | 10 +- .../components/containers/GroupContainer.jsx | 18 +- .../components/containers/RuleContainer.jsx | 23 +- packages/ui/modules/components/item/Group.jsx | 29 +- packages/ui/modules/components/item/Item.jsx | 6 - packages/ui/modules/components/item/Rule.jsx | 102 +- .../ui/modules/components/item/RuleGroup.jsx | 11 +- .../modules/components/item/RuleGroupExt.jsx | 51 +- packages/ui/modules/components/rule/Field.jsx | 64 +- .../modules/components/rule/FieldWrapper.jsx | 113 +- .../ui/modules/components/rule/FuncSelect.jsx | 96 +- .../ui/modules/components/rule/FuncWidget.jsx | 72 +- .../ui/modules/components/rule/Operator.jsx | 26 +- .../components/rule/OperatorOptions.jsx | 4 +- .../components/rule/OperatorWrapper.jsx | 10 +- .../ui/modules/components/rule/ValueField.jsx | 63 +- .../ui/modules/components/rule/Widget.jsx | 67 +- .../modules/components/rule/WidgetFactory.jsx | 20 +- .../vanilla/core/VanillaFieldSelect.jsx | 8 +- .../vanilla/core/VanillaValueSources.jsx | 2 +- packages/ui/modules/config/index.jsx | 1 + packages/ui/modules/index.d.ts | 1 + packages/ui/modules/stores/constants.js | 1 + packages/ui/styles/styles.scss | 109 +- 106 files changed, 4020 insertions(+), 1385 deletions(-) create mode 100644 packages/antd/scripts/fix-antd.js create mode 100644 packages/tests/specs/FuncAtLhs.test.ts diff --git a/.eslintrc.js b/.eslintrc.js index c0d82e843..30bde34fb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -194,8 +194,8 @@ module.exports = { "@typescript-eslint/prefer-regexp-exec": 0, "@typescript-eslint/no-empty-function": 0, "@typescript-eslint/ban-ts-comment": 0, - "@typescript-eslint/no-floating-promises": 0 - + "@typescript-eslint/no-floating-promises": 0, + "@typescript-eslint/no-non-null-assertion": 0, } }, ], diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d38b70da..73705d9e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +- 6.4.0 + - Functions can be used in LHS with `fieldSources: ["field", "func"]` in `settings` + Thanks @rhallerman1 (PR #900, #896) (issues #287, #250, #344, #336) + - Support import/export of functions for SpEL (PR #900) (issue #754) + - Fix issue with `fieldName` (PR #900) (issues #929, #609) - 6.3.0 - Allow saving and loading config from server (PR #866) (issue #817) - New utils: `compressConfig()`, `decompressConfig()` diff --git a/CONFIG.adoc b/CONFIG.adoc index 4788a2a17..02999ef87 100644 --- a/CONFIG.adoc +++ b/CONFIG.adoc @@ -274,6 +274,7 @@ const { FieldCascader, FieldDropdown, FieldTreeSelect } = AntdWidgets; widget: "func", } }, + fieldSources: ["field", "func"], locale: { moment: 'ru', antd: ru_RU, @@ -304,6 +305,8 @@ Behaviour settings: |valueSourcesInfo |`{value: {}}` |By default fields can be compared with values. + If you want to enable comparing with another fields, add `field` like in example above. + If you want to enable comparing with result of function, add `func` like in example above. +|fieldSources |`["field"]` |To enable functions in LHS, set to `["field", "func"]` +|keepInputOnChangeFieldSrc |true |Keep value entered in RHS after changing source of LHS? |showErrorMessage |false |Show error message in QueryBuilder if validateValue() in field config returns false |canReorder |true |Activate reordering support for rules and groups of rules? |canRegroup |true |Allow move rules (or groups) in/out groups during reorder? + @@ -431,6 +434,7 @@ Localization: |addRuleLabel |Add rule |addSubRuleLabel |Add sub rule |notLabel |Not +|fieldSourcesPopupTitle |Select source |valueSourcesPopupTitle |Select value source |removeRuleConfirmOptions |If you want to ask confirmation of removing non-empty rule/group, add these options. + List of all valid properties is https://ant.design/components/modal/#API[here] @@ -745,6 +749,7 @@ To enable this feature set `valueSources` of type to `['value', 'func']` (see be |mongoFormatFunc |- for MongoDB format | |Can be used instead of `mongoFunc`. Function with 1 param - args object `{ : }`, should return formatted function expression object. |jsonLogic |+ for http://jsonlogic.com[JsonLogic] | |String (function name) or function with 1 param - args object `{ : }`, should return formatted function expression for JsonLogic. |jsonLogicImport | | |Function to convert given JsonLogic expression to array of arguments of current function. If given expression can't be parsed into current function, throw an error. +|spelImport | | |Function to convert given raw SpEL value to array of arguments of current function. If given value can't be parsed into current function, throw an error. |args.* | | |Arguments of function. Config is almost same as for simple link:#configfields[fields] |args..label | |arg's key |Label to be displayed in arg's label or placeholder (if `config.settings.showLabels` is false) |args..type |+ | |One of types described in link:#configtypes[config.types] @@ -854,7 +859,7 @@ const ctx = { return (val.length < 10); }, myRenderField: (props: FieldProps, _ctx: ConfigContext) => { - if (props?.customProps["showSearch"]) { + if (props.customProps?.["showSearch"]) { return ; } else { return ; diff --git a/README.md b/README.md index b25e990af..fe728e560 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ See [live demo](https://ukrbublik.github.io/react-awesome-query-builder) * [ctx](#ctx) * [Versions](#versions) * [Changelog](#changelog) + * [Migration to 6.4.0](#migration-to-640) * [Migration to 6.3.0](#migration-to-630) * [Migration to 6.2.0](#migration-to-620) * [Migration to 6.0.0](#migration-to-600) @@ -68,10 +69,11 @@ See [live demo](https://ukrbublik.github.io/react-awesome-query-builder) - unary (is empty, is null) - 'between' (for numbers, dates, times) - complex operators like 'proximity' -- Values of fields can be compared with: +- RHS can be: - values - another fields (of same type) - - function (arguments also can be values/fields/funcs) + - functions (arguments also can be values/fields/funcs) +- LHS can be field or function - Reordering (drag-n-drop) support for rules and groups of rules - Themes: - [Ant Design](https://ant.design/) @@ -507,6 +509,13 @@ It's recommended to update your version to 6.x. You just need to change your imp ### Changelog See [`CHANGELOG`](/CHANGELOG.md) +### Migration to 6.4.0 + +If you want to enable functions in LHS, please add to `config.settings`: +```js +fieldSources: ["field", "func"], +``` + ### Migration to 6.3.0 Now config has new [`ctx`](#ctx) property. Make sure you add it to your config. diff --git a/packages/antd/modules/config/index.jsx b/packages/antd/modules/config/index.jsx index d7f4baf73..1c829b3d6 100644 --- a/packages/antd/modules/config/index.jsx +++ b/packages/antd/modules/config/index.jsx @@ -10,9 +10,9 @@ const settings = { ...BasicConfig.settings, renderField: (props, {RCE, W: {FieldSelect}}) => RCE(FieldSelect, props), - // renderField: (props, {RCE, W: {FieldDropdown}}) => RCE(FieldSelect, props), - // renderField: (props, {RCE, W: {FieldCascader}}) => RCE(FieldSelect, props), - // renderField: (props, {RCE, W: {FieldTreeSelect}}) => RCE(FieldSelect, props), + // renderField: (props, {RCE, W: {FieldDropdown}}) => RCE(FieldDropdown, props), + // renderField: (props, {RCE, W: {FieldCascader}}) => RCE(FieldCascader, props), + // renderField: (props, {RCE, W: {FieldTreeSelect}}) => RCE(FieldTreeSelect, props), renderOperator: (props, {RCE, W: {FieldSelect}}) => RCE(FieldSelect, props), // renderOperator: (props, {RCE, W: {FieldDropdown}}) => RCE(FieldDropdown, props), @@ -23,6 +23,7 @@ const settings = { renderButton: (props, {RCE, W: {Button}}) => RCE(Button, props), renderButtonGroup: (props, {RCE, W: {ButtonGroup}}) => RCE(ButtonGroup, props), renderValueSources: (props, {RCE, W: {ValueSources}}) => RCE(ValueSources, props), + renderFieldSources: (props, {RCE, W: {ValueSources}}) => RCE(ValueSources, props), renderProvider: (props, {RCE, W: {Provider}}) => RCE(Provider, props), renderConfirm: (props, {W: {confirm}}) => confirm(props), diff --git a/packages/antd/modules/widgets/core/FieldCascader.jsx b/packages/antd/modules/widgets/core/FieldCascader.jsx index 356dc4c4a..1e4382cf0 100644 --- a/packages/antd/modules/widgets/core/FieldCascader.jsx +++ b/packages/antd/modules/widgets/core/FieldCascader.jsx @@ -1,13 +1,17 @@ -import React, { PureComponent } from "react"; +import React, { Component } from "react"; import PropTypes from "prop-types"; import { Cascader, Tooltip } from "antd"; import {removePrefixPath} from "../../utils/stuff"; +import { Utils } from "@react-awesome-query-builder/ui"; +const { useOnPropsChanged } = Utils.ReactUtils; +const { getFieldParts } = Utils.ConfigUtils; -export default class FieldCascader extends PureComponent { +export default class FieldCascader extends Component { static propTypes = { config: PropTypes.object.isRequired, customProps: PropTypes.object, + errorText: PropTypes.string, items: PropTypes.array.isRequired, placeholder: PropTypes.string, selectedKey: PropTypes.string, @@ -22,6 +26,36 @@ export default class FieldCascader extends PureComponent { setField: PropTypes.func.isRequired, }; + constructor(props) { + super(props); + useOnPropsChanged(this); + this.onPropsChanged(props); + } + + onPropsChanged(nextProps) { + const { items } = nextProps; + this.items = this.getItems(items); + } + + getItems(items) { + return items.map(item => { + const {items, matchesType, label} = item; + + if (items) { + return { + ...item, + items: this.getItems(items), + label: matchesType ? {label} : label, + }; + } else { + return { + ...item, + label: matchesType ? {label} : label, + }; + } + }); + } + onChange = (keys) => { const { parentField } = this.props; const dotNotationToPath = str => str.split("."); @@ -38,7 +72,7 @@ export default class FieldCascader extends PureComponent { render() { const { - config, customProps, items, placeholder, + config, customProps, items, placeholder, errorText, selectedPath, selectedLabel, selectedOpts, selectedAltLabel, selectedFullLabel, readonly, selectedField, parentField, } = this.props; let customProps2 = {...customProps}; @@ -49,12 +83,13 @@ export default class FieldCascader extends PureComponent { } const {fieldSeparator} = config.settings; - const parentFieldPath = parentField ? parentField.split(fieldSeparator) : []; + const parentFieldPath = getFieldParts(parentField, config); const value = removePrefixPath(selectedPath, parentFieldPath); let res = ( { - const {items, key, path, label, fullLabel, altLabel, tooltip, disabled} = field; + const {items, key, path, label, fullLabel, altLabel, tooltip, disabled, matchesType} = field; const pathKey = path || key; - const option = tooltip ? {label} : label; + const optionText = matchesType ? {label} : label; + const option = tooltip ? {optionText} : optionText; if (items) { return {togglerLabel} ; @@ -76,7 +80,7 @@ export default class FieldDropdown extends PureComponent { render() { const { - config, customProps, items, placeholder, + config, customProps, items, placeholder, errorText, selectedKeys, selectedLabel, selectedOpts, readonly, selectedAltLabel, selectedFullLabel, } = this.props; @@ -87,14 +91,14 @@ export default class FieldDropdown extends PureComponent { //size={config.settings.renderSize} selectedKeys={selectedKeys} onClick={this.onChange} - {...customProps} + {...omit(customProps, ["showSearch"])} >{fieldMenuItems} ); const togglerLabel = selectedAltLabel || selectedLabel || placeholder; let tooltipText = selectedFullLabel; if (tooltipText == selectedLabel) tooltipText = null; - const fieldToggler = this.renderMenuToggler(togglerLabel, tooltipText, config, readonly); + const fieldToggler = this.renderMenuToggler(togglerLabel, tooltipText, config, readonly, errorText); return readonly ? fieldToggler : ( {fieldSelectItems} ); @@ -80,7 +82,7 @@ export default class FieldSelect extends PureComponent { renderSelectItems(fields, level = 0) { return fields.map(field => { - const {items, key, path, label, fullLabel, altLabel, tooltip, grouplabel, disabled} = field; + const {items, key, path, label, fullLabel, altLabel, tooltip, grouplabel, disabled, matchesType} = field; const groupPrefix = level > 0 ? "\u00A0\u00A0".repeat(level) : ""; const prefix = level > 1 ? "\u00A0\u00A0".repeat(level-1) : ""; const pathKey = path || key; @@ -96,7 +98,9 @@ export default class FieldSelect extends PureComponent { const list = complexItems.length ? this.renderSelectItems(complexItems, level+1) : []; return [...gr, ...list]; } else { - const option = tooltip ? {prefix+label} : prefix+label; + const optionText = matchesType ? {prefix+label} : prefix+label; + const option = tooltip ? {optionText} : optionText; + return