Repository navigation
Description
Bug description
A custom condition that names a target field is inverted twice under unless, unless_any, hide_when and hide_when_any, so the inversions cancel and the field is shown exactly when the blueprint says to hide it (and hidden when it says to show it). There is no error.
showOnPass is false for those four keys, and resources/js/components/field-conditions/Validator.js applies it in two places on the same evaluation. On 6.x (0a58bd5ff), passesCustomCondition() inverts the custom function's result:
passesCustomCondition(condition) {
// ...
let passes = customFunction({ /* ... */ });
return this.showOnPass ? passes : !passes;
}and passesConditions() inverts again whatever the all/any pass returned:
let passes = this.passOnAny ? this.passesAnyConditions(conditions) : this.passesAllConditions(conditions);
return this.showOnPass ? passes : !passes;A targeted custom condition (unless: { some_field: 'custom myCondition' }) goes through passesAllConditions() or passesAnyConditions() → passesCondition() → passesCustomCondition(), so it is inverted once inside and once outside.
The bare-string form (unless: 'custom myCondition') is already correct and must stay so. passesConditions() returns it straight from passesCustomCondition() without reaching the outer inversion, so for that path the inner inversion is the only one:
} else if (this.isCustomConditionWithoutTarget(conditions)) {
return this.passesCustomCondition(this.prepareCondition(conditions));
}So simply deleting the inner inversion would break the bare-string form. Standard operators (is, contains, ...) are unaffected, because passesCondition() returns the raw comparison and only passesConditions() inverts it.
The same code is in the latest release, v6.34.1 (lines 55-61 and 295 of the same file).
How to reproduce
-
Register a custom condition that always passes:
Statamic.$conditions.add('alwaysTrue', () => true);
-
Add two fields to a blueprint: a text field
referral_source, and a field with a targeted custom condition underunless:- handle: notes field: type: textarea unless: referral_source: 'custom alwaysTrue'
-
The validator reports
notesas visible. It should be hidden: the condition passes, andunlessmeans hide when it passes.
For contrast, unless: 'custom alwaysTrue' (no target field) hides notes as expected, and if: { referral_source: 'custom alwaysTrue' } shows it as expected.
As a test in resources/js/tests/FieldConditionsValidator.test.js, beside "it can call a custom function on a specific field":
Statamic.$conditions.add('lovesAnimals', ({ target }) => target.length > 3);
setValues({ favorite_animals: ['cats', 'dogs', 'rats', 'bats'] });
expect(Fields.showField({ unless: { favorite_animals: 'custom lovesAnimals' } })).toBe(false);On unchanged 6.x this returns true, and hide_when, unless_any and hide_when_any do the same. With a custom condition that fails, unless returns false where it should return true.
Environment
Environment
Laravel Version: 13.24.0
PHP Version: 8.4.23
Composer Version: 2.10.2
Environment: local
Debug Mode: ENABLED
Maintenance Mode: OFF
Timezone: UTC
Locale: en
Cache
Config: NOT CACHED
Events: NOT CACHED
Routes: NOT CACHED
Views: NOT CACHED
Drivers
Broadcasting: log
Cache: file
Database: sqlite
Logs: stack / single
Mail: smtp
Queue: sync
Session: file
Storage
public/storage: NOT LINKED
Statamic
Addons: 9
License Key: Not set
Sites: 255 (names redacted)
Stache Watcher: Enabled (auto)
Static Caching: Disabled
Version: 6.26.0 PRO
Statamic Addons
el-schneider/statamic-admin-bar: 0.3.2
jacksleight/statamic-bard-mutator: 3.0.5
jacksleight/statamic-bard-texstyle: 4.1.1
mitydigital/sitemapamic: 3.5.1
statamic/eloquent-driver: 5.10.0
statamic/ssg: 4.1.0
stillat/relationships: 2.4.0
studio1902/statamic-peak-tools: 9.2.0
uams-web/wordpress-importer: 0.19.0
Statamic Eloquent Driver
Addon Settings: file
Asset Containers: file
Assets: eloquent
Blueprints: file
Collection Trees: file
Collections: file
Entries: file
Fieldsets: file
Form Submissions: file
Forms: file
Global Sets: file
Global Variables: file
Navigation Trees: file
Navigations: file
Revisions: file
Sites: file
Taxonomies: file
Terms: file
Tokens: fileInstallation
Other (please explain)
Reproduced in the statamic/cms repository's own test suite rather than in a site.
Additional details
A fix that makes passesConditions() the only place the inversion happens: passesCustomCondition() returns the custom function's result as-is, and the bare-string branch applies the inversion itself.
} else if (this.isCustomConditionWithoutTarget(conditions)) {
let passes = this.passesCustomCondition(this.prepareCondition(conditions));
return this.showOnPass ? passes : !passes;
}
Activity