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 support for Child Blocks #6753

Merged
merged 10 commits into from
May 28, 2018
13 changes: 13 additions & 0 deletions docs/block-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,19 @@ A once-only block can be inserted into each post, one time only. For example, th
useOnce: true,
```

#### parent (optional)

* **Type:** `Array`

Blocks can be inserted into other blocks as nested content. Sometimes it is useful to restrict a block so that it is only available as a nested block. For example, you might want to allow an 'Add to Cart' block to only be available within a 'Product' block.
Copy link
Member

Choose a reason for hiding this comment

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

Blocks can be inserted into other blocks as nested content

By the current wording, there's an implication that I could insert a block into e.g. an image block, which is not true. This is to say: Not all blocks support nested content.

Copy link
Member Author

Choose a reason for hiding this comment

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

👌 changed in 920d7cc


Setting `parent` lets a block require that it is only available when nested within the specified blocks.

```js
// Only allow this block when it is nested in a Columns block
parent: [ 'core/columns' ],
```

#### supports (optional)

* **Type:** `Object`
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/deprecated.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Gutenberg's deprecation policy is intended to support backwards-compatibility fo
- `wp.data.withRehydratation` has been renamed to `wp.data.withRehydration`.
- The `wp.editor.ImagePlaceholder` component is removed. Please use `wp.editor.MediaPlaceholder` instead.
- `wp.utils.deprecated` function removed. Please use `wp.deprecated` instead.
- `getInserterItems`: the `allowedBlockTypes` argument was removed and the `parentUID` argument was added.
- `getFrecentInserterItems` selector removed. Please use `getInserterItems` instead.
- `getSupportedBlocks` selector removed. Please use `canInsertBlockType` instead.

## 3.1.0

Expand Down
7 changes: 3 additions & 4 deletions editor/components/inserter-with-shortcuts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@ function InserterWithShortcuts( { items, isLocked, onInsert } ) {

export default compose(
withSelect( ( select, { rootUID } ) => {
const { getEditorSettings, getFrecentInserterItems, getSupportedBlocks } = select( 'core/editor' );
const { templateLock, allowedBlockTypes } = getEditorSettings();
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
const { getEditorSettings, getInserterItems } = select( 'core/editor' );
const { templateLock } = getEditorSettings();
return {
items: getFrecentInserterItems( supportedBlocks, 4 ),
items: getInserterItems( rootUID ),
isLocked: !! templateLock,
};
} ),
Expand Down
20 changes: 5 additions & 15 deletions editor/components/inserter/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* External dependencies
*/
import { isEmpty } from 'lodash';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -41,15 +36,14 @@ class Inserter extends Component {

render() {
const {
items,
position,
title,
children,
onInsertBlock,
hasSupportedBlocks,
isLocked,
} = this.props;

if ( ! hasSupportedBlocks || isLocked ) {
if ( items.length === 0 ) {
return null;
}

Expand Down Expand Up @@ -80,7 +74,7 @@ class Inserter extends Component {
onClose();
};

return <InserterMenu onSelect={ onSelect } />;
return <InserterMenu items={ items } onSelect={ onSelect } />;
} }
/>
);
Expand All @@ -93,19 +87,15 @@ export default compose( [
getEditedPostAttribute,
getBlockInsertionPoint,
getSelectedBlock,
getSupportedBlocks,
getEditorSettings,
getInserterItems,
} = select( 'core/editor' );
const { allowedBlockTypes, templateLock } = getEditorSettings();
const insertionPoint = getBlockInsertionPoint();
const { rootUID } = insertionPoint;
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
return {
title: getEditedPostAttribute( 'title' ),
insertionPoint,
selectedBlock: getSelectedBlock(),
hasSupportedBlocks: true === supportedBlocks || ! isEmpty( supportedBlocks ),
isLocked: !! templateLock,
items: getInserterItems( rootUID ),
};
} ),
withDispatch( ( dispatch, ownProps ) => ( {
Expand Down
48 changes: 22 additions & 26 deletions editor/components/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
PanelBody,
} from '@wordpress/components';
import { getCategories, isSharedBlock } from '@wordpress/blocks';
import { withSelect, withDispatch } from '@wordpress/data';
import { withDispatch } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -33,6 +33,8 @@ import './style.scss';
import BlockPreview from '../block-preview';
import ItemList from './item-list';

const MAX_SUGGESTED_ITEMS = 9;

/**
* Filters an item list given a search term.
*
Expand All @@ -56,9 +58,10 @@ export class InserterMenu extends Component {
this.state = {
filterValue: '',
hoveredItem: null,
suggestedItems: [],
sharedItems: [],
itemsPerCategory: {},
openPanels: [ 'frecent' ],
openPanels: [ 'suggested' ],
};
this.onChangeSearchInput = this.onChangeSearchInput.bind( this );
this.onHover = this.onHover.bind( this );
Expand Down Expand Up @@ -107,7 +110,15 @@ export class InserterMenu extends Component {
filter( filterValue = '' ) {
const { items } = this.props;
const filteredItems = searchItems( items, filterValue );

let suggestedItems = [];
if ( ! filterValue ) {
const maxSuggestedItems = this.props.maxSuggestedItems || MAX_SUGGESTED_ITEMS;
suggestedItems = filter( items, ( item ) => item.utility > 0 ).slice( 0, maxSuggestedItems );
}

const sharedItems = filter( filteredItems, { category: 'shared' } );

const getCategoryIndex = ( item ) => {
return findIndex( getCategories(), ( category ) => category.slug === item.category );
};
Expand All @@ -116,10 +127,11 @@ export class InserterMenu extends Component {
( itemList ) => sortBy( itemList, getCategoryIndex ),
( itemList ) => groupBy( itemList, 'category' )
)( filteredItems );

let openPanels = this.state.openPanels;
if ( filterValue !== this.state.filterValue ) {
if ( ! filterValue ) {
openPanels = [ 'frecent' ];
openPanels = [ 'suggested' ];
} else if ( sharedItems.length ) {
openPanels = [ 'shared' ];
} else if ( filteredItems.length ) {
Expand All @@ -131,16 +143,16 @@ export class InserterMenu extends Component {
this.setState( {
hoveredItem: null,
filterValue,
suggestedItems,
sharedItems,
itemsPerCategory,
openPanels,
} );
}

render() {
const { instanceId, frecentItems, onSelect } = this.props;
const { hoveredItem, filterValue, sharedItems, itemsPerCategory, openPanels } = this.state;
const isSearching = !! filterValue;
const { instanceId, onSelect } = this.props;
const { hoveredItem, suggestedItems, sharedItems, itemsPerCategory, openPanels } = this.state;
const isPanelOpen = ( panel ) => openPanels.indexOf( panel ) !== -1;

// Disable reason: The inserter menu is a modal display, not one which
Expand All @@ -163,13 +175,13 @@ export class InserterMenu extends Component {
/>

<div className="editor-inserter__results">
{ ! isSearching &&
{ !! suggestedItems.length &&
<PanelBody
title={ __( 'Most Used' ) }
opened={ isPanelOpen( 'frecent' ) }
onToggle={ this.onTogglePanel( 'frecent' ) }
opened={ isPanelOpen( 'suggested' ) }
onToggle={ this.onTogglePanel( 'suggested' ) }
>
<ItemList items={ frecentItems } onSelect={ onSelect } onHover={ this.onHover } />
<ItemList items={ suggestedItems } onSelect={ onSelect } onHover={ this.onHover } />
</PanelBody>
}

Expand Down Expand Up @@ -211,22 +223,6 @@ export class InserterMenu extends Component {
}

export default compose(
withSelect( ( select ) => {
const {
getBlockInsertionPoint,
getInserterItems,
getFrecentInserterItems,
getSupportedBlocks,
getEditorSettings,
} = select( 'core/editor' );
const { allowedBlockTypes } = getEditorSettings();
const { rootUID } = getBlockInsertionPoint();
const supportedBlocks = getSupportedBlocks( rootUID, allowedBlockTypes );
return {
items: getInserterItems( supportedBlocks ),
frecentItems: getFrecentInserterItems( supportedBlocks ),
};
} ),
withDispatch( ( dispatch ) => ( {
fetchSharedBlocks: dispatch( 'core/editor' ).fetchSharedBlocks,
Copy link
Member

Choose a reason for hiding this comment

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

Minor: It is a little strange that we are dispatching a fetch action here but we are not selecting anything in this component. To make this relationship more clear maybe the items: getInserterItems( rootUid ) should continue to be done here and not passed as prop.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll leave this for now because we're looking into replacing this action with a resolver which would address this.

Copy link
Member

Choose a reason for hiding this comment

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

I'll leave this for now because we're looking into replacing this action with a resolver which would address this.

Where is this tracked?

Copy link
Member Author

Choose a reason for hiding this comment

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

} ) ),
Expand Down
Loading