Skip to content

Commit

Permalink
Block Directory: Store refactor (#22388)
Browse files Browse the repository at this point in the history
* Pass along action parameter

* Fix up comments

* Use shared package `data-controls`

* Remove `uninstallBlock`, `removeInstalledBlockType`

* Update controls to return promises

Includes very basic testing, since I'm having trouble with both promises and document mocking

* Update response format from API

* Refactor install & download into one single, multi-step action generator

This walks through each step, and removes the need for onError & onSuccess

* Switch to new action generator for installation

* Update Notice reducer to store the notice text

This will let us return more descriptive error messages for each kind of error, instead of generic install/download failed messages

* Bring `onSelect` functionality back

* Fix notice description

* Switch to `useDispatch` hook

* Update packages/block-directory/src/store/controls.js

Co-authored-by: Steven Dufresne <steve.dufresne@automattic.com>

* Remove extra export

* Remove unnecessary resets

* Update variable name to reflect value

* Add test to block list item

* Remove unused props

* Remove behavior test, update to use snapshot testing

Behavior is tested in DownloadableBlockListItem tests

* Update notice block to use `useSelect`

* Update package-lock

* Check file extension before trying to load scripts & styles

* Fix class name

* Bring back `setIsInstalling`

* Add i18n & punctuation to error messages

* Add a default error message

* Return a success/failure indicator for installation

This prevents an error where `onSelect` tries to run on a block that fails to install

* Remove unused state property, update tests

* Add selector tests for `getDownloadableBlocks`

* Update mocked API response

* Add default message for installation failures

Co-authored-by: Steven Dufresne <steve.dufresne@automattic.com>
  • Loading branch information
ryelle and StevenDufresne authored May 25, 2020
1 parent 3e434cc commit f717994
Show file tree
Hide file tree
Showing 25 changed files with 678 additions and 566 deletions.
2 changes: 1 addition & 1 deletion lib/class-wp-rest-block-directory-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public function install_block( $request ) {
return WP_Error( $activate_result->get_error_code(), $activate_result->get_error_message() );
}

return rest_ensure_response( true );
return rest_ensure_response( array( 'success' => true ) );
}

/**
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-directory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@wordpress/components": "file:../components",
"@wordpress/compose": "file:../compose",
"@wordpress/data": "file:../data",
"@wordpress/data-controls": "file:../data-controls",
"@wordpress/element": "file:../element",
"@wordpress/i18n": "file:../i18n",
"@wordpress/icons": "file:../icons",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DownloadableBlockListItem should render a block item 1`] = `
<li
className="block-directory-downloadable-block-list-item"
>
<article
className="block-directory-downloadable-block-list-item__panel"
>
<header
className="block-directory-downloadable-block-list-item__header"
>
<WithSelect(DownloadableBlockHeader)
icon="block-default"
onClick={[MockFunction]}
rating={5}
title="Boxer"
/>
</header>
<section
className="block-directory-downloadable-block-list-item__body"
>
<DownloadableBlockNotice
block={
Object {
"active_installs": 0,
"assets": Array [
"http://plugins.svn.wordpress.org/boxer-block/trunk/build/index.js",
"http://plugins.svn.wordpress.org/boxer-block/trunk/build/view.js",
],
"author": "CK Lee",
"author_block_count": "1",
"author_block_rating": 5,
"description": "Boxer is a Block that puts your WordPress posts into boxes on a page.",
"humanized_updated": "3 months ago",
"icon": "block-default",
"id": "boxer-block",
"name": "boxer/boxer",
"rating": 5,
"rating_count": 1,
"title": "Boxer",
}
}
onClick={[MockFunction]}
/>
<DownloadableBlockInfo
description="Boxer is a Block that puts your WordPress posts into boxes on a page."
/>
</section>
<footer
className="block-directory-downloadable-block-list-item__footer"
>
<DownloadableBlockAuthorInfo
author="CK Lee"
/>
</footer>
</article>
</li>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const item = {
name: 'boxer/boxer',
title: 'Boxer',
description:
'Boxer is a Block that puts your WordPress posts into boxes on a page.',
id: 'boxer-block',
icon: 'block-default',
rating: 5,
rating_count: 1,
active_installs: 0,
author_block_rating: 5,
author_block_count: '1',
author: 'CK Lee',
assets: [
'http://plugins.svn.wordpress.org/boxer-block/trunk/build/index.js',
'http://plugins.svn.wordpress.org/boxer-block/trunk/build/view.js',
],
humanized_updated: '3 months ago',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* Internal dependencies
*/
import DownloadableBlockListItem from '../index';
import DownloadableBlockHeader from '../../downloadable-block-header';
import { item } from './fixtures';

describe( 'DownloadableBlockListItem', () => {
it( 'should render a block item', () => {
const wrapper = shallow(
<DownloadableBlockListItem onClick={ jest.fn() } item={ item } />
);

expect( wrapper ).toMatchSnapshot();
} );

it( 'should try to install the block plugin', () => {
const onClick = jest.fn();
const wrapper = shallow(
<DownloadableBlockListItem onClick={ onClick } item={ item } />
);

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

expect( onClick ).toHaveBeenCalledTimes( 1 );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,27 @@
*/
import { __ } from '@wordpress/i18n';
import { Button, Notice } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { DOWNLOAD_ERROR_NOTICE_ID } from '../../store/constants';
export const DownloadableBlockNotice = ( { block, onClick } ) => {
const errorNotice = useSelect(
( select ) =>
select( 'core/block-directory' ).getErrorNoticeForBlock( block.id ),
[ block ]
);

export const DownloadableBlockNotice = ( { block, errorNotices, onClick } ) => {
if ( ! errorNotices[ block.id ] ) {
if ( ! errorNotice ) {
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"
className="block-directory-downloadable-block-notice"
>
<div className="block-directory-downloadable-blocks__notice-content">
{ copy }
<div className="block-directory-downloadable-block-notice__content">
{ errorNotice }
</div>
<Button
isSmall
Expand All @@ -46,8 +38,4 @@ export const DownloadableBlockNotice = ( { block, errorNotices, onClick } ) => {
);
};

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

.block-directory-downloadable-blocks__notice-content {
.block-directory-downloadable-block-notice__content {
padding-right: 12px;
margin-bottom: 8px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DownloadableBlockNotice Rendering should return something when there are error notices 1`] = `
<Notice
className="block-directory-downloadable-block-notice"
isDismissible={false}
status="error"
>
<div
className="block-directory-downloadable-block-notice__content"
>
Plugin not found.
</div>
<ForwardRef(Button)
isPrimary={true}
isSmall={true}
onClick={[Function]}
>
Retry
</ForwardRef(Button)>
</Notice>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,52 @@ import { shallow } from 'enzyme';
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { useSelect } from '@wordpress/data';

/**
* 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 }
/>
);
};
jest.mock( '@wordpress/data/src/components/use-select', () => {
// This allows us to tweak the returned value on each test
const mock = jest.fn();
return mock;
} );

describe( 'DownloadableBlockNotice', () => {
describe( 'Rendering', () => {
it( 'should return null when there are no error notices', () => {
const wrapper = getContainer( { block: plugin } );
useSelect.mockImplementation( () => false );
const wrapper = shallow(
<DownloadableBlockNotice
block={ plugin }
onClick={ jest.fn() }
/>
);
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 );
useSelect.mockImplementation( () => 'Plugin not found.' );
const wrapper = shallow(
<DownloadableBlockNotice
block={ plugin }
onClick={ jest.fn() }
/>
);
expect( wrapper ).toMatchSnapshot();
} );
} );

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

useSelect.mockImplementation( () => 'Plugin not found.' );
const onClick = jest.fn();
const wrapper = getContainer( {
block: plugin,
onClick,
errorNotices,
} );
const wrapper = shallow(
<DownloadableBlockNotice block={ plugin } onClick={ onClick } />
);

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

Expand Down
Loading

0 comments on commit f717994

Please sign in to comment.