Problem/Motivation
When a dependency's condition is Filled (!empty) or Emptied (empty) and the dependee is an optional (non-required), single-value select widget (e.g. an optional entity reference field using options_select), the condition never evaluates correctly, regardless of what the user actually selects.

ConditionalFieldsFormHelper::getState() builds the JS state constraint for any condition other than value like this:

$state = [$options['state'] => [$options['selector'] => [$options['condition'] => TRUE]]];
For !empty/empty this becomes {selector: {'!empty': true}} (or {'empty': true}), which Drupal core's states.js evaluates via a hardcoded check: val() === ''.

But Drupal core's own OptionsWidgetBase::formElement() always uses the literal string _none as the "nothing selected" option value for optional single-value select widgets:

$options = ['_none' => $empty_label] + $options;
_none is the fixed value regardless of what label text is configured ($empty_label is just the display text — "- None -", "- Select a value -", etc. — never the value). Since _none !== '', core's states.js permanently evaluates the field as "not empty," even when the user hasn't actually selected anything. The condition is effectively always true (for !empty) or always false (for empty).

Steps to reproduce
Create two fields on a bundle: Field A (any widget type) and Field B (an optional, single-value entity reference field using the options_select widget).
Add a Conditional Fields dependency: Field A's state is Required, dependee is Field B, condition is Filled (!empty).
Load the entity add form with both fields empty.
Observe: Field A is shown/enforced as required regardless of whether Field B actually has a value selected. Selecting or clearing Field B's value has no effect on Field A's required state — the condition never reflects the real selection state.
Proposed resolution
Detect this specific case in getState() — dependee element is a select widget and is not required — and emit a value/regex constraint instead of the plain empty/!empty constraint:

For !empty: {selector: {value: {regex: '^(?!_none$).+'}}}
For empty: {selector: {value: {regex: '^(_none)?$'}}}
Drupal core's states.js evaluates value/regex constraints via direct string comparison against the field's actual value, not through the special-cased empty-check path, so this correctly detects a real selection vs. the _none placeholder regardless of the configured empty-option label text.

Patch attached: conditional_fields-none-select-empty-check.patch, targeting src/ConditionalFieldsFormHelper.php. Verified against 4.0.0-alpha6, with the dependentValidate() early-return fix from #3467267 also applied.

Remaining tasks
Review and testing from a maintainer/other users.
Add automated test coverage for a dependency with an optional select dependee and a !empty/empty condition.
Confirm whether the same fix should also apply to checked/!checked conditions on other widget types with a similar "unselected sentinel value" pattern (not addressed by this patch, which targets select widgets specifically).
User interface changes
None. This only fixes the evaluation of an existing condition type; no new UI options are added.

API changes
None. ConditionalFieldsFormHelper::getState()'s method signature and public behavior for every other condition/widget combination are unchanged. The change is additive: only !empty/empty conditions on non-required select dependees take the new code path.

Data model changes
None.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

heshamkh created an issue. See original summary.

heshamkh’s picture

Attached patch fixes !empty/empty conditions on optional select dependees. Root cause: getState() builds a plain empty/!empty constraint, which core's states.js checks via val() === '' — but Drupal core's OptionsWidgetBase always uses the literal string _none for an optional select's unselected value, never '', so the condition never actually reflected the real selection. Patch detects this case (dependee is a non-required select) and emits a value/regex constraint instead, which core evaluates via direct string comparison rather than the broken empty-check path. No API changes — only this specific condition/widget combination is affected. Tested against 4.0.0-alpha6 with #3467267's patch also applied.

tribekk made their first commit to this issue’s fork.