Skip to content
Permalink
Browse files

NUX: Make tips appear inline

Improve the usability of NUX tips by making them appear as inline
notices rather than floating popovers.

Adds a new InlineTip component to accomplish this.
  • Loading branch information...
noisysocks committed Jul 15, 2019
1 parent cf1da64 commit 2011a876abe06fddc1ef7b9163d125836697e056
Showing with 256 additions and 253 deletions.
  1. +1 −2 package-lock.json
  2. +1 −0 packages/block-editor/package.json
  3. +4 −0 packages/block-editor/src/components/block-inspector/index.js
  4. +3 −0 packages/block-editor/src/components/block-inspector/style.scss
  5. +10 −1 packages/block-editor/src/components/inserter/menu.js
  6. +4 −0 packages/block-editor/src/components/inserter/style.scss
  7. +0 −4 packages/e2e-test-utils/src/create-new-post.js
  8. +63 −146 packages/e2e-tests/specs/nux.test.js
  9. +0 −1 packages/edit-post/package.json
  10. +0 −15 packages/edit-post/src/components/editor-initialization/index.js
  11. +1 −7 packages/edit-post/src/components/header/header-toolbar/index.js
  12. +8 −14 packages/edit-post/src/components/header/index.js
  13. +0 −36 packages/edit-post/src/components/options-modal/options/deferred.js
  14. +2 −7 packages/edit-post/src/components/options-modal/options/enable-tips.js
  15. +1 −1 packages/edit-post/src/components/options-modal/test/__snapshots__/index.js.snap
  16. +0 −1 packages/editor/package.json
  17. +0 −4 packages/editor/src/components/post-preview-button/index.js
  18. +0 −10 packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap
  19. +0 −4 packages/editor/src/components/post-publish-button/index.js
  20. +4 −0 packages/nux/CHANGELOG.md
  21. +34 −0 packages/nux/src/components/inline-tip/README.md
  22. +40 −0 packages/nux/src/components/inline-tip/index.js
  23. +34 −0 packages/nux/src/components/inline-tip/test/__snapshots__/index.js.snap
  24. +45 −0 packages/nux/src/components/inline-tip/test/index.js
  25. +1 −0 packages/nux/src/index.js

Some generated files are not rendered by default. Learn more.

@@ -36,6 +36,7 @@
"@wordpress/i18n": "file:../i18n",
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/nux": "file:../nux",
"@wordpress/rich-text": "file:../rich-text",
"@wordpress/token-list": "file:../token-list",
"@wordpress/url": "file:../url",
@@ -10,6 +10,7 @@ import { __ } from '@wordpress/i18n';
import { getBlockType, getUnregisteredTypeHandlerName } from '@wordpress/blocks';
import { PanelBody } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { InlineTip } from '@wordpress/nux';

/**
* Internal dependencies
@@ -51,6 +52,9 @@ const BlockInspector = ( {

return (
<>
<InlineTip tipId="core/editor.blockInspector" className="block-editor-block-inspector__tip">
{ __( 'The block tab contains additional settings for the selected block.' ) }
</InlineTip>
<div className="editor-block-inspector__card block-editor-block-inspector__card">
<BlockIcon icon={ blockType.icon } showColors />
<div className="editor-block-inspector__card-content block-editor-block-inspector__card-content">
@@ -6,6 +6,9 @@
text-align: center;
}

.block-editor-block-inspector__tip {
margin: 0 0 16px 0;
}

.block-editor-block-inspector__card {
display: flex;
@@ -22,7 +22,7 @@ import scrollIntoView from 'dom-scroll-into-view';
*/
import { __, _n, _x, sprintf } from '@wordpress/i18n';
import { Component, createRef } from '@wordpress/element';
import { withSpokenMessages, PanelBody } from '@wordpress/components';
import { withSpokenMessages, PanelBody, ExternalLink } from '@wordpress/components';
import {
getCategories,
isReusableBlock,
@@ -33,6 +33,7 @@ import { withDispatch, withSelect } from '@wordpress/data';
import { withInstanceId, compose, withSafeTimeout } from '@wordpress/compose';
import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes';
import { addQueryArgs } from '@wordpress/url';
import { InlineTip } from '@wordpress/nux';

/**
* Internal dependencies
@@ -290,6 +291,14 @@ export class InserterMenu extends Component {
aria-label={ __( 'Available block types' ) }
>

<InlineTip tipId="core/editor.inserter" className="block-editor-inserter__tip">
{ __( 'There are Blocks for most types of content: text, headings, images, lists, and lots more!' ) }
{ ' ' }
<ExternalLink href="https://wordpress.org/support/article/wordpress-editor/#blocks">
{ __( 'Learn more' ) }
</ExternalLink>
</InlineTip>

<ChildBlocks
rootClientId={ rootClientId }
items={ childItems }
@@ -105,6 +105,10 @@ $block-inserter-search-height: 38px;
}
}

.block-editor-inserter__tip {
margin: 0 0 $grid-size-large 0;
}

.block-editor-inserter__popover .block-editor-block-types-list {
margin: 0 -8px;
}
@@ -32,8 +32,4 @@ export async function createNewPost( {
const action = _enableTips ? 'enableTips' : 'disableTips';
wp.data.dispatch( 'core/nux' )[ action ]();
}, enableTips );

if ( enableTips ) {
await page.reload();
}
}
@@ -2,179 +2,96 @@
* WordPress dependencies
*/
import {
clickBlockAppender,
clickOnMoreMenuItem,
createNewPost,
saveDraft,
insertBlock,
openGlobalBlockInserter,
toggleScreenOption,
} from '@wordpress/e2e-test-utils';

describe( 'New User Experience (NUX)', () => {
async function clickAllTips( page ) {
// Click through all available tips.
const tips = await getTips( page );
const numberOfTips = tips.tipIds.length;

for ( let i = 1; i < numberOfTips; i++ ) {
await page.click( '.nux-dot-tip .components-button.is-link' );
}

return { numberOfTips, tips };
}

async function getTips( page ) {
return await page.evaluate( () => {
return wp.data.select( 'core/nux' ).getAssociatedGuide( 'core/editor.inserter' );
} );
}

async function getTipsEnabled( page ) {
return await page.evaluate( () => {
return wp.data.select( 'core/nux' ).areTipsEnabled();
} );
}
/**
* Queries the data store and returns whether or not NUX tips are enabled.
*
* @return {boolean} Whether or not NUX tips are enabled.
*/
async function areTipsEnabled() {
return await page.evaluate( () => wp.data.select( 'core/nux' ).areTipsEnabled() );
}

describe( 'New User Experience (NUX)', () => {
beforeEach( async () => {
await createNewPost( { enableTips: true } );
} );

it( 'should show tips to a first-time user', async () => {
const firstTipText = await page.$eval( '.nux-dot-tip', ( element ) => element.innerText );
expect( firstTipText ).toContain( 'Welcome to the wonderful world of blocks!' );

const [ nextTipButton ] = await page.$x( "//button[contains(text(), 'See next tip')]" );
await nextTipButton.click();

const secondTipText = await page.$eval( '.nux-dot-tip', ( element ) => element.innerText );
expect( secondTipText ).toContain( 'You’ll find more settings for your page and blocks in the sidebar.' );
} );

it( 'should show "Got it" once all tips have been displayed', async () => {
await clickAllTips( page );
it( 'should show a tip in the inserter', async () => {
// Open up the inserter.
await openGlobalBlockInserter();

// Make sure "Got it" button appears on the last tip.
const gotItButton = await page.$x( "//button[contains(text(), 'Got it')]" );
expect( gotItButton ).toHaveLength( 1 );

// Click the "Got it button".
await page.click( '.nux-dot-tip .components-button.is-link' );

// Verify no more tips are visible on the page.
const nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 0 );

// Tips should not be marked as disabled, but when the user has seen all
// of the available tips, they will not appear.
const areTipsEnabled = await getTipsEnabled( page );
expect( areTipsEnabled ).toEqual( true );
// Check there's a tip in the inserter.
const inserterTip = await page.$( '.block-editor-inserter__tip' );
expect( inserterTip ).not.toBeNull();
} );

it( 'should hide and disable tips if "disable tips" button is clicked', async () => {
await page.click( '.nux-dot-tip__disable' );

// Verify no more tips are visible on the page.
let nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 0 );
it( 'should show a tip in the block inspector', async () => {
// Insert any old block.
await insertBlock( 'Paragraph' );

// We should be disabling the tips so they don't appear again.
const areTipsEnabled = await getTipsEnabled( page );
expect( areTipsEnabled ).toEqual( false );

// Refresh the page; tips should not show because they were disabled.
await page.reload();

nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 0 );
// Check there's a tip in the block inspector.
const blockInspectorTip = await page.$( '.block-editor-block-inspector__tip' );
expect( blockInspectorTip ).not.toBeNull();
} );

it( 'should enable tips when the "Enable tips" option is toggled on', async () => {
// Start by disabling tips.
await page.click( '.nux-dot-tip__disable' );

// Verify no more tips are visible on the page.
let nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 0 );

// Tips should be disabled in localStorage as well.
let areTipsEnabled = await getTipsEnabled( page );
expect( areTipsEnabled ).toEqual( false );
it( 'should dismiss a single tip if X button is clicked and dialog is dismissed', async () => {
// We need to *dismiss* the upcoming confirm() dialog, so let's temporarily
// remove the listener that was added in by enablePageDialogAccept().
const listeners = page.rawListeners( 'dialog' );
page.removeAllListeners( 'dialog' );

// Toggle the 'Enable Tips' option to enable.
await toggleScreenOption( 'Enable Tips' );
// Open up the inserter.
await openGlobalBlockInserter();

// Tips should once again appear.
nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 1 );
// Dismiss the upcoming confirm() dialog.
page.once( 'dialog', async ( dialog ) => {
await dialog.dismiss();
} );

// Tips should be enabled in localStorage as well.
areTipsEnabled = await getTipsEnabled( page );
expect( areTipsEnabled ).toEqual( true );
} );
// Click the tip's X button.
await page.click( '.block-editor-inserter__tip button[aria-label="Dismiss this notice"]' );

// TODO: This test should be enabled once
// https://github.com/WordPress/gutenberg/issues/7458 is fixed.
it.skip( 'should show tips as disabled if all tips have been shown', async () => {
await clickAllTips( page );
// The tip should be gone.
const inserterTip = await page.$( '.block-editor-inserter__tip' );
expect( inserterTip ).toBeNull();

// Open the "More" menu to check the "Show Tips" element.
await page.click( '.edit-post-more-menu [aria-label="More tools & options"]' );
const showTipsButton = await page.$x( '//button[contains(text(), "Show Tips")][@aria-pressed="false"]' );
// Tips should still be enabled.
expect( await areTipsEnabled() ).toBe( true );

expect( showTipsButton ).toHaveLength( 1 );
// Restore the listeners that we removed above.
for ( const listener of listeners ) {
page.addListener( 'dialog', listener );
}
} );

// TODO: This test should be enabled once
// https://github.com/WordPress/gutenberg/issues/7458 is fixed.
it.skip( 'should reset tips if all tips have been shown and show tips was unchecked', async () => {
const { numberOfTips } = await clickAllTips( page );

// Click again to re-enable tips; they should appear.
await clickOnMoreMenuItem( 'Show Tips' );
it( 'should disable all tips if X button is clicked and dialog is confirmed', async () => {
// Open up the inserter.
await openGlobalBlockInserter();

// Open the "More" menu to check the "Show Tips" element.
await page.click( '.edit-post-more-menu [aria-label="More tools & options"]' );
const showTipsButton = await page.$x( '//button[contains(text(), "Show Tips")][@aria-pressed="true"]' );
// Dismiss the tip. (The confirm() dialog will automatically be accepted.)
await page.click( '.block-editor-inserter__tip button[aria-label="Dismiss this notice"]' );

expect( showTipsButton ).toHaveLength( 1 );
// The tip should be gone.
const inserterTip = await page.$( '.block-editor-inserter__tip' );
expect( inserterTip ).toBeNull();

// Tips should re-appear on the page.
const nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 1 );

// Tips should be enabled again.
const areTipsEnabled = await getTipsEnabled( page );
expect( areTipsEnabled ).toEqual( true );

// Dismissed tips should be reset and ready to be shown again.
const resetTips = await getTips( page );
const newNumberOfTips = resetTips.tipIds.length;
expect( newNumberOfTips ).toHaveLength( numberOfTips );
// Tips should now be disabled.
expect( await areTipsEnabled() ).toBe( false );
} );

// TODO: This test should be enabled once
// https://github.com/WordPress/gutenberg/issues/7753 is fixed.
// See: https://github.com/WordPress/gutenberg/issues/7753#issuecomment-403952816
it.skip( 'should show tips if "Show tips" was disabled on a draft and then enabled', async () => {
// Click the "Show tips" button (enabled by default) to disable tips.
await clickOnMoreMenuItem( 'Show Tips' );

// Let's type something so there's content in this post.
await page.click( '.editor-post-title__input' );
await page.keyboard.type( 'Post title' );
await clickBlockAppender();
await page.keyboard.type( 'Post content goes here.' );
await saveDraft();

// Refresh the page; tips should be disabled.
await page.reload();
let nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 0 );

// Clicking should re-enable tips.
await clickOnMoreMenuItem( 'Show Tips' );

// Tips should re-appear on the page.
nuxTipElements = await page.$$( '.nux-dot-tip' );
expect( nuxTipElements ).toHaveLength( 1 );
it( 'should enable and disable tips when option is toggled', async () => {
// Toggling the option off should disable tips.
await toggleScreenOption( 'Enable Tips', false );
expect( await areTipsEnabled() ).toBe( false );

// Toggling the option on should enable tips.
await toggleScreenOption( 'Enable Tips', true );
expect( await areTipsEnabled() ).toBe( true );
} );
} );
@@ -36,7 +36,6 @@
"@wordpress/hooks": "file:../hooks",
"@wordpress/i18n": "file:../i18n",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/nux": "file:../nux",
"@wordpress/plugins": "file:../plugins",
"@wordpress/url": "file:../url",
"@wordpress/viewport": "file:../viewport",
@@ -1,9 +1,3 @@
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
@@ -24,14 +18,5 @@ export default function( { postId } ) {
useAdjustSidebarListener( postId );
useBlockSelectionListener( postId );
useUpdatePostLinkListener( postId );
const { triggerGuide } = useDispatch( 'core/nux' );
useEffect( () => {
triggerGuide( [
'core/editor.inserter',
'core/editor.settings',
'core/editor.preview',
'core/editor.publish',
] );
}, [ triggerGuide ] );
return null;
}

0 comments on commit 2011a87

Please sign in to comment.
You can’t perform that action at this time.