Skip to content

Commit

Permalink
InlineTip: Use Modal instead of window.confirm()
Browse files Browse the repository at this point in the history
  • Loading branch information
noisysocks committed Aug 2, 2019
1 parent 609a09c commit b1270ca
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 86 deletions.
11 changes: 8 additions & 3 deletions packages/components/src/notice/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@ function Notice( {
actions = [],
__unstableHTML,
} ) {
const classes = classnames( className, 'components-notice', 'is-' + status, {
'is-dismissible': isDismissible,
} );
const classes = classnames(
className,
'components-notice',
status ? 'is-' + status : undefined,
{
'is-dismissible': isDismissible,
}
);

if ( __unstableHTML ) {
children = <RawHTML>{ children }</RawHTML>;
Expand Down
29 changes: 11 additions & 18 deletions packages/e2e-tests/specs/nux.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,43 +40,36 @@ describe( 'New User Experience (NUX)', () => {
expect( blockInspectorTip ).not.toBeNull();
} );

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' );

it( 'should dismiss a single tip if X button is clicked and confirmation is dismissed', async () => {
// Open up the inserter.
await openGlobalBlockInserter();

// Dismiss the upcoming confirm() dialog.
page.once( 'dialog', async ( dialog ) => {
await dialog.dismiss();
} );

// Click the tip's X button.
await page.click( '.block-editor-inserter__tip button[aria-label="Dismiss this notice"]' );

// Dismiss the confirmation modal.
const [ noButton ] = await page.$x( '//*[contains(@class, "nux-hide-tips-confirmation")]//button[text()="No"]' );
await noButton.click();

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

// Tips should still be enabled.
expect( await areTipsEnabled() ).toBe( true );

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

it( 'should disable all tips if X button is clicked and dialog is confirmed', async () => {
it( 'should disable all tips if X button is clicked and confirmation is confirmed', async () => {
// Open up the inserter.
await openGlobalBlockInserter();

// Dismiss the tip. (The confirm() dialog will automatically be accepted.)
// Click the tip's X button.
await page.click( '.block-editor-inserter__tip button[aria-label="Dismiss this notice"]' );

// Accept the confirmation modal.
const [ disableTipsButton ] = await page.$x( '//*[contains(@class, "nux-hide-tips-confirmation")]//button[text()="Disable Tips"]' );
await disableTipsButton.click();

// The tip should be gone.
const inserterTip = await page.$( '.block-editor-inserter__tip' );
expect( inserterTip ).toBeNull();
Expand Down
78 changes: 59 additions & 19 deletions packages/nux/src/components/inline-tip/index.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,80 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { Notice } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { Notice, Modal, Button } 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;
}
export function InlineTip( {
children,
className,
isTipVisible,
onDisableTips,
onDismissTip,
} ) {
const [ isConfirmationVisible, setIsConfirmationVisible ] = useState( false );

const openConfirmation = () => setIsConfirmationVisible( true );
const closeConfirmation = () => setIsConfirmationVisible( false );

const dismissTip = () => {
onDismissTip();
closeConfirmation();
};

const disableTips = () => {
onDisableTips();
closeConfirmation();
};

return (
<Notice className={ className } onRemove={ onRemove }>
{ children }
</Notice>
<>
{ isTipVisible && (
<Notice
className={ classnames( 'nux-inline-tip', className ) }
onRemove={ openConfirmation }
>
{ children }
</Notice>
) }

{ isConfirmationVisible && (
<Modal
className="nux-hide-tips-confirmation"
title={ __( 'Hide Tips' ) }
onRequestClose={ closeConfirmation }
>
{ __( 'Would you like to disable tips like these in the future?' ) }
<div className="nux-hide-tips-confirmation__buttons">
<Button isDefault isLarge onClick={ dismissTip }>
{ __( 'No' ) }
</Button>
<Button isPrimary isLarge onClick={ disableTips }>
{ __( 'Disable Tips' ) }
</Button>
</div>
</Modal>
) }
</>
);
}

export default compose(
withSelect( ( select, { tipId } ) => ( {
isVisible: select( 'core/nux' ).isTipVisible( tipId ),
isTipVisible: select( 'core/nux' ).isTipVisible( tipId ),
} ) ),
withDispatch( ( dispatch, { tipId } ) => {
const { disableTips, dismissTip } = dispatch( 'core/nux' );

return {
onRemove() {
// Disable reason: We don't yet have a <Confirm> 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 );
}
},
onDismissTip: () => dismissTip( tipId ),
onDisableTips: disableTips,
};
} )
)( InlineTip );
13 changes: 13 additions & 0 deletions packages/nux/src/components/inline-tip/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.nux-hide-tips-confirmation .components-modal__header {
border-bottom: none;
margin-bottom: 0;
}

.nux-hide-tips-confirmation__buttons {
margin-top: 2rem;
text-align: right;

.components-button {
margin-left: 10px;
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`InlineTip should render correctly 1`] = `
<div
className="components-notice is-undefined is-dismissible"
>
<div
className="components-notice__content"
<Fragment>
<Notice
className="nux-inline-tip"
onRemove={[Function]}
>
It looks like you’re writing a letter. Would you like help?
</div>
<button
aria-label="Dismiss this notice"
className="components-button components-icon-button components-notice__dismiss"
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="dashicon dashicons-no"
focusable="false"
height={20}
role="img"
viewBox="0 0 20 20"
width={20}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.12 10l3.53 3.53-2.12 2.12L10 12.12l-3.54 3.54-2.12-2.12L7.88 10 4.34 6.46l2.12-2.12L10 7.88l3.54-3.53 2.12 2.12z"
/>
</svg>
</button>
</div>
</Notice>
</Fragment>
`;
44 changes: 26 additions & 18 deletions packages/nux/src/components/inline-tip/test/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
/**
* External dependencies
*/
import TestRenderer from 'react-test-renderer';

/**
* WordPress dependencies
*/
import { Notice } from '@wordpress/components';
import { shallow, mount } from 'enzyme';

/**
* Internal dependencies
Expand All @@ -15,31 +10,44 @@ import { InlineTip } from '../';

describe( 'InlineTip', () => {
it( 'should not render anything if invisible', () => {
const renderer = TestRenderer.create(
<InlineTip isVisible={ false }>
const wrapper = shallow(
<InlineTip isTipVisible={ false }>
It looks like you’re writing a letter. Would you like help?
</InlineTip>
);
expect( renderer.root.children ).toHaveLength( 0 );
expect( wrapper.children() ).toHaveLength( 0 );
} );

it( 'should render correctly', () => {
const renderer = TestRenderer.create(
<InlineTip isVisible>
const wrapper = shallow(
<InlineTip isTipVisible>
It looks like you’re writing a letter. Would you like help?
</InlineTip>
);
expect( wrapper ).toMatchSnapshot();
} );

it( 'calls `onDismissTip` when the tip and confirmation are dismissed', () => {
const onDismissTip = jest.fn();
const wrapper = mount(
<InlineTip isTipVisible onDismissTip={ onDismissTip }>
It looks like you’re writing a letter. Would you like help?
</InlineTip>
);
expect( renderer ).toMatchSnapshot();
wrapper.find( 'button[aria-label="Dismiss this notice"]' ).simulate( 'click' );
wrapper.find( 'button' ).find( { children: 'No' } ).simulate( 'click' );
expect( onDismissTip ).toHaveBeenCalled();
} );

it( 'calls `onRemove` when the rendered notice is dismissed', () => {
const onRemove = jest.fn();
const renderer = TestRenderer.create(
<InlineTip isVisible onRemove={ onRemove }>
it( 'calls `onDisableTips` when the tip is dismissed and the confirmation is accepted', () => {
const onDisableTips = jest.fn();
const wrapper = mount(
<InlineTip isTipVisible onDisableTips={ onDisableTips }>
It looks like you’re writing a letter. Would you like help?
</InlineTip>
);
renderer.root.findByType( Notice ).props.onRemove();
expect( onRemove ).toHaveBeenCalled();
wrapper.find( 'button[aria-label="Dismiss this notice"]' ).simulate( 'click' );
wrapper.find( 'button' ).find( { children: 'Disable Tips' } ).simulate( 'click' );
expect( onDisableTips ).toHaveBeenCalled();
} );
} );
1 change: 1 addition & 0 deletions packages/nux/src/style.scss
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@import "./components/dot-tip/style.scss";
@import "./components/inline-tip/style.scss";

0 comments on commit b1270ca

Please sign in to comment.