Skip to content

Commit d62d455

Browse files
Block Directory: Add an "Install" button when a block type is not found (#22631)
* Update package-lock.json * Filter the Missing block edit component * Remove debug code * Updated warning styles to be more consistent with other patterns like the block placeholder state * Removed font size declaration - was inconsistent with other patterns particularly because of the use of `em` units for padding * changed padding on the warning and changed margin on the warning message * changed the side the padding was on for warning buttons * Run `parse` to get the correct attributes and innerBlocks Co-authored-by: Michael Arestad <marestad@gmail.com>
1 parent 2da8da9 commit d62d455

File tree

7 files changed

+147
-19
lines changed

7 files changed

+147
-19
lines changed

package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/block-directory/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@wordpress/data-controls": "file:../data-controls",
3636
"@wordpress/edit-post": "file:../edit-post",
3737
"@wordpress/element": "file:../element",
38+
"@wordpress/hooks": "file:../hooks",
3839
"@wordpress/html-entities": "file:../html-entities",
3940
"@wordpress/i18n": "file:../i18n",
4041
"@wordpress/icons": "file:../icons",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { __, sprintf } from '@wordpress/i18n';
5+
import { Button } from '@wordpress/components';
6+
import { RawHTML } from '@wordpress/element';
7+
import { useSelect } from '@wordpress/data';
8+
import { Warning } from '@wordpress/block-editor';
9+
10+
/**
11+
* Internal dependencies
12+
*/
13+
import InstallButton from './install-button';
14+
15+
const filterMissing = ( OriginalComponent ) => ( props ) => {
16+
const { originalName, originalUndelimitedContent } = props.attributes;
17+
const { block, hasPermission } = useSelect(
18+
( select ) => {
19+
const { getDownloadableBlocks } = select( 'core/block-directory' );
20+
const blocks = getDownloadableBlocks(
21+
'block:' + originalName
22+
).filter( ( { name } ) => originalName === name );
23+
return {
24+
hasPermission: select( 'core' ).canUser(
25+
'read',
26+
'block-directory/search'
27+
),
28+
block: blocks.length && blocks[ 0 ],
29+
};
30+
},
31+
[ originalName ]
32+
);
33+
34+
if ( ! hasPermission || ! block ) {
35+
return <OriginalComponent { ...props } />;
36+
}
37+
38+
const actions = [
39+
<InstallButton
40+
key="install"
41+
block={ block }
42+
attributes={ props.attributes }
43+
clientId={ props.clientId }
44+
/>,
45+
<Button key="convert" onClick={ props.convertToHTML } isLink>
46+
{ __( 'Keep as HTML' ) }
47+
</Button>,
48+
];
49+
50+
return (
51+
<>
52+
<Warning actions={ actions }>
53+
{ sprintf(
54+
/* translators: %s: block name */
55+
__(
56+
'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.'
57+
),
58+
block.title || originalName
59+
) }
60+
</Warning>
61+
<RawHTML>{ originalUndelimitedContent }</RawHTML>
62+
</>
63+
);
64+
};
65+
66+
export default filterMissing;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { __, sprintf } from '@wordpress/i18n';
5+
import { Button } from '@wordpress/components';
6+
import { createBlock, getBlockType, parse } from '@wordpress/blocks';
7+
import { useSelect, useDispatch } from '@wordpress/data';
8+
9+
export default function InstallButton( { attributes, block, clientId } ) {
10+
const isInstallingBlock = useSelect( ( select ) =>
11+
select( 'core/block-directory' ).isInstalling( block.id )
12+
);
13+
const { installBlockType } = useDispatch( 'core/block-directory' );
14+
const { replaceBlock } = useDispatch( 'core/block-editor' );
15+
16+
return (
17+
<Button
18+
onClick={ () =>
19+
installBlockType( block ).then( ( success ) => {
20+
if ( success ) {
21+
const blockType = getBlockType( block.name );
22+
const [ originalBlock ] = parse(
23+
attributes.originalContent
24+
);
25+
if ( originalBlock ) {
26+
replaceBlock(
27+
clientId,
28+
createBlock(
29+
blockType.name,
30+
originalBlock.attributes,
31+
originalBlock.innerBlocks
32+
)
33+
);
34+
}
35+
}
36+
} )
37+
}
38+
disabled={ isInstallingBlock }
39+
isBusy={ isInstallingBlock }
40+
isPrimary
41+
>
42+
{ sprintf(
43+
/* translators: %s: block name */
44+
__( 'Install %s' ),
45+
block.title
46+
) }
47+
</Button>
48+
);
49+
}

packages/block-directory/src/plugins/index.js

+8
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
* WordPress dependencies
33
*/
44
import { registerPlugin } from '@wordpress/plugins';
5+
import { addFilter } from '@wordpress/hooks';
56

67
/**
78
* Internal dependencies
89
*/
910
import AutoBlockUninstaller from '../components/auto-block-uninstaller';
1011
import InserterMenuDownloadableBlocksPanel from './inserter-menu-downloadable-blocks-panel';
1112
import InstalledBlocksPrePublishPanel from './installed-blocks-pre-publish-panel';
13+
import filterMissing from './filter-missing';
1214

1315
registerPlugin( 'block-directory', {
1416
render() {
@@ -21,3 +23,9 @@ registerPlugin( 'block-directory', {
2123
);
2224
},
2325
} );
26+
27+
addFilter(
28+
'editor.missingEdit',
29+
'block-directory/install-missing',
30+
filterMissing
31+
);

packages/block-editor/src/components/warning/style.scss

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
display: flex;
44
flex-wrap: wrap;
55
font-family: $default-font;
6-
font-size: $default-font-size;
7-
padding: ($grid-unit-10 - $border-width - $border-width) $grid-unit-15;
6+
padding: 1em;
87

98
// Block UI appearance.
109
border: $border-width solid $gray-900;
@@ -15,7 +14,7 @@
1514
line-height: $default-line-height;
1615
font-family: $default-font;
1716
font-size: $default-font-size;
18-
margin: 1em 0;
17+
margin: 0 0 1em;
1918

2019
}
2120

@@ -38,7 +37,7 @@
3837
}
3938

4039
.block-editor-warning__action {
41-
margin: 0 0 0 $grid-unit-10;
40+
margin: 0 $grid-unit-10 0 0;
4241
}
4342
}
4443

packages/block-library/src/missing/edit.js

+19-15
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* WordPress dependencies
33
*/
44
import { __, sprintf } from '@wordpress/i18n';
5+
import { compose } from '@wordpress/compose';
56
import { RawHTML } from '@wordpress/element';
6-
import { Button } from '@wordpress/components';
7+
import { Button, withFilters } from '@wordpress/components';
78
import { getBlockType, createBlock } from '@wordpress/blocks';
89
import { withDispatch } from '@wordpress/data';
910
import { Warning } from '@wordpress/block-editor';
@@ -24,7 +25,7 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
2425
originalName
2526
);
2627
actions.push(
27-
<Button key="convert" onClick={ convertToHTML } isLarge isPrimary>
28+
<Button key="convert" onClick={ convertToHTML } isPrimary>
2829
{ __( 'Keep as HTML' ) }
2930
</Button>
3031
);
@@ -46,18 +47,21 @@ function MissingBlockWarning( { attributes, convertToHTML } ) {
4647
);
4748
}
4849

49-
const MissingEdit = withDispatch( ( dispatch, { clientId, attributes } ) => {
50-
const { replaceBlock } = dispatch( 'core/block-editor' );
51-
return {
52-
convertToHTML() {
53-
replaceBlock(
54-
clientId,
55-
createBlock( 'core/html', {
56-
content: attributes.originalUndelimitedContent,
57-
} )
58-
);
59-
},
60-
};
61-
} )( MissingBlockWarning );
50+
const MissingEdit = compose(
51+
withDispatch( ( dispatch, { clientId, attributes } ) => {
52+
const { replaceBlock } = dispatch( 'core/block-editor' );
53+
return {
54+
convertToHTML() {
55+
replaceBlock(
56+
clientId,
57+
createBlock( 'core/html', {
58+
content: attributes.originalUndelimitedContent,
59+
} )
60+
);
61+
},
62+
};
63+
} ),
64+
withFilters( 'editor.missingEdit' )
65+
)( MissingBlockWarning );
6266

6367
export default MissingEdit;

0 commit comments

Comments
 (0)