Skip to content

Commit

Permalink
New User Experience (NUX) (#6631)
Browse files Browse the repository at this point in the history
* Fix appearance of DotTip on mobile

Force DotTip content to appear underneath the indicator dot when on
mobile viewports. This requires overriding a lot of .component-popover
styles which is quite gross.

* Remove unnecessary <Fragment>

* Position NUX dots right next to the edge of the button

Adjust popover's layout algorithm so that if yAxis === 'middle', it will
set popoverLeft such that the popover points to the edge of the anchor
node. This aligns with what e.g. position="bottom center" does.

* Increase NUX tip padding

* Add margin to sides of NUX tip on mobile

* Don't encourage using domReady in the NUX README

* Add wp-nux as a dependency of wp-edit-post

* Suggest that NUX tip identifiers are prefixed

* Don't use partial(), which is slated for removal

* tipID → tipId

* Improve NUX JSDoc comments

* Memoize the getAssociatedGuide() selector

* NUX: Dismiss the tip when one clicks away from it

* Position tooltips above NUX tips

* Remove aria-modal property from DotTip

Safari 11 has a weird bug when this property is used.

* Make order of the markup in DotTip match how they appear visually

* Improve a11y of DotTip labels

* Implement New User Guide

Adds a series of floating modal 'tips' which introduces a new user to
the editor. These tips can be advanced one by one or dismissed
alltogether. If dismissed, they will never show again.

* Dismiss tips instead of disabling when the X is clicked

* Fix DotTip focus issue

The first DotTip should receive focus when the page loads, and focus
should not be on the X button.

Also improve the note explaining our temporary position workaround.

* NUX: Only show inserter tip in DefaultBlockAppender

Only display the first DotTip in the new user guide in the
DefaultBlockAppender that appears in a new post. This prevents the guide
from appearing in an existing post that contains empty blocks.
  • Loading branch information
noisysocks authored Jun 7, 2018
1 parent be00077 commit e809505
Show file tree
Hide file tree
Showing 29 changed files with 912 additions and 31 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ module.exports = {
"selector": "ImportDeclaration[source.value=/^core-blocks$/]",
"message": "Use @wordpress/core-blocks as import path instead."
},
{
"selector": "ImportDeclaration[source.value=/^nux$/]",
"message": "Use @wordpress/nux as import path instead."
},
{
selector: 'CallExpression[callee.name="deprecated"] Property[key.name="version"][value.value=/' + majorMinorRegExp + '/]',
message: 'Deprecated functions must be removed before releasing this version.',
Expand Down
29 changes: 22 additions & 7 deletions components/popover/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,32 @@ const isMobileViewport = () => window.innerWidth < 782;
* @param {Object} anchorRect Anchor Rect.
* @param {Object} contentSize Content Size.
* @param {string} xAxis Desired xAxis.
* @param {string} chosenYAxis yAxis to be used.
* @param {boolean} expandOnMobile Whether to expand the popover on mobile or not.
*
* @return {Object} Popover xAxis position and constraints.
*/
export function computePopoverXAxisPosition( anchorRect, contentSize, xAxis ) {
export function computePopoverXAxisPosition( anchorRect, contentSize, xAxis, chosenYAxis ) {
const { width } = contentSize;
const popoverLeft = Math.round( anchorRect.left + ( anchorRect.width / 2 ) );

// x axis alignment choices
const anchorMidPoint = Math.round( anchorRect.left + ( anchorRect.width / 2 ) );
const centerAlignment = {
popoverLeft: anchorMidPoint,
contentWidth: (
( popoverLeft - ( width / 2 ) > 0 ? ( width / 2 ) : popoverLeft ) +
( popoverLeft + ( width / 2 ) > window.innerWidth ? window.innerWidth - popoverLeft : ( width / 2 ) )
( anchorMidPoint - ( width / 2 ) > 0 ? ( width / 2 ) : anchorMidPoint ) +
( anchorMidPoint + ( width / 2 ) > window.innerWidth ? window.innerWidth - anchorMidPoint : ( width / 2 ) )
),
};
const leftAlignmentX = chosenYAxis === 'middle' ? anchorRect.left : anchorMidPoint;

This comment has been minimized.

Copy link
@ellatrix

ellatrix Nov 29, 2019

Member

@noisysocks @youknowriad Do you remember why anchorMidPoint is used for top and bottom, even though it's asking for left? This doesn't make any sense. Would have been good to comment on this.

This comment has been minimized.

Copy link
@ellatrix

ellatrix Nov 29, 2019

Member

It makes even less sense now that I see that the DotTip's position is set to middle right... So why has it changed to anchorMidPoint for the rest?

This comment has been minimized.

Copy link
@ellatrix

ellatrix Nov 29, 2019

Member

I figured out how it works now. In case of y not being set to middle, the x parameter is actually the side to which the popover should float while the x position is actually centre. This is a bit confusing.

const leftAlignment = {
contentWidth: popoverLeft - width > 0 ? width : popoverLeft,
popoverLeft: leftAlignmentX,
contentWidth: leftAlignmentX - width > 0 ? width : leftAlignmentX,
};
const rightAlignmentX = chosenYAxis === 'middle' ? anchorRect.right : anchorMidPoint;
const rightAlignment = {
contentWidth: popoverLeft + width > window.innerWidth ? window.innerWidth - popoverLeft : width,
popoverLeft: rightAlignmentX,
contentWidth: rightAlignmentX + width > window.innerWidth ? window.innerWidth - rightAlignmentX : width,
};

// Choosing the x axis
Expand All @@ -48,6 +54,15 @@ export function computePopoverXAxisPosition( anchorRect, contentSize, xAxis ) {
contentWidth = chosenWidth !== width ? chosenWidth : null;
}

let popoverLeft;
if ( chosenXAxis === 'center' ) {
popoverLeft = centerAlignment.popoverLeft;
} else if ( chosenXAxis === 'left' ) {
popoverLeft = leftAlignment.popoverLeft;
} else {
popoverLeft = rightAlignment.popoverLeft;
}

return {
xAxis: chosenXAxis,
popoverLeft,
Expand Down Expand Up @@ -131,8 +146,8 @@ export function computePopoverYAxisPosition( anchorRect, contentSize, yAxis ) {
export function computePopoverPosition( anchorRect, contentSize, position = 'top', expandOnMobile = false ) {
const [ yAxis, xAxis = 'center' ] = position.split( ' ' );

const xAxisPosition = computePopoverXAxisPosition( anchorRect, contentSize, xAxis );
const yAxisPosition = computePopoverYAxisPosition( anchorRect, contentSize, yAxis );
const xAxisPosition = computePopoverXAxisPosition( anchorRect, contentSize, xAxis, yAxisPosition.yAxis );

return {
isMobile: isMobileViewport() && expandOnMobile,
Expand Down
1 change: 1 addition & 0 deletions components/tooltip/style.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.components-tooltip.components-popover {
pointer-events: none;
z-index: z-index( '.components-tooltip' );

&:before {
border-color: transparent;
Expand Down
6 changes: 6 additions & 0 deletions edit-post/assets/stylesheets/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ $z-layers: (
'.components-autocomplete__results': 1000000,

'.skip-to-selected-block': 100000,

// Show NUX tips above popovers, wp-admin menus, submenus, and sidebar:
'.nux-dot-tip': 1000001,

// Show tooltips above NUX tips, wp-admin menus, submenus, and sidebar:
'.components-tooltip': 1000002
);

@function z-index( $key ) {
Expand Down
7 changes: 6 additions & 1 deletion edit-post/components/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@wordpress/editor';
import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/element';
import { DotTip } from '@wordpress/nux';

/**
* Internal dependencies
Expand Down Expand Up @@ -57,7 +58,11 @@ function Header( {
onClick={ toggleGeneralSidebar }
isToggled={ isEditorSidebarOpened }
aria-expanded={ isEditorSidebarOpened }
/>
>
<DotTip id="core/editor.settings">
{ __( 'You’ll find more settings for your page and blocks in the sidebar. Click ‘Settings’ to open it.' ) }
</DotTip>
</IconButton>
<PinnedPlugins.Slot />
<MoreMenu />
</div>
Expand Down
8 changes: 8 additions & 0 deletions edit-post/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { registerCoreBlocks } from '@wordpress/core-blocks';
import { render, unmountComponentAtNode } from '@wordpress/element';
import { dispatch } from '@wordpress/data';

/**
* Internal dependencies
Expand Down Expand Up @@ -73,6 +74,13 @@ export function initializeEditor( id, postType, postId, settings, overridePost )

registerCoreBlocks();

dispatch( 'core/nux' ).triggerGuide( [
'core/editor.inserter',
'core/editor.settings',
'core/editor.preview',
'core/editor.publish',
] );

render(
<Editor settings={ settings } onError={ reboot } postId={ postId } postType={ postType } overridePost={ overridePost } />,
target
Expand Down
31 changes: 23 additions & 8 deletions editor/components/default-block-appender/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { get } from 'lodash';

/**
Expand All @@ -11,6 +12,7 @@ import { compose } from '@wordpress/element';
import { getDefaultBlockName } from '@wordpress/blocks';
import { decodeEntities } from '@wordpress/utils';
import { withSelect, withDispatch } from '@wordpress/data';
import { DotTip } from '@wordpress/nux';

/**
* Internal dependencies
Expand All @@ -28,6 +30,7 @@ export function DefaultBlockAppender( {
placeholder,
layout,
rootUID,
hasTip,
} ) {
if ( isLocked || ! isVisible ) {
return null;
Expand All @@ -38,7 +41,9 @@ export function DefaultBlockAppender( {
return (
<div
data-root-uid={ rootUID || '' }
className="editor-default-block-appender">
className={ classnames( 'editor-default-block-appender', {
'has-tip': hasTip,
} ) }>
<BlockDropZone rootUID={ rootUID } layout={ layout } />
<input
role="button"
Expand All @@ -52,17 +57,19 @@ export function DefaultBlockAppender( {
value={ showPrompt ? value : '' }
/>
<InserterWithShortcuts rootUID={ rootUID } layout={ layout } />
<Inserter position="top right" />
<Inserter position="top right">
<DotTip id="core/editor.inserter">
{ __( 'Welcome to the wonderful world of blocks! Click ‘Add block’ to insert different kinds of content—text, images, quotes, video, lists, and much more.' ) }
</DotTip>
</Inserter>
</div>
);
}
export default compose(
withSelect( ( select, ownProps ) => {
const {
getBlockCount,
getBlock,
getEditorSettings,
} = select( 'core/editor' );
const { getBlockCount, getBlock, getEditorSettings } = select( 'core/editor' );
const { isTipVisible } = select( 'core/nux' );

const isEmpty = ! getBlockCount( ownProps.rootUID );
const lastBlock = getBlock( ownProps.lastBlockUID );
const isLastBlockDefault = get( lastBlock, [ 'name' ] ) === getDefaultBlockName();
Expand All @@ -73,16 +80,20 @@ export default compose(
showPrompt: isEmpty,
isLocked: !! templateLock,
placeholder: bodyPlaceholder,
hasTip: isTipVisible( 'core/editor.inserter' ),
};
} ),
withDispatch( ( dispatch, ownProps ) => {
const {
insertDefaultBlock,
startTyping,
} = dispatch( 'core/editor' );

const { dismissTip } = dispatch( 'core/nux' );

return {
onAppend() {
const { layout, rootUID } = ownProps;
const { layout, rootUID, hasTip } = ownProps;

let attributes;
if ( layout ) {
Expand All @@ -91,6 +102,10 @@ export default compose(

insertDefaultBlock( attributes, rootUID );
startTyping();

if ( hasTip ) {
dismissTip( 'core/editor.inserter' );
}
},
};
} ),
Expand Down
14 changes: 7 additions & 7 deletions editor/components/default-block-appender/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ $empty-paragraph-height: $text-editor-font-size * 4;
}
}

// Don't show inserter until mousing
.editor-inserter__toggle:not( [aria-expanded="true"] ) {
opacity: 0;
&:hover .editor-inserter-with-shortcuts {
opacity: 1;
}

&:hover {
.editor-inserter-with-shortcuts {
opacity: 1;
// Show the inserter if mousing over or there is a tip
&:not( .has-tip ) {
.editor-inserter__toggle:not( [aria-expanded="true"] ) {
opacity: 0;
}

.editor-inserter__toggle {
&:hover .editor-inserter__toggle {
opacity: 1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ exports[`DefaultBlockAppender should append a default block when input focused 1
<WithSelect(WithDispatch(InserterWithShortcuts)) />
<WithSelect(WithDispatch(Inserter))
position="top right"
/>
>
<WithSelect(WithDispatch(DotTip))
id="core/editor.inserter"
>
Welcome to the wonderful world of blocks! Click ‘Add block’ to insert different kinds of content—text, images, quotes, video, lists, and much more.
</WithSelect(WithDispatch(DotTip))>
</WithSelect(WithDispatch(Inserter))>
</div>
`;

Expand All @@ -62,7 +68,13 @@ exports[`DefaultBlockAppender should match snapshot 1`] = `
<WithSelect(WithDispatch(InserterWithShortcuts)) />
<WithSelect(WithDispatch(Inserter))
position="top right"
/>
>
<WithSelect(WithDispatch(DotTip))
id="core/editor.inserter"
>
Welcome to the wonderful world of blocks! Click ‘Add block’ to insert different kinds of content—text, images, quotes, video, lists, and much more.
</WithSelect(WithDispatch(DotTip))>
</WithSelect(WithDispatch(Inserter))>
</div>
`;

Expand All @@ -86,6 +98,12 @@ exports[`DefaultBlockAppender should optionally show without prompt 1`] = `
<WithSelect(WithDispatch(InserterWithShortcuts)) />
<WithSelect(WithDispatch(Inserter))
position="top right"
/>
>
<WithSelect(WithDispatch(DotTip))
id="core/editor.inserter"
>
Welcome to the wonderful world of blocks! Click ‘Add block’ to insert different kinds of content—text, images, quotes, video, lists, and much more.
</WithSelect(WithDispatch(DotTip))>
</WithSelect(WithDispatch(Inserter))>
</div>
`;
6 changes: 5 additions & 1 deletion editor/components/post-preview-button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { get } from 'lodash';
*/
import { Component, compose } from '@wordpress/element';
import { Button, ifCondition } from '@wordpress/components';
import { _x } from '@wordpress/i18n';
import { __, _x } from '@wordpress/i18n';
import { withSelect, withDispatch } from '@wordpress/data';
import { DotTip } from '@wordpress/nux';

export class PostPreviewButton extends Component {
constructor() {
Expand Down Expand Up @@ -107,6 +108,9 @@ export class PostPreviewButton extends Component {
disabled={ ! isSaveable }
>
{ _x( 'Preview', 'imperative verb' ) }
<DotTip id="core/editor.preview">
{ __( 'Click ‘Preview’ to load a preview of this page, so you can make sure you’re happy with your blocks.' ) }
</DotTip>
</Button>
);
}
Expand Down
4 changes: 4 additions & 0 deletions editor/components/post-publish-panel/toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Button } from '@wordpress/components';
import { compose } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { withSelect } from '@wordpress/data';
import { DotTip } from '@wordpress/nux';

/**
* Internal Dependencies
Expand Down Expand Up @@ -50,6 +51,9 @@ function PostPublishPanelToggle( {
isBusy={ isSaving && isPublished }
>
{ isBeingScheduled ? __( 'Schedule…' ) : __( 'Publish…' ) }
<DotTip id="core/editor.publish">
{ __( 'Finished writing? That’s great, let’s get this published right now. Just click ‘Publish’ and you’re good to go.' ) }
</DotTip>
</Button>
);
}
Expand Down
20 changes: 18 additions & 2 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/core-blocks/index.js' ),
true
);
wp_register_script(
'wp-nux',
gutenberg_url( 'build/nux/index.js' ),
array( 'wp-element', 'wp-components', 'wp-data', 'wp-i18n', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/nux/index.js' ),
true
);
// Loading the old editor and its config to ensure the classic block works as expected.
wp_add_inline_script(
'editor', 'window.wp.oldEditor = window.wp.editor;', 'after'
Expand Down Expand Up @@ -361,6 +368,7 @@ function gutenberg_register_scripts_and_styles() {
'tinymce-latest-lists',
'tinymce-latest-paste',
'tinymce-latest-table',
'wp-nux',
),
filemtime( gutenberg_dir_path() . 'build/editor/index.js' )
);
Expand Down Expand Up @@ -408,15 +416,15 @@ function gutenberg_register_scripts_and_styles() {
wp_register_style(
'wp-editor',
gutenberg_url( 'build/editor/style.css' ),
array( 'wp-components', 'wp-editor-font' ),
array( 'wp-components', 'wp-editor-font', 'wp-nux' ),
filemtime( gutenberg_dir_path() . 'build/editor/style.css' )
);
wp_style_add_data( 'wp-editor', 'rtl', 'replace' );

wp_register_style(
'wp-edit-post',
gutenberg_url( 'build/edit-post/style.css' ),
array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-core-blocks' ),
array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-core-blocks', 'wp-nux' ),
filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' )
);
wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' );
Expand Down Expand Up @@ -450,6 +458,14 @@ function gutenberg_register_scripts_and_styles() {
);
wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );

wp_register_style(
'wp-nux',
gutenberg_url( 'build/nux/style.css' ),
array( 'wp-components' ),
filemtime( gutenberg_dir_path() . 'build/nux/style.css' )
);
wp_style_add_data( 'wp-nux', 'rtl', 'replace' );

wp_register_style(
'wp-core-blocks-theme',
gutenberg_url( 'build/core-blocks/theme.css' ),
Expand Down
Loading

0 comments on commit e809505

Please sign in to comment.