Skip to content
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

Introduce formal API for displaying notices #5975

Closed
danielbachhuber opened this Issue Apr 4, 2018 · 16 comments

Comments

Projects
8 participants
@danielbachhuber
Copy link
Member

danielbachhuber commented Apr 4, 2018

Core WordPress has an admin_notices action where it's quite common for plugins / themes to display notices.

In Gutenberg, this looks quite bad:

image

Gutenberg should have some formal API for plugins / themes to register their notices. This should support notices specific to the view (e.g. editor) and user actions (e.g. saving a post).

Previously: #5590 #5927 #3964

@jsmoriss

This comment has been minimized.

Copy link

jsmoriss commented Apr 5, 2018

FYI - My own plugins provide important notices to users after creating / updating the metabox. I need a way to submit notices to Gutenberg for display. I cannot make my plugins compatible with Gutenberg without this functionality. Here is the code I've started, but have stopped development because 1) I need to exclude auto-saves (don't want to refresh on auto-save), and 2) I need to submit notices to Gutenberg, which does not appear to be possible right now.

var editPost = wp.data.select( 'core/edit-post' ), lastIsSaving = false;

wp.data.subscribe( function() {
        var isSaving = editPost.isSavingMetaBoxes();
        if ( isSaving !== lastIsSaving && !isSaving ) {
                lastIsSaving = isSaving;

                // TODO: ajax call to get metabox HTML

                // TODO: refresh the metabox container

                // TODO: ajax call to get the notices array

                // TODO: submit notices to Gutenberg for display

        }
        lastIsSaving = isSaving;
} );

Thanks,

js.

@karmatosed karmatosed modified the milestones: Merge Proposal, Merge Proposal: Back Compat Apr 12, 2018

@adamsilverstein

This comment has been minimized.

Copy link
Contributor

adamsilverstein commented Apr 17, 2018

For this task it would be worth considering adding direct support for add_settings_error (https://developer.wordpress.org/reference/functions/add_settings_error/) so existing uses will continue to work. I'm imaging an api similar to the customizer where the PHP size maps to a similar JS based api that plugins could also use.

@westonruter

This comment has been minimized.

Copy link
Member

westonruter commented Apr 17, 2018

Are not notices already supported? I've been using wp.data.dispatch( 'core/editor' ).createWarningNotice() successfully to create them in https://github.com/Automattic/amp-wp/blob/286381c/assets/js/amp-block-validation.js#L175-L185

@adamsilverstein

This comment has been minimized.

Copy link
Contributor

adamsilverstein commented Apr 18, 2018

Right, we can add notices this way in JavaScript.... @danielbachhuber is this proposing adding a PHP side API?

Supporting the existing PHP API ( add_settings_error ) could be useful for error states that occur on the PHP side, in an endpoint, or at page load for example, or for plugins that may have no post JavaScript at all and still have a reason to display a notice (for example, an api required to save posts is not available).

@danielbachhuber

This comment has been minimized.

Copy link
Member Author

danielbachhuber commented Apr 18, 2018

I was unaware of wp.data.dispatch( 'core/editor' ).createWarningNotice(). If there is such equivalent API, this may be a matter of documentation (or some magic transformation of the existing PHP admin notices into Gutenberg notices).

@jsmoriss

This comment has been minimized.

Copy link

jsmoriss commented Apr 18, 2018

Is createWarningNotice() unable to handle HTML? I'm getting an "TypeError: e.replace is not a function" error...

var noticeMsgHtml = data[noticeType][noticeNum];
var noticeElement = wp.element.createElement( 'div' );

noticeElement.innerHTML = noticeMsgHtml;

createWarningNotice( noticeElement );

Thanks,

js.

@adamsilverstein

This comment has been minimized.

Copy link
Contributor

adamsilverstein commented Apr 19, 2018

@jsmoriss you can try following this pattern from guternberg internals:

dispatch( createSuccessNotice(
<p>
<span>{ noticeMessage }</span>
{ ' ' }
{ shouldShowLink && <a href={ post.link }>{ __( 'View post' ) }</a> }
</p>,
{ id: SAVE_POST_NOTICE_ID, spokenMessage: noticeMessage }
) );

@mtias

This comment has been minimized.

Copy link
Contributor

mtias commented Oct 3, 2018

Closing in favor of #6388 proposal.

@rgomezp

This comment has been minimized.

Copy link

rgomezp commented Feb 26, 2019

I have yet to find good documentation on how to do notices in Gutenberg. The best I could find was this one: https://github.com/WordPress/gutenberg/tree/master/docs/designers-developers/developers/tutorials/notices

However, it does not really describe how best to display a notice based on data from an asynchronous HTTP call. I want to grab data from the call, pass it to Javascript somehow (using wp_enqueue_script perhaps?), and get the notice to display with my custom content.

main.php:

$response = wp_remote_post($url, $request);
$status = $response['response']['code']
if($status==200) {
    // display notice 1
} else {
   // display notice 2
}

Any ideas on how best to do this? Thanks

@danielbachhuber

This comment has been minimized.

Copy link
Member Author

danielbachhuber commented Feb 27, 2019

I want to grab data from the call, pass it to Javascript somehow (using wp_enqueue_script perhaps?), and get the notice to display with my custom content.

We did exactly this in our implementation.

Here's where we load the data into the window variable:

/**
 * Registers data we need client-side as a part of the initial page load.
 */
public static function action_enqueue_block_editor_assets() {
	$blocks_data = array(
		'editorNotices' => array(),
	);
	$post_id     = self::get_current_post_id();
	if ( $post_id ) {
		$blocks_data['editorNotices'] = Editor::get_converter_messages( $post_id );
	}
	wp_localize_script( 'tasty-recipes-block-editor', 'tastyRecipesBlockEditor', $blocks_data );
}

And here's where we create the notices client-side:

/**
 * WordPress dependencies
 */
const {
	dispatch,
} = wp.data;

/**
 * Fetches existing notice data and generates the corresponding notice.
 */
export default function generateNotices() {
	const { createNotice } = dispatch( 'core/notices' );
	window.tastyRecipesBlockEditor.editorNotices.forEach( ( notice ) => {
		createNotice( notice.type, notice.content, {
			isDismissible: notice.dismissible || false,
			actions: notice.actions || null,
		} );
	} );
}
@rgomezp

This comment has been minimized.

Copy link

rgomezp commented Mar 1, 2019

@danielbachhuber Thanks for the response Daniel. However, I didn't quite understand what will invoke the generateNotices function? I would think it shouldn't run until AFTER the data is available in the window variable (e.g: receives some sort of trigger from server as opposed to "as part of initial page load^^"). Also, should I add the javascript using wp_enqueue_script?

@danielbachhuber

This comment has been minimized.

Copy link
Member Author

danielbachhuber commented Mar 1, 2019

However, I didn't quite understand what will invoke the generateNotices function? I would think it shouldn't run until AFTER the data is available in the window variable (e.g: receives some sort of trigger from server as opposed to "as part of initial page load^^"). Also, should I add the javascript using wp_enqueue_script?

Yes. It's ES6 so you'll need to hook up Webpack to transpile it down. You'll also need to set up wp_enqueue_script to enqueue it.

Here's the full starting point on using JavaScript: https://wordpress.org/gutenberg/handbook/designers-developers/developers/tutorials/javascript/

@davidfcarr

This comment has been minimized.

Copy link

davidfcarr commented Mar 6, 2019

@danielbachhuber I've had a similar challenge about finding a good tutorial on this, and I appreciate your code sample. One thing I don't understand is whether the code you're showing tells Gutenberg when the notice should be displayed.

My challenge is creating a notice for a specific post type that will be displayed when the post is saved. Actually, the display of this notice would be conditional -- it should only be shown if the post contains metadata marking it as a template. I can figure out the application logic, but I can't figure out how to

  • detect that a post has been saved
  • create a notice that will be displayed every time the post is published or updated (not autosaved), provided the application-specific conditions are met

You've explained how to create the notice, but I don't see how it's connected to something like a publish or update event.

I'm far enough into learning Gutenberg that I've created a number of custom blocks, but certain aspects such as notifications, modals remain mysteries. I've managed to do a few things with the wp.data state storage system, but that also remains a tough one for me to understand.

Any further clues about notices would be appreciated.

@danielbachhuber

This comment has been minimized.

Copy link
Member Author

danielbachhuber commented Mar 6, 2019

You've explained how to create the notice, but I don't see how it's connected to something like a publish or update event.

Right — it's just connected to the page load event at the moment. Off the top of my head, I'm not sure of how to hook into a "Save Post" event. You'd need to do some research to sort that out.

@davidfcarr

This comment has been minimized.

Copy link

davidfcarr commented Mar 6, 2019

Thanks. At least that tells me it's not obvious.

@davidfcarr

This comment has been minimized.

Copy link

davidfcarr commented Mar 8, 2019

@danielbachhuber @rgomezp I've made progress figuring out how to display a notice triggered by an event such as post save. The routine below is loosely based on code Gutenberg uses internally to save metabox content on post save.

In my use case, certain posts of the type rsvpmaker are used as templates for specific events. The RSVPMaker plugin needs to display a notice prompting the user who has updated a template to click through to another screen if they want to create or update events based on that template. The url for the admin page where you do that is localized under the rsvpmaker_json variable. On the PHP side, we test that the post is of type rsvpmaker before outputting that variable.

My Gutenberg code tests whether the post is in the isSavingPost state but not an autosave.

const { subscribe } = wp.data;
if((typeof rsvpmaker_json !== 'undefined' ) && rsvpmaker_json.projected_url) {
		let wasSavingPost = wp.data.select( 'core/editor' ).isSavingPost();
		let wasAutosavingPost = wp.data.select( 'core/editor' ).isAutosavingPost();
		let wasPreviewingPost = wp.data.select( 'core/editor' ).isPreviewingPost();
		// determine whether to show notice
		subscribe( () => {
			const isSavingPost = wp.data.select( 'core/editor' ).isSavingPost();
			const isAutosavingPost = wp.data.select( 'core/editor' ).isAutosavingPost();
			const isPreviewingPost = wp.data.select( 'core/editor' ).isPreviewingPost();
			const hasActiveMetaBoxes = wp.data.select( 'core/edit-post' ).hasMetaBoxes();
			// Save metaboxes on save completion, except for autosaves that are not a post preview.
			const shouldTriggerTemplateNotice = (
					( wasSavingPost && ! isSavingPost && ! wasAutosavingPost ) ||
					( wasAutosavingPost && wasPreviewingPost && ! isPreviewingPost )
				);
			// Save current state for next inspection.
			wasSavingPost = isSavingPost;
			wasAutosavingPost = isAutosavingPost;
			wasPreviewingPost = isPreviewingPost;
			if ( shouldTriggerTemplateNotice ) {
	wp.data.dispatch('core/notices').createNotice(
		'info', // Can be one of: success, info, warning, error.
		__('After updating this template, click'), // Text string to display.
		{
			isDismissible: true, // Whether the user can dismiss the notice.
			// Any actions the user can perform.
			actions: [
				{
					url: rsvpmaker_json.projected_url,
					label: __('create / update events')
				}
			]
		}
	);
			}
			/* placeholder for logic to remove notice
			else {
				console.log('remove notice');
			}
			*/
} );	
}

This works pretty well. One lingering issue is that if you save the post multiple times, the notice gets output multiple times. If you can suggest a way of preventing that from happening, I'd like to hear it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.