-
Notifications
You must be signed in to change notification settings - Fork 4.6k
New Block: core/dialog
#71618
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: trunk
Are you sure you want to change the base?
New Block: core/dialog
#71618
Conversation
|
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. |
|
Just occurred to me, this will need block icons for all three blocks |
luisherranz
left a comment
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.
This is awesome, Seth 🙂👏
Lastly, other plugins and consumers of this block can open/close or close all dialogs utilizing the Interactivity API, like so:
store('core/dialog').state.dialogs.[{the dialog's elm id}].isOpen = trueor of course identifying all dialogs on page viastore('core/dialog').state.dialogs.
We can combine private and public stores to make this block extensible without exposing all the internal details of the store publicly. It would be the first one from Core, but I think it would be good to do it so we can start exploring extensibility patterns. We can check it a bit later when the new implementation has advanced a bit more.
Other than that, I've only taken a preliminary look at the Interactivity API part, and in general, it's quite good, although here are a few small suggestions.
Co-authored-by: Luis Herranz <luisherranz@gmail.com>
|
I'll be making a few more updates by end of the week, mostly around animations and your suggestions @luisherranz. |
@luisherranz on this topic, interesting, I would just do like this? // 1st party
store('core/dialog', {
state:{... internal derivced state funcs },
actions: {... internal funcs },
callbacks: {... internal callbacks }
}, {lock:true});
// Accessible by 3rd parties
const { state } = store('core/dialog', {
actions:{
open(id){
state.dialogs[id].isOpen = true;
}
}
}, {lock:false}) |
|
You need to use a different namespace. So it would be more like this: const { state, actions } = store( 'core/dialog/private', {
state: {
// internal derived state funcs
},
actions: {
// internal funcs
},
callbacks: {
// internal callbacks
}
}, {
lock: true
} );
store( 'core/dialog', {
// We add here the state that we want to be public.
state: {
// We can use getters for the state that we want to be read-only.
get dialog() {
return state.dialog;
},
// We can use setters for the state that we want to be modified.
get isOpen() {
return state.dialog.isOpen;
},
set isOpen( value ) {
state.dialog.isOpen = value;
},
},
// We add here the actions that we want to be public.
actions: {
open( id ) {
actions.open( id );
}
}
} );I've given the example of how to modify the private state if necessary, but for the most part, we're going to want the state to be read-only and to be modified through actions on public stores. EDIT: Oh, to return objects like function createReadOnlyProxy( obj ) {
return new Proxy( obj, {
get( target, prop ) {
const value = target[ prop ];
if ( typeof value === 'object' && value !== null ) {
return createReadOnlyProxy( value );
}
return value;
},
set() {
return false;
},
deleteProperty() {
return false;
}
});
}
store( 'core/dialog', {
state: {
get dialog() {
return createReadOnlyProxy( state.dialog );
}, |
|
Oh one other thing, I think I'm going to abstract out the dialog close button into it's own block so it can be more easily changed by 3rd parties. |
|
Actually, on further thought, I think I'm going to try to adopt |
…ry test case for 3rd party interactions
|
Some updates from me:
|
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.
One thing I wasn't sure about (although I've found it critical to a good experience in the editor) is introducing a custom redux store for a block. I don't believe I've seen a core block do this before. Any thoughts, concerns against this pattern emerging?
What?
Closes #61297
#61297
This PR introduces 3 blocks from prc-block-library.
core/dialogcore/dialogis actually a collection of three blocks: 1.core/dialoga wrapper really for 2.core/dialog-trigger, and 3.core/dialog-element.core/dialog-triggerThis block is the "trigger" or "action" to open the dialog-element
<dialog/>block.core/dialog-elementThis is the primary block and is a representation of the
<dialog/>element in modal form. With added support for positioning center, top left, top center, top right, center left, center right, bottom left, center, and right and support for backdrop coloring it utilize core supports for box shadow and border quite well.Additionally, this block and it's iAPI store have been designed to be maximally extensible. We utilize this block throughout pewresearch.org, most complexly in RLS https://www.pewresearch.org/religious-landscape-study/age-distribution/18-29/?dialogId=dialog_prayer-frequency&activeChartId=6ac4c46314ff76e21be70e1f66fe1a19. This link shows off another feature built into
core/dialog, in this case a modified version of it's built in Deep Linking feature. Which allows you to open a dialog on page render so long as it's id is in the url with?dialogId. There is also an Auto Activation timer for opening a dialog immediately on page render based on a ms timer. Lastly, other plugins and consumers of this block can open/close or close all dialogs utilizing the Interactivity API, like so:store('core/dialog').state.dialogs.[{the dialog's elm id}].isOpen = trueor of course identifying all dialogs on page viastore('core/dialog').state.dialogs.Lastly, there is a block binding provided to associate a heading with the dialogLabel attribute for
core/dialog-element.This provides an easy and familiar interface to provide a standard paradigm ~ a heading at the top of a dialog while also allowing the user to set the label and remove the display block, the heading, from the dialog. In either case, if a heading is not present a dynamic heading is created for accessibility and hidden from view on the frontend.
Why?
Dialogs, or "modals" are a common UI pattern that many 3rd parties have attempted. This provides a base and a representation of an actual HTML element,
<dialog/>that should be present in the core block library.How?
Testing Instructions
Testing Instructions for Keyboard
The block toolbar controls should allow keyboard accessibility to open/close dialogs in the editor. And on the frontend the trigger is wrapped with a button element with proper arias to signal relationships. In both contexts there are keyboard handlers to handle escaping out of a dialog.
Screenshots or screencast
CleanShot.2025-09-11.at.13.24.28.mp4
CleanShot.2025-09-11.at.13.29.24.mp4