Skip to content

fix(editor): enforce locked attributes, honour i18n.locale, warn on duplicate field names - #509

Merged
kevinchappell merged 5 commits into
mainfrom
fix/editor-attr-guards
Sep 28, 2026
Merged

kevinchappell merged 5 commits into
mainfrom
fix/editor-attr-guards

Conversation

@kevinchappell

Copy link
Copy Markdown
Collaborator

What

Three editor fixes.

Locked attributes stay locked (Refs #116, #159)

  • The "+ Attribute" dialog rejected disabled names but let a locked name be re-added, which overwrote its value. It now rejects locked names too, and EditPanel#addAttribute guards against both, so custom actions.add.attr handlers can't bypass it.
  • readonly doesn't stop Space on a checkbox or the arrow keys on a select. Locked checkbox, radio and select controls are now disabled; locked text inputs stay readonly.
  • Docs that described "+ Attribute" as a way to recover a locked attribute are corrected.

The configured i18n.locale is used on first load

  • loadResources passed the sessionStorage locale unconditionally. On a first visit that was null, which reset the configured locale to en-US.
  • A locale picked with editor.i18n.setLang() still wins for the tab. Otherwise the config's locale applies.

Duplicate field names get a warning (Refs #331)

  • Radio and checkbox groups now honour attrs.name, so two fields can end up submitting under one key, most often after a clone.
  • Every field that shares its name with another field in the same editor shows a hint under its name attribute (role="status", linked with aria-describedby).
  • The hint refreshes on rename, clone, remove and load.
  • New i18n key duplicateFieldName, with an English fallback until @draggable/formeo-languages ships it (branch feat/editor-followup-keys in that repo).

Testing

  • npm test: 595 pass
  • npm run lint: clean
  • Playwright: 154 passed, 3 skipped, with new specs tests/editor-i18n-locale.spec.js and tests/duplicate-field-names.spec.js, and new cases in tests/control-attr-rules.spec.js

…ed attributes

The add-attribute dialog rejected disabled names but let a locked one
be re-added, overwriting its value, and readonly doesn't stop Space on
a checkbox or the arrow keys on a select. Reject locked names in the
dialog and in EditPanel#addAttribute, and disable locked checkbox,
radio and select controls (text inputs stay readonly).

Refs #116
Refs #159
loadResources passed the stored locale unconditionally, so on a first
visit it passed null and i18n fell back to en-US. Only override the
configured locale when setLang() has stored one.
Radio and checkbox groups now honour attrs.name (#331), so two fields
can end up submitting under one key, most often after a clone. Show a
hint under the name attribute of every field that shares its name, and
refresh it as names change and fields are added or removed.

Refs #331
Only rewrite a hint when its text changes, so its status region is not
announced again while another field is edited. Watch every field in
the scan, so a name set in code on a field without a name row still
refreshes the others. Describe a picklist name control too, and drop
an unused return from the i18n locale spec.

Refs #331
Copilot AI lite review requested due to automatic review settings September 28, 2026 11:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Unresolved correctness, accessibility-refresh, control-locking, and documentation issues remain.

Review effort: Lite
Findings: 2 Medium severity

Open (2)
What changed in this PR

This PR fixes editor attribute locking, initial locale precedence, and duplicate field-name warnings.

Changes:

  • Enforces locked attributes and control states.
  • Honors configured i18n locale on first load.
  • Adds duplicate-name detection, accessible hints, tests, and documentation updates.
File Reviewed changes
tests/​editor-i18n-locale.spec.js Tests locale precedence.
tests/​duplicate-field-names.spec.js Tests duplicate-name warnings.
tests/​control-attr-rules.spec.js Tests locked control behavior.
src/​lib/​js/​editor.js Applies the configured locale correctly.
src/​lib/​js/​components/​fields/​duplicate-names.test.mjs Tests duplicate-name utilities.
src/​lib/​js/​components/​fields/​duplicate-names.mjs Detects duplicate effective field names; normalization needs correction.
src/​lib/​js/​components/​edit-panel/​locked-attrs.test.js Tests locked attribute guards and rendering.
src/​lib/​js/​components/​edit-panel/​edit-panel.js Guards attribute additions; canonicalize names before lock checks.
src/​lib/​js/​components/​edit-panel/​edit-panel-item.mjs Applies locked control states; preserve disabled-attribute enforcement.
src/​lib/​js/​components/​edit-panel/​duplicate-name-hint.test.js Tests hint lifecycle and accessibility.
src/​lib/​js/​components/​edit-panel/​duplicate-name-hint.js Renders duplicate-name hints; IDs and locale refresh need updates.
src/​lib/​js/​common/​actions.test.js Tests locked attribute validation.
src/​lib/​js/​common/​actions.js Rejects locked attribute names.
docs/​options/​i18n/​README.md Documents locale behavior.
docs/​options/​controls/​README.md Documents locked controls; include radios.
docs/​options/​config/​README.md Updates locking guidance; include radios.
docs/​controls/​custom-attribute-types.md Updates attribute rules; include radios.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +298 to +304
// readonly does nothing on checkboxes, radios and selects, so a locked one is disabled instead
if (this.isLocked) {
const isTextControl =
baseConfig.tag === 'textarea' ||
(baseConfig.tag === 'input' && !['checkbox', 'radio'].includes(baseConfig.attrs?.type))
attrs[isTextControl ? 'readonly' : 'disabled'] = true
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Leaving this as is. attrs.disabled = this.isDisabled was always false at this point: itemInput returns null a few lines up when this.isDisabled is true, and EditPanel#createProps skips disabled props before an EditPanelItem is built. hideDisabled: false doesn't render an editable input for a disabled attribute on either the old or the new code.

Comment on lines +3 to +5
String(name ?? '')
.trim()
.replace(/\[\]$/, '')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 36d84c7. fieldNameKey no longer trims. It only drops the trailing [], the same way userData does, so email and email are now separate keys and two whitespace-only names are flagged.

…s them

fieldNameKey trimmed names, so 'email' and ' email ' were flagged as
sharing a key though they post under different ones, and two
whitespace-only names were ignored though they collide.

Refs #331
@kevinchappell
kevinchappell merged commit 0372904 into main Sep 28, 2026
2 checks passed
@kevinchappell
kevinchappell deleted the fix/editor-attr-guards branch September 28, 2026 12:13
@kevinchappell

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 5.10.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants