From 2011a876abe06fddc1ef7b9163d125836697e056 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 15 Jul 2019 13:39:40 +1000 Subject: [PATCH] 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. --- package-lock.json | 3 +- packages/block-editor/package.json | 1 + .../src/components/block-inspector/index.js | 4 + .../src/components/block-inspector/style.scss | 3 + .../src/components/inserter/menu.js | 11 +- .../src/components/inserter/style.scss | 4 + .../e2e-test-utils/src/create-new-post.js | 4 - packages/e2e-tests/specs/nux.test.js | 209 ++++++------------ packages/edit-post/package.json | 1 - .../components/editor-initialization/index.js | 15 -- .../components/header/header-toolbar/index.js | 8 +- .../edit-post/src/components/header/index.js | 22 +- .../options-modal/options/deferred.js | 36 --- .../options-modal/options/enable-tips.js | 9 +- .../test/__snapshots__/index.js.snap | 2 +- packages/editor/package.json | 1 - .../components/post-preview-button/index.js | 4 - .../test/__snapshots__/index.js.snap | 10 - .../components/post-publish-button/index.js | 4 - packages/nux/CHANGELOG.md | 4 + .../nux/src/components/inline-tip/README.md | 34 +++ .../nux/src/components/inline-tip/index.js | 40 ++++ .../test/__snapshots__/index.js.snap | 34 +++ .../src/components/inline-tip/test/index.js | 45 ++++ packages/nux/src/index.js | 1 + 25 files changed, 256 insertions(+), 253 deletions(-) delete mode 100644 packages/edit-post/src/components/options-modal/options/deferred.js create mode 100644 packages/nux/src/components/inline-tip/README.md create mode 100644 packages/nux/src/components/inline-tip/index.js create mode 100644 packages/nux/src/components/inline-tip/test/__snapshots__/index.js.snap create mode 100644 packages/nux/src/components/inline-tip/test/index.js diff --git a/package-lock.json b/package-lock.json index 0e27a7383cf08a..146135a21d975e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3739,6 +3739,7 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", + "@wordpress/nux": "file:packages/nux", "@wordpress/rich-text": "file:packages/rich-text", "@wordpress/token-list": "file:packages/token-list", "@wordpress/url": "file:packages/url", @@ -3994,7 +3995,6 @@ "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", "@wordpress/keycodes": "file:packages/keycodes", - "@wordpress/nux": "file:packages/nux", "@wordpress/plugins": "file:packages/plugins", "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", @@ -4036,7 +4036,6 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", - "@wordpress/nux": "file:packages/nux", "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", "@wordpress/wordcount": "file:packages/wordcount", diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 5add09430524e1..546ed42424c784 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -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", diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index e04dbcbd932be1..62665978676a42 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -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 ( <> + + { __( 'The block tab contains additional settings for the selected block.' ) } +
diff --git a/packages/block-editor/src/components/block-inspector/style.scss b/packages/block-editor/src/components/block-inspector/style.scss index 2fdb6d04b44aab..1633e262da3408 100644 --- a/packages/block-editor/src/components/block-inspector/style.scss +++ b/packages/block-editor/src/components/block-inspector/style.scss @@ -6,6 +6,9 @@ text-align: center; } +.block-editor-block-inspector__tip { + margin: 0 0 16px 0; +} .block-editor-block-inspector__card { display: flex; diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index bf659f789d1881..835ffc1c9e85d2 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -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' ) } > + + { __( 'There are Blocks for most types of content: text, headings, images, lists, and lots more!' ) } + { ' ' } + + { __( 'Learn more' ) } + + + { - 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 ); } ); } ); diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 09cebd91d061b5..08f46e37086fac 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -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", diff --git a/packages/edit-post/src/components/editor-initialization/index.js b/packages/edit-post/src/components/editor-initialization/index.js index 51cb90681f8ae8..f7ab4d7f5b008d 100644 --- a/packages/edit-post/src/components/editor-initialization/index.js +++ b/packages/edit-post/src/components/editor-initialization/index.js @@ -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; } diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index 8ff5e2209594df..ca75a3fa3303b9 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -4,7 +4,6 @@ import { compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; -import { DotTip } from '@wordpress/nux'; import { __ } from '@wordpress/i18n'; import { Inserter, @@ -30,12 +29,7 @@ function HeaderToolbar( { hasFixedToolbar, isLargeViewport, showInserter, isText className="edit-post-header-toolbar" aria-label={ toolbarAriaLabel } > -
- - - { __( 'Welcome to the wonderful world of blocks! Click the “+” (“Add block”) button to add a new block. There are blocks available for all kinds of content: you can insert text, headings, images, lists, and lots more!' ) } - -
+ diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index dff5622593603a..b6817580b48d67 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -9,7 +9,6 @@ import { } from '@wordpress/editor'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { DotTip } from '@wordpress/nux'; /** * Internal dependencies @@ -63,19 +62,14 @@ function Header( { forceIsDirty={ hasActiveMetaboxes } forceIsSaving={ isSaving } /> -
- - - { __( 'You’ll find more settings for your page and blocks in the sidebar. Click the cog icon to toggle the sidebar open and closed.' ) } - -
+
diff --git a/packages/edit-post/src/components/options-modal/options/deferred.js b/packages/edit-post/src/components/options-modal/options/deferred.js deleted file mode 100644 index 2a0f84348d23bb..00000000000000 --- a/packages/edit-post/src/components/options-modal/options/deferred.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import BaseOption from './base'; - -class DeferredOption extends Component { - constructor( { isChecked } ) { - super( ...arguments ); - this.state = { - isChecked, - }; - } - - componentWillUnmount() { - if ( this.state.isChecked !== this.props.isChecked ) { - this.props.onChange( this.state.isChecked ); - } - } - - render() { - return ( - this.setState( { isChecked } ) } - /> - ); - } -} - -export default DeferredOption; diff --git a/packages/edit-post/src/components/options-modal/options/enable-tips.js b/packages/edit-post/src/components/options-modal/options/enable-tips.js index 8771f8437ba53a..928cfaaec9afff 100644 --- a/packages/edit-post/src/components/options-modal/options/enable-tips.js +++ b/packages/edit-post/src/components/options-modal/options/enable-tips.js @@ -7,7 +7,7 @@ import { withSelect, withDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import DeferredOption from './deferred'; +import BaseOption from './base'; export default compose( withSelect( ( select ) => ( { @@ -19,9 +19,4 @@ export default compose( onChange: ( isEnabled ) => ( isEnabled ? enableTips() : disableTips() ), }; } ) -)( - // Using DeferredOption here means enableTips() is called when the Options - // modal is dismissed. This stops the NUX guide from appearing above the - // Options modal, which looks totally weird. - DeferredOption -); +)( BaseOption ); diff --git a/packages/edit-post/src/components/options-modal/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/options-modal/test/__snapshots__/index.js.snap index a81252b32b95fb..906cc1d08da940 100644 --- a/packages/edit-post/src/components/options-modal/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/options-modal/test/__snapshots__/index.js.snap @@ -12,7 +12,7 @@ exports[`OptionsModal should match snapshot when the modal is active 1`] = ` - diff --git a/packages/editor/package.json b/packages/editor/package.json index 88ddf540a926aa..4b016788545833 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -40,7 +40,6 @@ "@wordpress/keycodes": "file:../keycodes", "@wordpress/media-utils": "file:../media-utils", "@wordpress/notices": "file:../notices", - "@wordpress/nux": "file:../nux", "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "@wordpress/wordcount": "file:../wordcount", diff --git a/packages/editor/src/components/post-preview-button/index.js b/packages/editor/src/components/post-preview-button/index.js index b09f50b431269d..b77bc4211f6543 100644 --- a/packages/editor/src/components/post-preview-button/index.js +++ b/packages/editor/src/components/post-preview-button/index.js @@ -10,7 +10,6 @@ import { Component, renderToString } from '@wordpress/element'; import { Button, Path, SVG } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { withSelect, withDispatch } from '@wordpress/data'; -import { DotTip } from '@wordpress/nux'; import { ifCondition, compose } from '@wordpress/compose'; import { applyFilters } from '@wordpress/hooks'; @@ -191,9 +190,6 @@ export class PostPreviewButton extends Component { __( '(opens in a new tab)' ) } - - { __( 'Click “Preview” to load a preview of this page, so you can make sure you’re happy with your blocks.' ) } - ); } diff --git a/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap b/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap index fc928b5e12cb9e..587f30bf8a91bb 100644 --- a/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap +++ b/packages/editor/src/components/post-preview-button/test/__snapshots__/index.js.snap @@ -15,11 +15,6 @@ exports[`PostPreviewButton render() should render currentPostLink otherwise 1`] > (opens in a new tab) - - Click “Preview” to load a preview of this page, so you can make sure you’re happy with your blocks. - `; @@ -38,10 +33,5 @@ exports[`PostPreviewButton render() should render previewLink if provided 1`] = > (opens in a new tab) - - Click “Preview” to load a preview of this page, so you can make sure you’re happy with your blocks. - `; diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index ff3bedf5a79fb5..604770d7157ad9 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -11,7 +11,6 @@ import { Component, createRef } from '@wordpress/element'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; -import { DotTip } from '@wordpress/nux'; /** * Internal dependencies @@ -116,9 +115,6 @@ export class PostPublishButton extends Component { { ...componentProps } > { componentChildren } - - { __( 'Finished writing? That’s great, let’s get this published right now. Just click “Publish” and you’re good to go.' ) } - ); } diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md index 546a22e7930b46..7ca31c33a7c6d4 100644 --- a/packages/nux/CHANGELOG.md +++ b/packages/nux/CHANGELOG.md @@ -1,3 +1,7 @@ +# 3.1.0 (Unreleased) + +- Adds the `InlineTip` component. + ## 3.0.6 (2019-01-03) ## 3.0.5 (2018-12-12) diff --git a/packages/nux/src/components/inline-tip/README.md b/packages/nux/src/components/inline-tip/README.md new file mode 100644 index 00000000000000..27fe9364bd039a --- /dev/null +++ b/packages/nux/src/components/inline-tip/README.md @@ -0,0 +1,34 @@ +InlineTip +========= + +`InlineTip` is a React component that renders a single _tip_ on the screen. It has the same appearance as a `Notice`. Each tip is uniquely identified by a string passed to `tipId`. + +## Usage + +```jsx + + Add the product to your shopping cart by clicking ‘Add to Cart’. + +``` + +## Props + +The component accepts the following props: + +### tipId + +A string that uniquely identifies the tip. Identifiers should be prefixed with the name of the plugin, followed by a `/`. For example, `acme/add-to-cart`. + +- Type: `string` +- Required: Yes + +### className + +Class name added to the rendered tip. + +- Type: `string` +- Required: Yes + +### children + +Any React element or elements can be passed as children. They will be rendered within the tip. diff --git a/packages/nux/src/components/inline-tip/index.js b/packages/nux/src/components/inline-tip/index.js new file mode 100644 index 00000000000000..a1bec5742b21d3 --- /dev/null +++ b/packages/nux/src/components/inline-tip/index.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { Notice } from '@wordpress/components'; +import { compose } from '@wordpress/compose'; +import { withSelect, withDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +export function InlineTip( { isVisible, className, onRemove, children } ) { + if ( ! isVisible ) { + return null; + } + + return ( + + { children } + + ); +} + +export default compose( + withSelect( ( select, { tipId } ) => ( { + isVisible: select( 'core/nux' ).isTipVisible( tipId ), + } ) ), + withDispatch( ( dispatch, { tipId } ) => { + const { disableTips, dismissTip } = dispatch( 'core/nux' ); + + return { + onRemove() { + // Disable reason: We don't yet have a component. One day! + // eslint-disable-next-line no-alert + if ( window.confirm( __( 'Would you like to disable tips like these in the future? ' ) ) ) { + disableTips(); + } else { + dismissTip( tipId ); + } + }, + }; + } ) +)( InlineTip ); diff --git a/packages/nux/src/components/inline-tip/test/__snapshots__/index.js.snap b/packages/nux/src/components/inline-tip/test/__snapshots__/index.js.snap new file mode 100644 index 00000000000000..fb596ab9500806 --- /dev/null +++ b/packages/nux/src/components/inline-tip/test/__snapshots__/index.js.snap @@ -0,0 +1,34 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InlineTip should render correctly 1`] = ` +
+
+ It looks like you’re writing a letter. Would you like help? +
+ +
+`; diff --git a/packages/nux/src/components/inline-tip/test/index.js b/packages/nux/src/components/inline-tip/test/index.js new file mode 100644 index 00000000000000..56914b414753b2 --- /dev/null +++ b/packages/nux/src/components/inline-tip/test/index.js @@ -0,0 +1,45 @@ +/** + * External dependencies + */ +import TestRenderer from 'react-test-renderer'; + +/** + * WordPress dependencies + */ +import { Notice } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { InlineTip } from '../'; + +describe( 'InlineTip', () => { + it( 'should not render anything if invisible', () => { + const renderer = TestRenderer.create( + + It looks like you’re writing a letter. Would you like help? + + ); + expect( renderer.root.children ).toHaveLength( 0 ); + } ); + + it( 'should render correctly', () => { + const renderer = TestRenderer.create( + + It looks like you’re writing a letter. Would you like help? + + ); + expect( renderer ).toMatchSnapshot(); + } ); + + it( 'should call onRemove when the remove button is clicked', () => { + const onRemove = jest.fn(); + const renderer = TestRenderer.create( + + It looks like you’re writing a letter. Would you like help? + + ); + renderer.root.findByType( Notice ).props.onRemove(); + expect( onRemove ).toHaveBeenCalled(); + } ); +} ); diff --git a/packages/nux/src/index.js b/packages/nux/src/index.js index a11d17bc96961a..44cd97344d11f6 100644 --- a/packages/nux/src/index.js +++ b/packages/nux/src/index.js @@ -4,3 +4,4 @@ import './store'; export { default as DotTip } from './components/dot-tip'; +export { default as InlineTip } from './components/inline-tip';