Skip to content

Commit

Permalink
Block Directory: Add error messages inline. (#20001)
Browse files Browse the repository at this point in the history
* Block Directory: Add error messages inline.

* Make the linter happy

* Place the retry button on its own line.

* Update classNames to match standards

* Get the error notices in `DownloadableBlockNotice`

This prevents some prop-drilling by getting the notices when we need them

* Get `isLoading` in `DownloadableBlockHeader`

This prevents some prop-drilling by getting the loading status when we need it

* Avoid `children` abstraction in item

* Refactor install callback

This consolidates the install logic into one callback function, simplifying the list component

* Disable the "Add Block" button when installing

* Fix broken tests.

* Update padding under error messages.

Co-authored-by: Kelly Dwan <[email protected]>
  • Loading branch information
StevenDufresne and ryelle authored May 18, 2020
1 parent 6c80cc3 commit 573767e
Show file tree
Hide file tree
Showing 16 changed files with 599 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { BlockIcon } from '@wordpress/block-editor';
import BlockRatings from '../block-ratings';

function DownloadableBlockHeader( {
export function DownloadableBlockHeader( {
icon,
title,
rating,
ratingCount,
isLoading,
onClick,
} ) {
return (
Expand Down Expand Up @@ -45,15 +47,23 @@ function DownloadableBlockHeader( {
</div>
<Button
isSecondary
isBusy={ isLoading }
disabled={ isLoading }
onClick={ ( event ) => {
event.preventDefault();
onClick();
if ( ! isLoading ) {
onClick();
}
} }
>
{ __( 'Add block' ) }
{ isLoading ? __( 'Adding…' ) : __( 'Add block' ) }
</Button>
</div>
);
}

export default DownloadableBlockHeader;
export default withSelect( ( select ) => {
return {
isLoading: select( 'core/block-directory' ).isInstalling(),
};
} )( DownloadableBlockHeader );
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@ import { shallow } from 'enzyme';
* WordPress dependencies
*/
import { BlockIcon } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';

/**
* Internal dependencies
*/
import DownloadableBlockHeader from '../index';
import { DownloadableBlockHeader } from '../index';
import { pluginWithImg, pluginWithIcon } from './fixtures';

const getContainer = ( { icon, title, rating, ratingCount } ) => {
const getContainer = (
{ icon, title, rating, ratingCount },
onClick = jest.fn(),
isLoading = false
) => {
return shallow(
<DownloadableBlockHeader
icon={ icon }
onClick={ () => {} }
onClick={ onClick }
title={ title }
rating={ rating }
ratingCount={ ratingCount }
isLoading={ isLoading }
/>
);
};
Expand Down Expand Up @@ -50,4 +56,28 @@ describe( 'DownloadableBlockHeader', () => {
expect( wrapper.find( BlockIcon ) ).toHaveLength( 1 );
} );
} );

describe( 'user interaction', () => {
test( 'should trigger the onClick function', () => {
const onClickMock = jest.fn();
const wrapper = getContainer( pluginWithIcon, onClickMock );
const event = {
preventDefault: jest.fn(),
};
wrapper.find( Button ).simulate( 'click', event );
expect( onClickMock ).toHaveBeenCalledTimes( 1 );
expect( event.preventDefault ).toHaveBeenCalled();
} );

test( 'should not trigger the onClick function if loading', () => {
const onClickMock = jest.fn();
const wrapper = getContainer( pluginWithIcon, onClickMock, true );
const event = {
preventDefault: jest.fn(),
};
wrapper.find( Button ).simulate( 'click', event );
expect( event.preventDefault ).toHaveBeenCalled();
expect( onClickMock ).toHaveBeenCalledTimes( 0 );
} );
} );
} );
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/**
* Internal dependencies
*/
import DownloadableBlockHeader from '../downloadable-block-header';
import DownloadableBlockAuthorInfo from '../downloadable-block-author-info';
import DownloadableBlockHeader from '../downloadable-block-header';
import DownloadableBlockInfo from '../downloadable-block-info';
import DownloadableBlockNotice from '../downloadable-block-notice';

function DownloadableBlockListItem( { item, onClick } ) {
const {
Expand Down Expand Up @@ -32,6 +33,10 @@ function DownloadableBlockListItem( { item, onClick } ) {
/>
</header>
<section className="block-directory-downloadable-block-list-item__body">
<DownloadableBlockNotice
onClick={ onClick }
block={ item }
/>
<DownloadableBlockInfo
activeInstalls={ activeInstalls }
description={ description }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button, Notice } from '@wordpress/components';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { DOWNLOAD_ERROR_NOTICE_ID } from '../../store/constants';

export const DownloadableBlockNotice = ( { block, errorNotices, onClick } ) => {
if ( ! errorNotices[ block.id ] ) {
return null;
}

// A Failed install is the default error as its the first step
let copy = __( 'Block could not be added.' );

if ( errorNotices[ block.id ] === DOWNLOAD_ERROR_NOTICE_ID ) {
copy = __(
'Block could not be added. There is a problem with the block.'
);
}

return (
<Notice
status="error"
isDismissible={ false }
className="block-directory-downloadable-blocks__notice"
>
<div className="block-directory-downloadable-blocks__notice-content">
{ copy }
</div>
<Button
isSmall
isPrimary
onClick={ () => {
onClick( block );
} }
>
{ __( 'Retry' ) }
</Button>
</Notice>
);
};

export default withSelect( ( select ) => {
return {
errorNotices: select( 'core/block-directory' ).getErrorNotices(),
};
} )( DownloadableBlockNotice );
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.block-directory-downloadable-blocks__notice {
margin: 0 0 16px;
}

.block-directory-downloadable-blocks__notice-content {
padding-right: 12px;
margin-bottom: 8px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const plugin = {
id: 'boxer-block',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';

/**
* Internal dependencies
*/
import { DownloadableBlockNotice } from '../index';
import { plugin } from './fixtures';

import { INSTALL_ERROR_NOTICE_ID } from '../../../store/constants';

const getContainer = ( { block, onClick = jest.fn(), errorNotices = {} } ) => {
return shallow(
<DownloadableBlockNotice
block={ block }
onClick={ onClick }
errorNotices={ errorNotices }
/>
);
};

describe( 'DownloadableBlockNotice', () => {
describe( 'Rendering', () => {
it( 'should return null when there are no error notices', () => {
const wrapper = getContainer( { block: plugin } );
expect( wrapper.isEmptyRender() ).toBe( true );
} );

it( 'should return something when there are error notices', () => {
const errorNotices = {
[ plugin.id ]: INSTALL_ERROR_NOTICE_ID,
};
const wrapper = getContainer( { block: plugin, errorNotices } );
expect( wrapper.length ).toBeGreaterThan( 0 );
} );
} );

describe( 'Behavior', () => {
it( 'should trigger the callback on button click', () => {
const errorNotices = {
[ plugin.id ]: INSTALL_ERROR_NOTICE_ID,
};

const onClick = jest.fn();
const wrapper = getContainer( {
block: plugin,
onClick,
errorNotices,
} );

wrapper.find( Button ).simulate( 'click', { event: {} } );

expect( onClick ).toHaveBeenCalledTimes( 1 );
} );
} );
} );
Loading

0 comments on commit 573767e

Please sign in to comment.