diff --git a/src/js/_enqueues/wp/dashboard.js b/src/js/_enqueues/wp/dashboard.js index bea3373ae5c97..9290d36c6bb34 100644 --- a/src/js/_enqueues/wp/dashboard.js +++ b/src/js/_enqueues/wp/dashboard.js @@ -3,7 +3,7 @@ */ /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true, ajaxWidgets */ -/* global ajaxPopulateWidgets, quickPressLoad, */ +/* global ajaxPopulateWidgets, quickPressLoad */ window.wp = window.wp || {}; window.communityEventsData = window.communityEventsData || {}; @@ -137,31 +137,27 @@ jQuery( function($) { * @return {void} */ window.quickPressLoad = function() { - var act = $('#quickpost-action'), t; + var act = $( '#quickpost-action' ), t; - // Enable the submit buttons. - $( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false ); + // Enable the submit button. + $( '#quick-press .submit input[type="submit"]' ).prop( 'disabled', false ); - t = $('#quick-press').on( 'submit', function( e ) { + t = $( '#quick-press' ).on( 'submit', function( e ) { e.preventDefault(); - // Show a spinner. - $('#dashboard_quick_press #publishing-action .spinner').show(); - // Disable the submit button to prevent duplicate submissions. - $('#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]').prop('disabled', true); + $( '#quick-press .submit input[type="submit"]' ).prop( 'disabled', true ); // Post the entered data to save it. $.post( t.attr( 'action' ), t.serializeArray(), function( data ) { // Replace the form, and prepend the published post. - $('#dashboard_quick_press .inside').html( data ); - $('#quick-press').removeClass('initial-form'); + $( '#dashboard_quick_press .inside' ).html( data ); quickPressLoad(); highlightLatestPost(); // Focus the title to allow for quickly drafting another post. - $('#title').trigger( 'focus' ); - }); + $( '#title' ).focus(); + } ); /** * Highlights the latest post for one second. @@ -169,20 +165,20 @@ jQuery( function($) { * @return {void} */ function highlightLatestPost () { - var latestPost = $('.drafts ul li').first(); - latestPost.css('background', '#fffbe5'); - setTimeout(function () { - latestPost.css('background', 'none'); - }, 1000); + var latestPost = $( '.drafts ul li' ) .first(); + latestPost.css( 'background', '#fffbe5' ); + setTimeout( function () { + latestPost.css( 'background', 'none' ); + }, 1000 ); } } ); // Change the QuickPost action to the publish value. - $('#publish').on( 'click', function() { act.val( 'post-quickpress-publish' ); } ); + $( '#publish' ).on( 'click', function() { act.val( 'post-quickpress-publish' ); } ); - $('#quick-press').on( 'click focusin', function() { + $( '#quick-press' ).on( 'click focusin', function() { wpActiveEditor = 'content'; - }); + } ); autoResizeTextarea(); }; diff --git a/src/wp-admin/css/dashboard.css b/src/wp-admin/css/dashboard.css index ab73f828f7067..892d309de4da6 100644 --- a/src/wp-admin/css/dashboard.css +++ b/src/wp-admin/css/dashboard.css @@ -780,12 +780,6 @@ body #dashboard-widgets .postbox form .submit { padding: 0; } -#dashboard_quick_press div.updated { - margin-bottom: 10px; - border: 1px solid #f0f0f1; - border-width: 1px 1px 1px 0; -} - #dashboard_quick_press form { margin: 12px; } @@ -803,7 +797,6 @@ body #dashboard-widgets .postbox form .submit { #dashboard_quick_press input, #dashboard_quick_press textarea { - box-sizing: border-box; margin: 0; } @@ -850,11 +843,12 @@ body #dashboard-widgets .postbox form .submit { #dashboard_quick_press .drafts li { margin-bottom: 1em; } + #dashboard_quick_press .drafts li time { color: #646970; } -#dashboard_quick_press .drafts p { +#dashboard_quick_press .drafts .draft-content { margin: 0; word-wrap: break-word; } diff --git a/src/wp-admin/includes/dashboard.php b/src/wp-admin/includes/dashboard.php index 778e3de40326b..5a0a524e201ab 100644 --- a/src/wp-admin/includes/dashboard.php +++ b/src/wp-admin/includes/dashboard.php @@ -541,12 +541,14 @@ function wp_network_dashboard_right_now() { * Displays the Quick Draft widget. * * @since 3.8.0 + * @since 7.1.0 Added $notice_type parameter. * * @global int $post_ID * - * @param string|false $error_msg Optional. Error message. Default false. + * @param string|false $message Optional. Error or success message. Default false. + * @param string $notice_type Optional. Admin notice type. Default 'notice-error'. */ -function wp_dashboard_quick_press( $error_msg = false ) { +function wp_dashboard_quick_press( $message = false, $notice_type = 'notice-error' ) { global $post_ID; if ( ! current_user_can( 'edit_posts' ) ) { @@ -578,14 +580,17 @@ function wp_dashboard_quick_press( $error_msg = false ) { $post_ID = (int) $post->ID; ?> -
+ array( 'error' ), + 'additional_classes' => array( $notice_type ), + 'attributes' => array( + 'role' => 'alert', + ), ) ); } @@ -688,7 +693,7 @@ function wp_dashboard_recent_drafts( $drafts = false ) { $the_content = wp_trim_words( $draft->post_content, $draft_length ); if ( $the_content ) { - echo '

' . $the_content . '

'; + echo '

' . $the_content . '

'; } echo "\n"; } diff --git a/src/wp-admin/post.php b/src/wp-admin/post.php index dd7bad1bb3830..a265f62f2e443 100644 --- a/src/wp-admin/post.php +++ b/src/wp-admin/post.php @@ -96,16 +96,28 @@ $_POST['comment_status'] = get_default_comment_status( $post->post_type ); $_POST['ping_status'] = get_default_comment_status( $post->post_type, 'pingback' ); - // Wrap Quick Draft content in the Paragraph block. - if ( ! str_contains( $_POST['content'], '' ) ) { + $quickdraft_post_title = trim( $_POST['post_title'] ); + $quickdraft_post_content = trim( $_POST['content'] ); + + if ( empty( $quickdraft_post_title ) && empty( $quickdraft_post_content ) ) { + return wp_dashboard_quick_press( __( 'Cannot create a draft post with empty title and content.' ) ); + } + + // Wrap Quick Draft content in a Paragraph block. + if ( + use_block_editor_for_post_type( $post->post_type ) && + ! empty( $quickdraft_post_content ) && + ! str_contains( $quickdraft_post_content, '' ) + ) { + // Note that `edit_post()` reads from the $_POST superglobal by reference. $_POST['content'] = sprintf( '%s', - str_replace( array( "\r\n", "\r", "\n" ), '
', $_POST['content'] ) + str_replace( array( "\r\n", "\r", "\n" ), '
', $quickdraft_post_content ) ); } edit_post(); - wp_dashboard_quick_press(); + wp_dashboard_quick_press( __( 'Draft created successfully.' ), 'notice-success' ); exit; case 'postajaxpost': diff --git a/tests/e2e/specs/dashboard.test.js b/tests/e2e/specs/dashboard.test.js index 90459ac83ae6f..09508f6622299 100644 --- a/tests/e2e/specs/dashboard.test.js +++ b/tests/e2e/specs/dashboard.test.js @@ -8,13 +8,13 @@ test.describe( 'Quick Draft', () => { await requestUtils.deleteAllPosts(); } ); - test( 'Allows draft to be created with Title and Content', async ( { + test( 'should allow Quick Draft to be created with Title and Content', async ( { admin, page } ) => { await admin.visitAdminPage( '/' ); - // Wait for Quick Draft title field to appear. + // Wait for the Quick Draft title field to appear. const draftTitleField = page.locator( '#quick-press' ).getByRole( 'textbox', { name: 'Title' } ); @@ -22,36 +22,51 @@ test.describe( 'Quick Draft', () => { await expect( draftTitleField ).toBeVisible(); // Focus and fill in a title. - await draftTitleField.fill( 'Test Draft Title' ); + await draftTitleField.fill( 'Quick Draft test title' ); - // Navigate to content field and type in some content - await page.keyboard.press( 'Tab' ); - await page.keyboard.type( 'Test Draft Content' ); + // Wait for the Quick Draft content textarea to appear. + const quickDraftContentTextarea = page.locator( + '#quick-press' + ).getByRole( 'textbox', { name: 'Content' } ); + + await expect( quickDraftContentTextarea ).toBeVisible(); + + // Focus and fill in some content. + await quickDraftContentTextarea.fill( 'Quick Draft test content' ); + + // Wait for the Save Draft button to appear and click it. + const saveDraftButton = page.locator( + '#quick-press' + ).getByRole( 'button', { name: 'Save Draft' } ); - // Navigate to Save Draft button and press it. - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Enter' ); + await expect( saveDraftButton ).toBeVisible(); + await saveDraftButton.click(); - // Check that new draft appears in Your Recent Drafts section + // Check that the new draft title appears in the 'Your Recent Drafts' section. await expect( page.locator( '.drafts .draft-title' ).first().getByRole( 'link' ) - ).toHaveText( 'Test Draft Title' ); + ).toHaveText( 'Quick Draft test title' ); + + // Check that the new draft content appears in the 'Your Recent Drafts' section. + await expect( + page.locator( '.drafts .draft-content' ).first() + ).toHaveText( 'Quick Draft test content' ); - // Check that new draft appears in Posts page + // Check that the new draft appears in the Posts page. await admin.visitAdminPage( '/edit.php' ); await expect( page.locator( '.type-post.status-draft .title' ).first() - ).toContainText( 'Test Draft Title' ); + ).toContainText( 'Quick Draft test title' ); } ); - test( 'Allows draft to be created without Title or Content', async ( { + test( 'should prevent Quick Draft from being created without Title or Content', async ( { admin, page } ) => { await admin.visitAdminPage( '/' ); - // Wait for Save Draft button to appear and click it + // Wait for the Save Draft button to appear and click it. const saveDraftButton = page.locator( '#quick-press' ).getByRole( 'button', { name: 'Save Draft' } ); @@ -59,12 +74,92 @@ test.describe( 'Quick Draft', () => { await expect( saveDraftButton ).toBeVisible(); await saveDraftButton.click(); - // Check that new draft appears in Your Recent Drafts section + // Check that an admin notice with ARIA role 'alert' appears. + await expect( + page.locator( '#quick-press' ).getByRole( 'alert' ) + ).toHaveText( 'Cannot create a draft post with empty title and content.' ); + + // Check that no new draft appears in the Posts page. + await admin.visitAdminPage( '/edit.php' ); + + await expect( + page.locator( '#the-list .no-items .colspanchange' ) + ).toContainText( 'No posts found.' ); + } ); + + test( 'should allow Quick Draft to be created with only the Title', async ( { + admin, + page + } ) => { + await admin.visitAdminPage( '/' ); + + // Wait for the Quick Draft title field to appear. + const quickDraftTitleField = page.locator( + '#quick-press' + ).getByRole( 'textbox', { name: 'Title' } ); + + await expect( quickDraftTitleField ).toBeVisible(); + + // Focus and fill in a title. + await quickDraftTitleField.fill( 'Quick Draft test title' ); + + // Wait for the Save Draft button to appear and click it. + const saveDraftButton = page.locator( + '#quick-press' + ).getByRole( 'button', { name: 'Save Draft' } ); + + await expect( saveDraftButton ).toBeVisible(); + await saveDraftButton.click(); + + // Check that the new draft title appears in the 'Your Recent Drafts' section. + await expect( + page.locator( '.drafts .draft-title' ).first().getByRole( 'link' ) + ).toHaveText( 'Quick Draft test title' ); + + // Check that the new draft appears in the Posts page. + await admin.visitAdminPage( '/edit.php' ); + + await expect( + page.locator( '.type-post.status-draft .title' ).first() + ).toContainText( 'Quick Draft test title' ); + } ); + + test( 'should allow Quick Draft to be created with only the Content', async ( { + admin, + page + } ) => { + await admin.visitAdminPage( '/' ); + + // Wait for the Quick Draft content textarea to appear. + const quickDraftContentTextarea = page.locator( + '#quick-press' + ).getByRole( 'textbox', { name: 'Content' } ); + + await expect( quickDraftContentTextarea ).toBeVisible(); + + // Focus and fill in some content. + await quickDraftContentTextarea.fill( 'Quick Draft test content' ); + + // Wait for the Save Draft button to appear and click it. + const saveDraftButton = page.locator( + '#quick-press' + ).getByRole( 'button', { name: 'Save Draft' } ); + + await expect( saveDraftButton ).toBeVisible(); + await saveDraftButton.click(); + + // Check that the new draft title appears in the 'Your Recent Drafts' section. + // This test relies on Twenty Twenty-One being the active theme. + // Twenty Twenty-One alters the default post title from "(no title)" to "Untitled". await expect( page.locator( '.drafts .draft-title' ).first().getByRole( 'link' ) ).toHaveText( 'Untitled' ); - // Check that new draft appears in Posts page + await expect( + page.locator( '.drafts .draft-content' ).first() + ).toHaveText( 'Quick Draft test content' ); + + // Check that the new draft appears in the Posts page. await admin.visitAdminPage( '/edit.php' ); await expect(