-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Feat: conditional fields #7664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat: conditional fields #7664
Conversation
There was a problem hiding this comment.
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 implements conditional field visibility for the Decap CMS, allowing fields to be shown or hidden based on the values of other fields in the same collection. This addresses issue #565 and replaces PR #3891 with a simpler implementation supporting a single condition per field.
Key Changes:
- Added
Conditioninterface to TypeScript definitions allowing field conditions with operators (==,!=,>,<,>=,<=) - Implemented
calculateConditionfunction to evaluate field visibility based on conditional logic - Integrated condition checks into field rendering and validation workflows
Reviewed Changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/decap-cms-core/src/types/redux.ts | Adds TypeScript Condition interface and condition property to CmsFieldBase |
| packages/decap-cms-core/index.d.ts | Mirrors TypeScript definitions for external module consumers |
| packages/decap-cms-core/src/constants/configSchema.js | Adds JSON schema validation for the condition field configuration |
| packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControlPane.js | Implements conditional field logic in calculateCondition function and integrates it into validation and rendering |
| dev-test/config.yml | Adds test examples for conditional string and object fields |
Comments suppressed due to low confidence (1)
packages/decap-cms-core/src/components/Editor/EditorControlPane/EditorControlPane.js:98
- Avoid automated semicolon insertion (93% of all statements in the enclosing function have an explicit semicolon).
const condition = field.get('condition')
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.props.fields.forEach(field => { | ||
| if (field.get('widget') === 'hidden') return; | ||
| if (isFieldTranslatable(field, targetLocale, sourceLocale)) { | ||
| const copyValue = getFieldValue({ | ||
| field, | ||
| entry, | ||
| locale: sourceLocale, | ||
| isTranslatable: sourceLocale !== defaultLocale, | ||
| }); | ||
| if (copyValue) this.props.onChange(field, copyValue, undefined, i18n); | ||
| } | ||
| }); |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The copyFromOtherLocale function doesn't check if fields meet their condition before copying values. This could result in copying values to fields that are currently hidden due to unmet conditions. Consider adding a condition check similar to the one in the validate method and the render logic.
| type: 'object', | ||
| properties: { | ||
| field: { type: 'string' }, | ||
| value: { types: ['string', 'boolean'] }, |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema uses types (plural) instead of type (singular) for the value property. This should be type: ['string', 'boolean', 'number'] to match JSON Schema standards and be consistent with how other properties are defined in the schema.
| value: { types: ['string', 'boolean'] }, | |
| value: { type: ['string', 'boolean'] }, |
| type: 'object', | ||
| properties: { | ||
| field: { type: 'string' }, | ||
| value: { types: ['string', 'boolean'] }, |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The schema definition for the value property is missing the number type. According to the TypeScript definition in redux.ts and index.d.ts, the value should accept string | boolean | number. Add 'number' to the types array: type: ['string', 'boolean', 'number'].
| value: { types: ['string', 'boolean'] }, | |
| value: { type: ['string', 'boolean', 'number'] }, |
| const operator = condition.get('operator') || '=='; | ||
| const condValue = condition.get('value'); | ||
|
|
||
| const condField = fields.find(f => f.get('name') === condFieldName); |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The calculateCondition function doesn't handle the case where the referenced field is not found. If condField is undefined (when no field matches condFieldName), calling getFieldValue will fail. Add a null check:
const condField = fields.find(f => f.get('name') === condFieldName);
if (!condField) return false; // or true, depending on desired behavior| const condField = fields.find(f => f.get('name') === condFieldName); | |
| const condField = fields.find(f => f.get('name') === condFieldName); | |
| if (!condField) return false; |
| case '==': | ||
| return condFieldValue == condValue; | ||
| case '!=': | ||
| return condFieldValue != condValue; |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comparison operators use loose equality (== and !=) instead of strict equality (=== and !==). This can lead to unexpected type coercion. For example, "0" == 0 would be true with loose equality. Consider using strict equality operators for more predictable behavior.
| } | ||
|
|
||
| function calculateCondition({field, fields, entry, locale, isTranslatable}) { | ||
| const condition = field.get('condition') |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Missing semicolon at the end of the statement. This is inconsistent with the code style used elsewhere in the function (lines 101-103).
| const condition = field.get('condition') | |
| const condition = field.get('condition'); |
| locale, | ||
| isTranslatable, | ||
| }); | ||
| if (!isConditionMet) return |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing return value. When the condition is not met, this function returns undefined implicitly. For clarity and consistency with React rendering, explicitly return null:
if (!isConditionMet) return null;| if (!isConditionMet) return | |
| if (!isConditionMet) return null; |
- replace operator with descriptive names, - add tests, add regex, includes, oneOf
Addresses #565
Replaces #3891
This is a bit simpler than the first PR - there is only one condition possible instead of multiple conditions.
Usage:
Test plan:
test contidional widgets are added to dev-text config. Checkout this PR and
npm run develop- see Conditional string field.