Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a "reinstall" button when a block type is not found #22631

Merged
merged 6 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -35,6 +35,7 @@
"@wordpress/data-controls": "file:../data-controls",
"@wordpress/edit-post": "file:../edit-post",
"@wordpress/element": "file:../element",
"@wordpress/hooks": "file:../hooks",
"@wordpress/html-entities": "file:../html-entities",
"@wordpress/i18n": "file:../i18n",
"@wordpress/icons": "file:../icons",
Expand Down
66 changes: 66 additions & 0 deletions packages/block-directory/src/plugins/filter-missing/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { RawHTML } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { Warning } from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import InstallButton from './install-button';

const filterMissing = ( OriginalComponent ) => ( props ) => {
const { originalName, originalUndelimitedContent } = props.attributes;
const { block, hasPermission } = useSelect(
( select ) => {
const { getDownloadableBlocks } = select( 'core/block-directory' );
const blocks = getDownloadableBlocks(
'block:' + originalName
).filter( ( { name } ) => originalName === name );
return {
hasPermission: select( 'core' ).canUser(
'read',
'block-directory/search'
),
block: blocks.length && blocks[ 0 ],
};
},
[ originalName ]
);

if ( ! hasPermission || ! block ) {
return <OriginalComponent { ...props } />;
}

const actions = [
<InstallButton
key="install"
block={ block }
attributes={ props.attributes }
clientId={ props.clientId }
/>,
<Button key="convert" onClick={ props.convertToHTML } isLink>
{ __( 'Keep as HTML' ) }
</Button>,
];

return (
<>
<Warning actions={ actions }>
{ sprintf(
/* translators: %s: block name */
__(
'Your site doesn’t include support for the %s block. You can try installing the block, convert it to a Custom HTML block, or remove it entirely.'
),
block.title || originalName
) }
</Warning>
<RawHTML>{ originalUndelimitedContent }</RawHTML>
</>
);
};

export default filterMissing;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { createBlock, getBlockType, parse } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';

export default function InstallButton( { attributes, block, clientId } ) {
const isInstallingBlock = useSelect( ( select ) =>
select( 'core/block-directory' ).isInstalling( block.id )
);
const { installBlockType } = useDispatch( 'core/block-directory' );
const { replaceBlock } = useDispatch( 'core/block-editor' );

return (
<Button
onClick={ () =>
installBlockType( block ).then( ( success ) => {
if ( success ) {
const blockType = getBlockType( block.name );
const [ originalBlock ] = parse(
attributes.originalContent
);
if ( originalBlock ) {
replaceBlock(
clientId,
createBlock(
blockType.name,
originalBlock.attributes,
originalBlock.innerBlocks
)
);
}
}
} )
}
disabled={ isInstallingBlock }
isBusy={ isInstallingBlock }
isPrimary
>
{ sprintf(
/* translators: %s: block name */
__( 'Install %s' ),
block.title
) }
</Button>
);
}
8 changes: 8 additions & 0 deletions packages/block-directory/src/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
* WordPress dependencies
*/
import { registerPlugin } from '@wordpress/plugins';
import { addFilter } from '@wordpress/hooks';

/**
* Internal dependencies
*/
import AutoBlockUninstaller from '../components/auto-block-uninstaller';
import InserterMenuDownloadableBlocksPanel from './inserter-menu-downloadable-blocks-panel';
import InstalledBlocksPrePublishPanel from './installed-blocks-pre-publish-panel';
import filterMissing from './filter-missing';

registerPlugin( 'block-directory', {
render() {
Expand All @@ -21,3 +23,9 @@ registerPlugin( 'block-directory', {
);
},
} );

addFilter(
'editor.missingEdit',
'block-directory/install-missing',
filterMissing
);
7 changes: 3 additions & 4 deletions packages/block-editor/src/components/warning/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
display: flex;
flex-wrap: wrap;
font-family: $default-font;
font-size: $default-font-size;
padding: ($grid-unit-10 - $border-width - $border-width) $grid-unit-15;
padding: 1em;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having padding set as an em unit could lead to some odd outcomes should the block be broken inside of a parent block. It will inherit its font size. May be better to use rem units.

Copy link
Contributor

@MichaelArestad MichaelArestad Sep 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an open PR to switch to em units and the block Placeholder state already uses em units. That's the reason for the change. Might as well be consistent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up....

I wonder if that PR is considering third party blocks. This may get interesting. 🍿

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That PR looks like it was halting for further testing. That said, this will still be more consistent with the placeholder state for other blocks.


// Block UI appearance.
border: $border-width solid $gray-900;
Expand All @@ -15,7 +14,7 @@
line-height: $default-line-height;
font-family: $default-font;
font-size: $default-font-size;
margin: 1em 0;
margin: 0 0 1em;

}

Expand All @@ -38,7 +37,7 @@
}

.block-editor-warning__action {
margin: 0 0 0 $grid-unit-10;
margin: 0 $grid-unit-10 0 0;
}
}

Expand Down
34 changes: 19 additions & 15 deletions packages/block-library/src/missing/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { compose } from '@wordpress/compose';
import { RawHTML } from '@wordpress/element';
import { Button } from '@wordpress/components';
import { Button, withFilters } from '@wordpress/components';
import { getBlockType, createBlock } from '@wordpress/blocks';
import { withDispatch } from '@wordpress/data';
import { Warning } from '@wordpress/block-editor';
Expand All @@ -24,7 +25,7 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
originalName
);
actions.push(
<Button key="convert" onClick={ convertToHTML } isLarge isPrimary>
<Button key="convert" onClick={ convertToHTML } isPrimary>
{ __( 'Keep as HTML' ) }
</Button>
);
Expand All @@ -46,18 +47,21 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
);
}

const MissingEdit = withDispatch( ( dispatch, { clientId, attributes } ) => {
const { replaceBlock } = dispatch( 'core/block-editor' );
return {
convertToHTML() {
replaceBlock(
clientId,
createBlock( 'core/html', {
content: attributes.originalUndelimitedContent,
} )
);
},
};
} )( MissingBlockWarning );
const MissingEdit = compose(
withDispatch( ( dispatch, { clientId, attributes } ) => {
const { replaceBlock } = dispatch( 'core/block-editor' );
return {
convertToHTML() {
replaceBlock(
clientId,
createBlock( 'core/html', {
content: attributes.originalUndelimitedContent,
} )
);
},
};
} ),
withFilters( 'editor.missingEdit' )
)( MissingBlockWarning );

export default MissingEdit;