James/b/updated qb branch - #4993
Conversation
- Implemented ValueSources component for selecting value sources with icons. - Created IgnoreIfUnassignedWidget to allow criteria to be ignored if unassigned components are referenced. - Enhanced styles for query builder components, improving layout and visual consistency.
…into teboho/e/4346
- Introduced a set of JsonLogic operators to identify valid expression nodes. - Implemented isJsonLogicNode function to check if a value is a JsonLogic node. - Updated deepMergeValues to replace JsonLogic nodes as a whole during merging.
- Implemented mustache expression evaluation in utils.ts, allowing for dynamic string interpolation using mappings. - Introduced new utility functions for building expression contexts and evaluating mustache templates. - Added a new file for mustache expression functions, defining various string, date, and number manipulation functions. - Created a test setup file to mock window.matchMedia for testing environments.
… mustache expression widget
Optional body: replace ValueSources usage with new shared SourceSelector remove duplicated packed source trigger code and re-export ValueSources from sourceSelector align query-builder source trigger, rule cell, and input styles for consistent compact layout simplify mustache expression widget wrapper to fit updated value-cell flex behavior
…er box. I removed the left padding from styles.ts.
- Add mustacheExpressionFunc with strict-boolean return type for field-side fx rules (standalone boolean expression, no operator/value) - Register MUSTACHE_EXPRESSION func in query builder config - Add full CSS chain (display: contents) for field-source expression editor inside rule--field-wrapper - Source selector: hide func option on field-source side; always compact icon-only style - Field autocomplete: borderless variant to match pill container styling - Fix TypeScript build errors: renderSize, parentFieldCanReorder, handleDraggerMouseDown, JsonTree/IPlainTreeNode, labelYes/labelNo, ExpressionContextTree, singleValueTitle
- Add CustomQueryBuilder: a fully custom render of groups, rules, drag-and-drop, and relation selectors, replacing the library's default rendering pipeline - Remove renderFunc, renderField, renderFieldSources, renderValueSources, renderButton, renderItem, renderConjs, renderBeforeWidget, renderBeforeActions, renderAfterActions settings overrides — all now handled inside CustomQueryBuilder - Simplify index.tsx to settings-only config; remove all button/item render helpers - Remove qb-logic-heading heading element from queryBuilderContent (moved into custom builder) - Update styles for custom builder rule/group layout, drag handles, relation selectors, and field wrapper - Field source selector: remove func option on field (LHS) side - Query builder field component: minor prop adjustment
…expression editor polish - Zero out compact_styles residual padding/margin on .rule and .group--children to fix double-spacing between rules (gap now comes solely from filter-body/group-children gap: 10px) - Remove background from item rail so delete/drag icons render directly on page background - Boolean segmented widget restyled: transparent track, active item as filled blue pill, inactive as plain text, thumb hidden - Custom builder: expose canDelete/canDrag props on QueryBuilderItem and pass through to rail; align group action buttons to icon-only 32×32px - PropertySelect and expression editor: selection-item overflow fixes, min-width constraints on select slots
- Replace antd Segmented with a plain BoolButtonGroup component (two <button> elements) — no track background, no thumb animation - Active button renders as a filled blue rounded pill; inactive is plain text - Add sha-bool-btn-group styles: transparent container, pill-shaped active state, disabled opacity - Remove now-unused segmented overrides from styles
- Restrict boolean field valueSources to ['value'] in config, removing field/func options - Custom builder: detect boolean/strict-boolean field type and render the bool button group directly without a value-source selector or operator widget - Add sha-query-builder-boolean-value wrapper styles - Add test asserting boolean fields only allow direct value sources
- Update :has() selectors to use the exact custom builder DOM path (sha-query-builder-item-row > sha-query-builder-item-main > sha-query-builder-rule-row) so focus expansion only triggers on the correct ancestor, not on unrelated expression inputs elsewhere - Extract FLOATING_EDITOR_WIDTH constant in expression editor
Enables the left-hand operand of a query rule to use a mustache expression via a Function source option, mirroring the value-side UX exactly — same shell layout, expression editor, ignore-if-unassigned checkbox, and focus-expand behaviour. Switching back to Field source resets the rule in-place to a blank state.
At 3+ levels of group nesting the fixed-width grid columns leave no room for the value widget. Switches to flex-wrap at path depth ≥ 4 so field, operator, and value always remain fully visible regardless of nesting depth.
…it fixes Backend: Expression.Constant now preserves the actual JValue type instead of boxing everything as object — fixes !! and ! on strings, enables correct GUID/numeric/DateTime conversion paths, and adds an evaluate operator handler. Null comparisons on string members now check for both null and whitespace (is empty / is not empty semantics). Frontend: unify is_empty/is_not_empty into is_null/is_not_null with user-facing labels "is empty"/"is not empty", fix field-func round-tripping (parseFieldFuncExpression type guard), hide value column for unary operators, guard refListDropdown against missing field settings, and avoid mutating shared AntdConfig.operators.
Guard against missing ConfigUtils methods (getOperatorsForField, getValueSourcesForFieldOp, getWidgetForFieldOp, etc.) by checking availability before calling and falling back to field/type/config-level
… component for the root group Removing unused spacer/empty CSS classes and simplifying surface layout Fixing value editor slot sizing (flex, width, padding) and boolean button group styling Tightening expression editor padding/borders for inline use in query builder
… visual polish
- Enforce max group nesting depth (3 levels) with disabled "Add Group" buttons and drag validation
- Replace item rail with inline action buttons (delete/drag) inside a new item-shell grid layout
- Restyle group cards with heading lbels ("All/Any of the following...") and compact dropdown for add actions
- Add focus-ring styling for input controls and CSS variable for prefix column width
- Dynamically widen query builder modal based on JsonLogic group depth
- Make floating expression editor width adapt to anchor element width
c7328be to
b47a305
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@shesha-reactjs/src/components/queryBuilder/customBuilder/index.tsx`:
- Around line 1095-1098: The canDelete and canDrag variables currently only
check the readOnly flag, but should use the same group-readonly gate as relation
editing controls to ensure consistency with immutableGroupsMode. Replace the
readOnly checks in both canDelete and canDrag definitions with
getGroupReadonly(config, readOnly) to match the gating logic used in relation
editing. Apply this same change to the second occurrence of similar logic at
lines 1121-1126 to ensure all structural actions (delete, drag, add, and
relation editing) use the unified group-readonly state.
In `@shesha-reactjs/src/components/queryBuilder/queryBuilderContent.tsx`:
- Around line 63-64: Replace the type cast in the `children1` variable access
with a type guard function that validates the value has a size property before
accessing it. Create a reusable type guard function like `hasSize` that checks
if the value is an object with a numeric size property, then use that guard in
the `hasRules` boolean expression instead of the `as { size: number }` cast. For
line 81, replace the chained type casts in the `QbUtils.getTree()` call (the
pattern `as unknown as IPlainTreeNode) as unknown as JsonTree`) with an explicit
type guard function that properly validates the structure before accessing it,
or update the type contracts for `normalizeTreeForJsonLogic` to accept the
concrete types returned by `QbUtils.getTree()` to eliminate the need for
multiple casts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 54bb1a3c-17f0-4ffe-a479-b8f830955caf
⛔ Files ignored due to path filters (1)
shesha-reactjs/eslint.config.mjsis excluded by none and included by none
📒 Files selected for processing (10)
shesha-reactjs/src/components/queryBuilder/conjunctionSelect/index.tsxshesha-reactjs/src/components/queryBuilder/customBuilder/index.tsxshesha-reactjs/src/components/queryBuilder/groupDragAction/index.tsxshesha-reactjs/src/components/queryBuilder/itemWithRelation/index.tsxshesha-reactjs/src/components/queryBuilder/queryBuilderContent.tsxshesha-reactjs/src/components/queryBuilder/ruleDragHandlePlaceholder/index.tsxshesha-reactjs/src/components/queryBuilder/sourceSelector/index.tsxshesha-reactjs/src/components/queryBuilder/treeRelations.tsshesha-reactjs/src/components/queryBuilder/widgets/booleanButtonSelect/index.tsxshesha-reactjs/src/components/queryBuilder/widgets/refListDropDown/index.tsx
Adopt main's strict tsconfig (exactOptionalPropertyTypes, noPropertyAccessFromIndexSignature, noUncheckedIndexedAccess) across the query-builder components: conditional spreads for optional props, bracket access for index signatures, narrowing/guards for possibly-undefined values, and React.JSX.Element type references. eslint and tsc -p tsconfig.rollup.json are clean for all touched files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
shesha-reactjs/src/components/queryBuilder/treeRelations.ts (1)
110-118:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve existing group negation during relation normalization.
This rewrite always exports
not: false, so a loaded or edited negated group that also contains inline relations loses its negation beforejsonLogicFormatserializes the filter.🐛 Proposed fix
+ const exportedProperties = strippedProperties ?? {}; + return { ...normalizedNode, properties: { - ...stripRelationProperty(node.properties), + ...exportedProperties, conjunction: clauseNodes.length > 1 ? 'OR' : 'AND', - not: false, + not: exportedProperties.not === true, }, children1: clauseNodes, };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/treeRelations.ts` around lines 110 - 118, The issue is that the normalization logic in the return statement is hardcoding `not: false`, which overwrites any existing negation state from the original node and causes negated groups containing inline relations to lose their negation. To fix this, preserve the original negation value from the input node by replacing the hardcoded `not: false` with the actual negation state from the node object (access the negation property from the original node parameter to retain its current value).shesha-reactjs/src/designer-components/queryBuilder/queryBuilderField.tsx (2)
72-80:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBlock OK while the JSON editor has a parse error.
After
applyJsonfails,jsonInputErroris set but OK still commits the previousdraftLogicand closes the modal, silently discarding the invalid JSON the user is looking at.🐛 Proposed fix
const onOkClick = (): void => { if (builderErrors.length > 0) { setActiveTab(queryBuilderTabKey); return; } + + if (jsonInputError) { + setActiveTab(jsonInputTabKey); + return; + } if (props.onChange) props.onChange(draftLogic ?? null); @@ )} onOk={onOkClick} - okButtonProps={{ hidden: readOnly }} + okButtonProps={{ hidden: readOnly, disabled: builderErrors.length > 0 || Boolean(jsonInputError) }} onCancel={() => setModalVisible(false)}Also applies to: 116-119, 230-231
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/designer-components/queryBuilder/queryBuilderField.tsx` around lines 72 - 80, The onOkClick function does not check for JSON parsing errors before committing changes and closing the modal. Add a check for jsonInputError in the onOkClick function (around lines 72-80) alongside the existing builderErrors check. If jsonInputError exists, set the active tab to the appropriate JSON editor tab key and return early to prevent the modal from closing and discarding the invalid JSON the user is viewing. Apply the same fix to the other locations mentioned (lines 116-119 and 230-231) where OK/confirmation actions occur.
187-206:⚠️ Potential issue | 🟡 MinorKeep the JSON preview Collapse consistently controlled.
The Collapse switches between controlled and uncontrolled modes:
activeKeyis only passed whenjsonExpandedis true. This inconsistency can cause React warnings and unpredictable state synchronization. Additionally, sinceactionButtonsis used as the panel label, clicking them may unintentionally toggle the preview due to event propagation. The expand icon spans haveonClickhandlers that are redundant with a properonChangecallback.Pass
activeKeyin both states and implement anonChangehandler to keep the component consistently controlled. Also wrapactionButtonsin the label withstopPropagation()to prevent unintended toggling when interacting with action buttons.Suggested approach
- const onExpandClick = (): void => { - setJsonExpanded(!jsonExpanded); + const onCollapseChange = (key: string | string[]): void => { + const activeKeys = Array.isArray(key) ? key : [key]; + setJsonExpanded(activeKeys.includes('1')); }; @@ <Collapse className={styles.shaQueryBuilderField} - {...(jsonExpanded ? { activeKey: '1' } : {})} + activeKey={jsonExpanded ? ['1'] : []} + onChange={onCollapseChange} expandIconPlacement="end" bordered={false} ghost={true} expandIcon={({ isActive }) => isActive ? ( - <span onClick={onExpandClick}> + <span> hide json <CaretRightOutlined rotate={90} /> </span> ) : ( - <span onClick={onExpandClick}> + <span> show json <CaretRightOutlined rotate={0} /> </span> )} items={[ { key: '1', - label: actionButtons, + label: <div onClick={(event) => event.stopPropagation()}>{actionButtons}</div>,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/designer-components/queryBuilder/queryBuilderField.tsx` around lines 187 - 206, The Collapse component in queryBuilderField.tsx is inconsistently controlled, switching between controlled and uncontrolled modes based on jsonExpanded state, which causes React warnings. The activeKey prop should always be passed (either with value '1' when jsonExpanded is true or empty/undefined when false) to keep the component consistently controlled. Replace the redundant onClick handlers in the expand icon spans with a proper onChange callback that updates the jsonExpanded state. Additionally, wrap the actionButtons label content with event.stopPropagation() to prevent unintended Collapse toggling when users interact with those action buttons.shesha-reactjs/src/components/queryBuilder/widgets/refListDropDown/index.tsx (1)
12-33:⚠️ Potential issue | 🟡 MinorRemove the type cast from the factory function.
The explicit cast
as FactoryWithContext<NumberWidgetProps>on line 33 is unnecessary and masks potential type mismatches. The widget type annotation (RefListDropdownWidgetType) on line 8 should already enforce the correct factory signature. Removing the cast aligns with the established pattern in the codebase where other widgets rely on the type annotation without casting (seeentityAutocomplete,field,booleanButtonSelect, etc.).Per coding guidelines: "Prefer type guards over type casting for type checking" and "Leverage TypeScript to its full potential as a type system."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/widgets/refListDropDown/index.tsx` around lines 12 - 33, Remove the unnecessary type cast `as FactoryWithContext<NumberWidgetProps>` from the end of the factory function definition. The factory function assigned to the factory property already has its type enforced by the `RefListDropdownWidgetType` annotation, so the explicit cast is redundant. This aligns with the pattern used by other widgets in the codebase like `entityAutocomplete`, `field`, and `booleanButtonSelect` which rely on their type annotations without casting.Source: Coding guidelines
shesha-reactjs/src/components/queryBuilder/fieldAutocomplete/index.tsx (1)
37-69:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPreserve the caller’s
readonlystate.
QueryRuleRowpassesreadonly: fieldReadonly, but this component ignores it and only checksimmutableFieldsMode, so read-only builders can still expose an editable field selector.🐛 Proposed fix
- const readOnly = isDefined(config) && config.settings.immutableFieldsMode === true; + const readOnly = Boolean( + props.readonly || + (isDefined(config) && config.settings.immutableFieldsMode === true), + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/fieldAutocomplete/index.tsx` around lines 37 - 69, The component is not respecting the readonly state passed by the caller (QueryRuleRow). The readOnly variable currently only checks config.settings.immutableFieldsMode but ignores any readonly prop passed to this component. Add readonly as a prop to the component and update the readOnly variable assignment to combine both the readonly prop from the caller and the immutableFieldsMode setting using logical OR, so that readOnly is true if either condition is met. This ensures that when the caller explicitly passes readonly, the field selector respects that constraint regardless of the immutableFieldsMode setting.shesha-reactjs/src/components/queryBuilder/funcSelect/index.tsx (1)
16-118: 🧹 Nitpick | 🔵 TrivialRemove the redundant type assertion on the FuncSelect export.
The export declaration at line 16 already specifies the type as
FactoryWithContext<FieldProps>. The trailingas FactoryWithContext<FieldProps>cast at line 118 is unnecessary and conflicts with the coding guideline to prefer type guards over type casting.♻️ Proposed refactor
-export const FuncSelect: FactoryWithContext<FieldProps> = ((props) => { +export const FuncSelect: FactoryWithContext<FieldProps> = (props) => { const onChange = (key: string | undefined): void => { props.setField(key ?? ""); }; @@ -}) as FactoryWithContext<FieldProps>; +};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/funcSelect/index.tsx` around lines 16 - 118, The FuncSelect export declaration has a redundant type assertion. The type FactoryWithContext<FieldProps> is already explicitly declared in the variable declaration on line 16, so the trailing as FactoryWithContext<FieldProps> type cast at the end of the function definition is unnecessary and should be removed. Simply remove the as FactoryWithContext<FieldProps> cast at the end of the arrow function closing parenthesis while keeping the type annotation on the variable declaration itself.Source: Coding guidelines
shesha-reactjs/src/components/queryBuilder/itemWithRelation/index.tsx (1)
121-139:⚠️ Potential issue | 🟡 MinorReplace assertions with type guards for tree operations (lines 134–138); remove redundant assertion at line 123.
Line 123 cast is redundant—
definitionis already typed as{ label?: string }from theRecordvalue type. Remove it.Lines 134 and 138 rely on assertions for
tree. Replace with a type guard:♻️ Type-guard refactor
+type TreeWithSetIn = { + setIn: (path: (string | number)[], val: unknown) => unknown; +}; + +const hasSetIn = (value: unknown): value is TreeWithSetIn => + typeof value === 'object' && + value !== null && + 'setIn' in value && + typeof value.setIn === 'function'; + const conjunctionOptions = Object.entries(config.conjunctions ?? {}).map(([key, definition]) => ({ value: key, - label: (definition as { label?: string }).label ?? key, + label: definition.label ?? key, })); const selectOptions = conjunctionOptions; @@ const handleChange = (value: string): void => { - if (!tree || typeof (tree as { setIn?: unknown }).setIn !== 'function' || !actions.setTree) + if (!hasSetIn(tree) || !actions.setTree) return; const nodeTreePath = getNodeTreePath(itemPath); - const updatedTree = (tree as { setIn: (path: (string | number)[], val: unknown) => unknown }).setIn( + const updatedTree = tree.setIn( [...nodeTreePath, 'properties', '__relation'], value, );Aligns with TypeScript best practices: prefer type guards over assertions, and avoid redundant casts when types are already inferred.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/itemWithRelation/index.tsx` around lines 121 - 139, Remove the redundant type assertion on the definition variable in the conjunctionOptions.map() call, as definition is already properly typed as {label?: string} from the Record value type. In the handleChange function, replace the multiple type assertions for tree operations with a proper type guard that checks if tree is an object with a setIn function, narrows the type accordingly, and then uses that narrowed type for the subsequent setIn call instead of recasting with assertions. This improves type safety and aligns with TypeScript best practices.Source: Coding guidelines
♻️ Duplicate comments (1)
shesha-reactjs/src/components/queryBuilder/conjunctionSelect/index.tsx (1)
46-46:⚠️ Potential issue | 🟠 MajorReplace unsafe non-null assertion with optional chaining, consistent with other queryBuilder components.
Line 46 uses
config!(non-null assertion) and repeated type casting, which bypasses nullability checks and contradicts the pattern used elsewhere in the queryBuilder folder. ThefuncSelectcomponent (line 53) correctly uses optional chaining:config?.settings.renderSize. Adopt the same safe pattern here to prevent potential runtime crashes and align with the project guideline: "Prefer Type Guards Over Type Casting."Suggested patch
- size={(config!.settings as SettingsWithRenderSize).renderSize === 'medium' ? 'middle' : (config!.settings as SettingsWithRenderSize).renderSize as 'small' | 'large' | undefined} + size={config?.settings?.renderSize === 'medium' ? 'middle' : config?.settings?.renderSize}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-reactjs/src/components/queryBuilder/conjunctionSelect/index.tsx` at line 46, Replace the unsafe non-null assertion operator on the `size` prop configuration in the conjunctionSelect component. Instead of using `config!.settings` with repeated type casting to SettingsWithRenderSize, use safe optional chaining with `config?.settings` to access the renderSize property, matching the pattern already established in the funcSelect component on line 53. This ensures type safety and prevents potential runtime crashes when config might be undefined or null.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@shesha-reactjs/src/components/queryBuilder/fieldAutocomplete/index.tsx`:
- Around line 37-69: The component is not respecting the readonly state passed
by the caller (QueryRuleRow). The readOnly variable currently only checks
config.settings.immutableFieldsMode but ignores any readonly prop passed to this
component. Add readonly as a prop to the component and update the readOnly
variable assignment to combine both the readonly prop from the caller and the
immutableFieldsMode setting using logical OR, so that readOnly is true if either
condition is met. This ensures that when the caller explicitly passes readonly,
the field selector respects that constraint regardless of the
immutableFieldsMode setting.
In `@shesha-reactjs/src/components/queryBuilder/funcSelect/index.tsx`:
- Around line 16-118: The FuncSelect export declaration has a redundant type
assertion. The type FactoryWithContext<FieldProps> is already explicitly
declared in the variable declaration on line 16, so the trailing as
FactoryWithContext<FieldProps> type cast at the end of the function definition
is unnecessary and should be removed. Simply remove the as
FactoryWithContext<FieldProps> cast at the end of the arrow function closing
parenthesis while keeping the type annotation on the variable declaration
itself.
In `@shesha-reactjs/src/components/queryBuilder/itemWithRelation/index.tsx`:
- Around line 121-139: Remove the redundant type assertion on the definition
variable in the conjunctionOptions.map() call, as definition is already properly
typed as {label?: string} from the Record value type. In the handleChange
function, replace the multiple type assertions for tree operations with a proper
type guard that checks if tree is an object with a setIn function, narrows the
type accordingly, and then uses that narrowed type for the subsequent setIn call
instead of recasting with assertions. This improves type safety and aligns with
TypeScript best practices.
In `@shesha-reactjs/src/components/queryBuilder/treeRelations.ts`:
- Around line 110-118: The issue is that the normalization logic in the return
statement is hardcoding `not: false`, which overwrites any existing negation
state from the original node and causes negated groups containing inline
relations to lose their negation. To fix this, preserve the original negation
value from the input node by replacing the hardcoded `not: false` with the
actual negation state from the node object (access the negation property from
the original node parameter to retain its current value).
In
`@shesha-reactjs/src/components/queryBuilder/widgets/refListDropDown/index.tsx`:
- Around line 12-33: Remove the unnecessary type cast `as
FactoryWithContext<NumberWidgetProps>` from the end of the factory function
definition. The factory function assigned to the factory property already has
its type enforced by the `RefListDropdownWidgetType` annotation, so the explicit
cast is redundant. This aligns with the pattern used by other widgets in the
codebase like `entityAutocomplete`, `field`, and `booleanButtonSelect` which
rely on their type annotations without casting.
In `@shesha-reactjs/src/designer-components/queryBuilder/queryBuilderField.tsx`:
- Around line 72-80: The onOkClick function does not check for JSON parsing
errors before committing changes and closing the modal. Add a check for
jsonInputError in the onOkClick function (around lines 72-80) alongside the
existing builderErrors check. If jsonInputError exists, set the active tab to
the appropriate JSON editor tab key and return early to prevent the modal from
closing and discarding the invalid JSON the user is viewing. Apply the same fix
to the other locations mentioned (lines 116-119 and 230-231) where
OK/confirmation actions occur.
- Around line 187-206: The Collapse component in queryBuilderField.tsx is
inconsistently controlled, switching between controlled and uncontrolled modes
based on jsonExpanded state, which causes React warnings. The activeKey prop
should always be passed (either with value '1' when jsonExpanded is true or
empty/undefined when false) to keep the component consistently controlled.
Replace the redundant onClick handlers in the expand icon spans with a proper
onChange callback that updates the jsonExpanded state. Additionally, wrap the
actionButtons label content with event.stopPropagation() to prevent unintended
Collapse toggling when users interact with those action buttons.
---
Duplicate comments:
In `@shesha-reactjs/src/components/queryBuilder/conjunctionSelect/index.tsx`:
- Line 46: Replace the unsafe non-null assertion operator on the `size` prop
configuration in the conjunctionSelect component. Instead of using
`config!.settings` with repeated type casting to SettingsWithRenderSize, use
safe optional chaining with `config?.settings` to access the renderSize
property, matching the pattern already established in the funcSelect component
on line 53. This ensures type safety and prevents potential runtime crashes when
config might be undefined or null.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9c0a770b-e41b-406d-a805-207acacba245
📒 Files selected for processing (19)
shesha-reactjs/src/components/propertyAutocomplete/propertySelect.tsxshesha-reactjs/src/components/queryBuilder/config.tsshesha-reactjs/src/components/queryBuilder/conjunctionSelect/index.tsxshesha-reactjs/src/components/queryBuilder/customBuilder/index.tsxshesha-reactjs/src/components/queryBuilder/emptyRulePlaceholders/index.tsxshesha-reactjs/src/components/queryBuilder/fieldAutocomplete/index.tsxshesha-reactjs/src/components/queryBuilder/funcSelect/index.tsxshesha-reactjs/src/components/queryBuilder/groupEmptyState/index.tsxshesha-reactjs/src/components/queryBuilder/groupEmptyState/queryRuleElement.tsxshesha-reactjs/src/components/queryBuilder/itemPrefix/index.tsxshesha-reactjs/src/components/queryBuilder/itemWithRelation/index.tsxshesha-reactjs/src/components/queryBuilder/ruleDragHandlePlaceholder/index.tsxshesha-reactjs/src/components/queryBuilder/sourceSelector/index.tsxshesha-reactjs/src/components/queryBuilder/treeRelations.tsshesha-reactjs/src/components/queryBuilder/widgets/booleanButtonSelect/index.tsxshesha-reactjs/src/components/queryBuilder/widgets/ignoreIfUnassigned/index.tsxshesha-reactjs/src/components/queryBuilder/widgets/refListDropDown/index.tsxshesha-reactjs/src/components/refListDropDown/genericRefListDropDown.tsxshesha-reactjs/src/designer-components/queryBuilder/queryBuilderField.tsx
…pty semantics The JsonLogic2LinqConverter rewrite broadened string "is empty" (the `!` operator) from null-only to null-or-whitespace, matching the already-updated StringField_IsNotEmpty_Test. Update the StringField_IsEmpty_Convert assertion to the same intended semantics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- JsonLogic2LinqConverter: cast Int64 to int for AddMonths/AddYears (was a runtime ArgumentException), and add lower-bound to the int-overflow check plus fix typos in the message. - queryBuilder/config: drop stale 'proximity' from text field-widget operators (the operator is deleted from the operators map). - booleanButtonSelect: respect readonly (disable buttons) and stop editor-side defaulting of the value to true (preserve the controlled contract). - customBuilder: gate delete/drag on group-readonly so immutableGroupsMode locks structural actions; gate drag-start on group-readonly too. - groupDragAction: drop redundant handler cast (prop is already typed). - form/utils: evaluateComplexStringWithResult returns a proper result object for blank input instead of a bare string (the string was masked by a type-guard narrowing to never). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
shesha-core/src/Shesha.Framework/JsonLogic/JsonLogic2LinqConverter.cs (1)
685-703:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winSilent failure when
required=truebut expression is empty.When
requireddefaults totrueandexpressionis null or whitespace, the code returnsExpression.Constant(null, typeof(string))instead of throwing an error. This could lead to unexpected query results where a "required" expression silently becomes a null comparison.Consider throwing when a required expression is empty:
🐛 Proposed fix
case JsOperators.Evaluate: { var evalArg = `@operator.Arguments.FirstOrDefault`(); if (evalArg is JObject evalObj) { var expression = evalObj.Value<string>("expression"); var required = evalObj.Value<bool?>("required") ?? true; if (string.IsNullOrWhiteSpace(expression) && !required) return null; + if (string.IsNullOrWhiteSpace(expression) && required) + throw new JsonLogicParsingFailedException($"'{JsOperators.Evaluate}' node has empty expression but 'required' is true", rule); + return Expression.Constant(expression, typeof(string)); } throw new JsonLogicParsingFailedException("Invalid 'evaluate' node format", rule); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shesha-core/src/Shesha.Framework/JsonLogic/JsonLogic2LinqConverter.cs` around lines 685 - 703, In the JsOperators.Evaluate case handler, the current code only returns null when the expression is empty and required is false, but silently passes through when required is true and expression is empty. Add a validation check after extracting the expression and required values to throw a JsonLogicParsingFailedException when the expression is null or whitespace AND required is true, ensuring that required expressions with empty values are properly caught and reported as parsing errors rather than silently becoming null constants.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@shesha-reactjs/src/providers/form/utils.ts`:
- Around line 999-1000: In the early return statement within the
isNullOrWhiteSpace check, change the unevaluatedExpressions value from undefined
to an empty array to maintain type consistency with the normal return path at
line 1041. This ensures downstream consumers always receive a string array type
for unevaluatedExpressions rather than having to handle both undefined and
string[] types.
---
Outside diff comments:
In `@shesha-core/src/Shesha.Framework/JsonLogic/JsonLogic2LinqConverter.cs`:
- Around line 685-703: In the JsOperators.Evaluate case handler, the current
code only returns null when the expression is empty and required is false, but
silently passes through when required is true and expression is empty. Add a
validation check after extracting the expression and required values to throw a
JsonLogicParsingFailedException when the expression is null or whitespace AND
required is true, ensuring that required expressions with empty values are
properly caught and reported as parsing errors rather than silently becoming
null constants.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: fdf9b0f6-284f-4904-93aa-a7ab48a7a200
⛔ Files ignored due to path filters (1)
shesha-core/test/Shesha.Tests/JsonLogic/JsonLogic2LinqConverter_ValueTests.csis excluded by none and included by none
📒 Files selected for processing (6)
shesha-core/src/Shesha.Framework/JsonLogic/JsonLogic2LinqConverter.csshesha-reactjs/src/components/queryBuilder/config.tsshesha-reactjs/src/components/queryBuilder/customBuilder/index.tsxshesha-reactjs/src/components/queryBuilder/groupDragAction/index.tsxshesha-reactjs/src/components/queryBuilder/widgets/booleanButtonSelect/index.tsxshesha-reactjs/src/providers/form/utils.ts
💤 Files with no reviewable changes (1)
- shesha-reactjs/src/components/queryBuilder/config.ts
…Baloyi/shesha-framework into james/b/updated-qb-branch
Keeps the IEvaluateComplexStringResult.unevaluatedExpressions type consistent with the normal return path (always string[]), so consumers don't have to handle undefined. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IvanIlyichev
left a comment
There was a problem hiding this comment.
Hi @James-Baloyi. Please ensure all comments are resolved and marked accordingly. Also, note that the value editors on the query builder are currently broken - they trigger an infinite loop and hang the browser tab. Given that, please test all changes locally before submitting, as repeated iterations with the query builder tend to cause crashes or freezes.
The JS and Specification condition editors ran a useEffect with no dependency
array that called onChange('') whenever the value was undefined. When the
builder doesn't persist '' as a defined value the effect re-fires every render,
looping onChange → setValue → render and hanging the browser tab. Make the
default-value initialisation mount-only.
Also narrow funcSelect's auto-select effect deps from the whole props object
(new reference every render) to setField, removing a second loop risk flagged
by CodeRabbit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The props type was declared twice — in the React.FC generic and again on the destructured parameter. Drop the redundant annotation (matches RuleWidgetEditor). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Hi @James-Baloyi. Please check the following:
-
Remove all usages of as unknown as from the code. This pattern is unsafe, and we should stick to strongly typed code wherever possible. I temporarily used it in a few specific cases during the recent large refactoring just to keep things moving, but I plan to add a linter rule later to prevent these unsafe casts from recurring.
-
The horizontal scrollbar negatively affects the user experience. When configuring groups, I actually couldn't even find the drag‑and‑drop handle at first because it was hidden—and that handle is required for the configuration (see the attached image).
- Proposed changes hide functionality available in the current implementation
- Functions like
UppercaseandLowercaseare not available
- In preveious version the `between` option allows to select source type for both `from` and `to` but current implementation uses single selector for source type.
- Arguments editor is not available
- size the modal and builder to the viewport instead of a fixed 1038px, and drop the 966px cap that stopped the builder filling it - let rule rows wrap at a legible minimum rather than shrinking controls toward zero, so nesting grows downwards instead of being crushed - host every embedded control in a single control slot that owns the border, replacing per-slot reset rules that had drifted apart Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
I've addressed the first two items, and then had a chat with @ihouvet - the suggestion is to remove function altogether and replace it with expression editor. I've attached a screenshot below.
|
Hi @James-Baloyi. In the current implementation, all functions used in the query builder are translated into LINQ expressions and then, via the ORM, into SQL queries. Replacing this with evaluating Mustache expressions on the frontend is not feasible for several reasons:
Additionally, Mustache is a logic-less templating language, so it cannot accept arguments like proper functions. As I mentioned in my previous comment, relative date evaluation (e.g., now+n days) is essential for filters such as "last 5 days" and Mustache simply does not cover such use cases. PS: Next time, please use the "re-request review" function, it will help speed up the code review process. |


Summary by CodeRabbit
Release Notes
evaluateoperator).matchMediapolyfill.