Skip to content

fix: evaluate multi-choice visibility conditions as exact option membership#177

Merged
ManukMinasyan merged 1 commit into
3.xfrom
fix/multi-choice-conditional-visibility-membership
Jul 1, 2026
Merged

fix: evaluate multi-choice visibility conditions as exact option membership#177
ManukMinasyan merged 1 commit into
3.xfrom
fix/multi-choice-conditional-visibility-membership

Conversation

@ManukMinasyan

Copy link
Copy Markdown
Collaborator

Problem

Follow-up to #176 (v3.5.1), which fixed conditional-required validation for single-choice triggers but left multi-choice ones broken.

Conditional visibility contains / does not contain on option-backed choice fields (multi-select, checkbox-list) diverged between client and server:

  • Client (visibleJs) resolved the condition to option ids and substring-matched id strings.
  • Server normalized the selected ids to option names and substring-matched names.

Two failure modes:

  1. A field shown only when a multi-select contains an option still enforced its required rule while hidden — the reported bug (dependent detail fields blocking submit on Abode's forms).
  2. For substring-overlapping option names (e.g. Do vs Dog) or overlapping ids (1 vs 11), the two sides disagreed, so the gate could skip validation on a field the user can still see — silent data loss. The v3.5.1 gate avoided this only by deferring (never skipping) for these operators, which is what left mode (1) broken.

Fix

Both the client visibility expression and the server validation gate now treat contains / does not contain on option-backed choice fields as exact option membership — the selected options include (any of) the condition's options — matching how equals already works for choice fields.

  • Client: buildContainsExpression routes option-backed choice fields through the same option-membership expression as equals (conditionIds.some(id => selectedIds.includes(id))).
  • Server: VisibilityData::evaluate accepts the codes of option-backed choice fields and evaluates their contains/not_contains conditions as exact, case-insensitive membership. The form validation gate passes those codes.
  • contains/not_contains are added to CHOICE_SAFE_OPERATORS so the gate reproduces them instead of deferring.

Text fields and option-less multi-value fields (email, phone, link, record, tags-input) keep substring matching — client and server already agree there, so they are intentionally excluded.

Scope

This covers the form validation gate and the client visibility rendering (the reported bug). The server-side rendering fallback used by infolists/exports (BackendVisibilityService::isFieldVisible → cascading) is a separate surface with its own value extraction and is left unchanged.

Coverage — every field type as a condition trigger

Category Operators offered Behaviour
Text / Numeric / Date / Boolean non-choice unchanged (already reproduced)
Single-choice (select, radio, toggle-buttons) equals/not-equals exact option compare, unchanged
Multi-choice with options (multi-select, checkbox-list) contains/not-contains now exact membership on both sides
Multi-choice without options (email, phone, link, record, tags-input) contains/not-contains substring, identical on both sides

Tests

Extended ConditionalVisibilityValidationTest with multi-select and checkbox-list cases (hidden → not required, visible → required, not-contains both ways) and an overlapping-name exactness test (Do/Dog) that fails under both the old defer behaviour and a naive substring whitelist.

  • Full package suite: 735 passed (+ new cases), Pint + PHPStan clean.
  • Verified end-to-end in the consuming app (Journey) against a real multi-select Vendor form.

Copilot AI review requested due to automatic review settings July 1, 2026 14:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns client-side conditional visibility rendering (visibleJs) and server-side validation gating for multi-choice, option-backed fields by treating contains / does not contain as exact option membership (rather than substring matching). This resolves cases where dependent fields were incorrectly required while hidden, and eliminates client/server disagreement for overlapping option ids/names.

Changes:

  • Updated frontend visibility JS generation so option-backed choice-field contains uses the same option-id membership logic as equals.
  • Extended server-side visibility evaluation to optionally treat contains / not_contains as exact, case-insensitive membership for specified option-backed choice field codes, and wired this into the validation gate.
  • Expanded integration coverage to include multi-select + checkbox-list membership behavior and an overlapping-name exactness regression (Do vs Dog).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/Feature/Integration/ConditionalVisibilityValidationTest.php Adds multi-choice membership + overlapping-name regression tests for conditional visibility validation gating.
src/Services/Visibility/FrontendVisibilityService.php Routes option-backed choice contains to option-id membership JS (same semantics as equals).
src/Services/Visibility/CoreVisibilityLogicService.php Plumbs optional “membership field codes” through to VisibilityData::evaluate() for server evaluation when requested.
src/Filament/Integration/Base/AbstractFormComponent.php Expands safe operators to include contains/not_contains and passes option-backed choice field codes into the gate’s visibility evaluation.
src/Data/VisibilityData.php Implements exact, case-insensitive membership evaluation for contains/not_contains when the field code is flagged as option-backed.
CHANGELOG.md Documents the v3.5.2 fix and the prior divergence failure modes.

…ership

Conditional-visibility "contains"/"does not contain" on option-backed choice
fields (multi-select, checkbox-list) diverged between client and server: the
client compared option ids while the server substring-matched option names. A
dependent field required only when visible could stay required while hidden,
and for substring-overlapping option names (e.g. "Do" vs "Dog") the server
could skip validation on a field the user can still see.

Both the client visibility expression and the server validation gate now treat
contains/not-contains on option-backed choice fields as exact option membership
(the selected options include any of the condition's options). Text fields and
option-less multi-value fields (email, tags, ...) keep substring matching,
identical on both sides. Adds regression coverage for multi-select and
checkbox-list, including overlapping option names.
@ManukMinasyan ManukMinasyan force-pushed the fix/multi-choice-conditional-visibility-membership branch from 5c3dfe9 to 0ef6d56 Compare July 1, 2026 14:52
@ManukMinasyan ManukMinasyan merged commit a652f37 into 3.x Jul 1, 2026
4 checks passed
@ManukMinasyan ManukMinasyan deleted the fix/multi-choice-conditional-visibility-membership branch July 1, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants