DataForm: Add customizable button text to panel modal#76099
Conversation
Adds optional applyButtonText and cancelButtonText properties to the PanelLayout configuration type, allowing consumers to customize the modal action button labels. This supports use cases like saving as the modal closes.
- Add type properties to PanelLayout and NormalizedPanelLayout
- Pass button text through normalization (defaults applied in component)
- Extract layout properties in PanelModal and pass to ModalContent
- Use custom text with i18n defaults (__('Apply') and __('Cancel'))
- Add storybook controls for testing both button label variations
- Set default openAs to 'modal' in LayoutPanel story
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use || instead of ?? so empty strings fall back to default labels - Remove unnecessary `as NormalizedPanelLayout` cast since the discriminant check already narrows the type Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Size Change: +268 B (0%) Total Size: 6.87 MB
ℹ️ View Unchanged
|
Restructure the panel layout API so modal button labels are scoped
within the openAs configuration rather than being top-level properties.
openAs now accepts either a string ('dropdown' | 'modal') or an object
({ type: 'modal', applyLabel?, cancelLabel? }). Normalization flattens
the object form. Also adds unit tests and conditional storybook controls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ption Restructure storybook so label controls are scoped to a dedicated 'modalConfig' openAs option. The component assembles the PanelOpenAsModal object from storybook args, keeping getPanelLayoutFromStoryArgs simple. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Flaky tests detected in a8fb533. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/22763961584
|
Instead of flattening PanelOpenAsModal into separate applyLabel/cancelLabel properties, keep openAs as 'dropdown' | 'modal' | PanelOpenAsModal on the normalized type. This scopes labels within the modal config consistently across both input and normalized types. Normalization becomes a simple passthrough with ?? 'dropdown' default. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
|
||
| export type EditVisibility = 'always' | 'on-hover'; | ||
|
|
||
| export type PanelOpenAsModal = { |
There was a problem hiding this comment.
Does this need to be exported? Everything we export becomes public API for the types.
There was a problem hiding this comment.
It's being used in ModalContent now when destructuring the labels.
| onClose: () => void; | ||
| fieldLabel: string; | ||
| touched: boolean; | ||
| applyLabel?: string; |
There was a problem hiding this comment.
Do we expect any other labels in DataForm components? I searched a bit and couldn't find any cases for labels right now, but that could affect the props (e.g. a labels object).
There was a problem hiding this comment.
Question for @oandregal perhaps. I've not come across more yet.
packages/dataviews/src/components/dataform-layouts/panel/modal.tsx
Outdated
Show resolved
Hide resolved
packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts
Outdated
Show resolved
Hide resolved
packages/dataviews/src/components/dataform-layouts/panel/index.tsx
Outdated
Show resolved
Hide resolved
Normalize openAs to objects (PanelOpenAsDropdown | PanelOpenAsModal) so consumers use openAs.type everywhere. ModalContent extracts labels from field.layout directly. Storybook simplified to default/dropdown/modal options with conditional label controls. Keep PanelOpenAsDropdown private, export PanelOpenAsModal for the modal component cast. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| expect( result.layout ).toEqual( { | ||
| type: 'panel', | ||
| labelPosition: 'side', | ||
| openAs: { type: 'modal' }, |
There was a problem hiding this comment.
Mentioned over at #76099 (comment) but this object should contain the default labels.
There was a problem hiding this comment.
🤔 I assumed it would be safer to have the defaults in the component rather than the normalization but missed the empty case. I believe the strings should be optional.
There was a problem hiding this comment.
Was on the fence whether to normalise the default strings, or leave in the modal component. I went with normalisation in a8fb533. Let me know if the previous way was preferred.
| export type PanelOpenAsModal = { | ||
| type: 'modal'; | ||
| applyLabel?: string; | ||
| cancelLabel?: string; |
There was a problem hiding this comment.
What would you think of making the labels required?
There was a problem hiding this comment.
Personally I think they are fine as optional.
…sent Normalization now trims whitespace and falls back to i18n defaults for applyLabel/cancelLabel, so buttons always have visible text. This makes PanelOpenAsModal labels required strings and simplifies modal rendering by removing runtime fallbacks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the quick iterations, @mikejolley! |

What?
Adds optional
applyLabelandcancelLabelconfiguration to the DataForm panel modal layout'sopenAsconfig, allowing customization of modal action button labels.Why?
Some use cases need different button labels—for example, using "Save" instead of "Apply" when the modal closes after the action. This change enables that flexibility while maintaining backward compatibility with existing code that relies on the default "Apply" and "Cancel" labels.
How?
PanelOpenAsDropdownandPanelOpenAsModaltypes for the normalizedopenAsproperty — normalization always produces objects so consumers useopenAs.typeconsistentlyPanelLayout.openAs(input) still accepts strings ('dropdown' | 'modal') or an object ({ type: 'modal', applyLabel?, cancelLabel? })__('Apply')/__('Cancel')), ensuring labels are always present — consumers don't need runtime fallbacksPanelOpenAsModal.applyLabelandcancelLabelare required strings on the normalized typeModalContentextracts labels directly fromfield.layout— no prop drilling neededopenAsismodal)API example
Testing Instructions
npm run storybook:devmodalin the Controls panelTesting Instructions for Keyboard
Screenshots or screencast