Skip to content

Adds Block Appender as placeholder to empty InnerBlocks#14241

Merged
talldan merged 55 commits intoWordPress:masterfrom
getdave:add/inner-blocks-block-appender-as-placeholder
Apr 12, 2019
Merged

Adds Block Appender as placeholder to empty InnerBlocks#14241
talldan merged 55 commits intoWordPress:masterfrom
getdave:add/inner-blocks-block-appender-as-placeholder

Conversation

@getdave
Copy link
Copy Markdown
Contributor

@getdave getdave commented Mar 5, 2019

When inserting a Block that makes use of InnerBlocks(eg: Columns) there are two issues:

  1. It isn't always clear that the Block had been inserted (few visual cues)
  2. By default an empty paragraph Block is inserted - typically with high level Blocks this isn't what you want and it hides the controls on the parent Block and shows those of the paragraph Block.

This change aims to address this by replacing the default behaviour and instead showing the Block Appender as a placeholder:

screen shot 2019-03-05 at 16 16 24

This works by checking whether the parent Block has 1 or more innerBlocks. If there are none then it displays the Block Appender as a placeholder thereby clearly indicating the Block has been inserted and providing an opportunity for the user to manually select the appropriate child Block to insert.

Once a child Block is inserted then the placeholder is removed.

This is closely tied to the current work on the Container Block which requires exactly the functionality described above to be perceivable to users. Please see #13964 (comment)

Todos / Outstanding Tasks

  • Adjust appender to use 100 version of the gray.
  • Fix to ensure appender is not larger than column content. Attempt to match up with this.
  • Fix e2e tests that broke as a result of the UI / UX change
  • Write new e2e tests to cover the new UI/UX
  • Write e2e tests to verify
    • it works with a custom appender
    • it works with the exposed components
  • Address final feedback points

How has this been tested?

For testing purposes apply the following patch to enable the ButtonBlockAppender on core/seciton and core/column

diff --git a/packages/block-library/src/columns/column.js b/packages/block-library/src/columns/column.js
index a13779e54..3224f2df0 100644
--- a/packages/block-library/src/columns/column.js
+++ b/packages/block-library/src/columns/column.js
@@ -33,6 +33,9 @@ const ColumnEdit = ( { attributes, updateAlignment } ) => {
 			</BlockControls>
 			<InnerBlocks
 				templateLock={ false }
+				renderAppender={ () => (
+					<InnerBlocks.ButtonBlockAppender />
+				) }
 			/>
 		</div>
 	);
diff --git a/packages/block-library/src/section/edit.js b/packages/block-library/src/section/edit.js
index fcf17c40c..bbbd5adfc 100644
--- a/packages/block-library/src/section/edit.js
+++ b/packages/block-library/src/section/edit.js
@@ -39,7 +39,11 @@ function SectionEdit( { className, setBackgroundColor, backgroundColor } ) {
 				/>
 			</InspectorControls>
 			<div className={ classes } style={ styles }>
-				<InnerBlocks />
+				<InnerBlocks
+					renderAppender={ () => (
+						<InnerBlocks.ButtonBlockAppender />
+					) }
+				/>
 			</div>
 		</Fragment>
 	);

Screenshots

Types of changes

  • New feature (non-breaking change which adds functionality
  • (Possible) Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.

@getdave getdave added [Feature] Inserter The main way to insert blocks using the + button in the editing interface Needs Design Feedback Needs general design feedback. [Feature] Nested / Inner Blocks Anything related to the experience of nested/inner blocks inside a larger container, like Group or P [Type] Feedback Issues that relate purely to feedback on a feature that isn't necessarily actionable Needs Technical Feedback Needs testing from a developer perspective. Needs Accessibility Feedback Need input from accessibility labels Mar 5, 2019
@getdave getdave self-assigned this Mar 5, 2019
@talldan
Copy link
Copy Markdown
Contributor

talldan commented Mar 6, 2019

I think only some blocks would want to implement this (container being the main candidate).

The question is ... how should it be declared? Perhaps as a block setting? Would it live under supports? e.g.

supports: { appender: 'button' }

or

supports: { buttonAppender: true }

@youknowriad
Copy link
Copy Markdown
Contributor

The InnerBlocks component has a bunch of props to configure it right now, so I think it's fine to keep using a prop for consistency.

@richtabor
Copy link
Copy Markdown
Member

I've played around with an improved column appender as well for the CoBlocks Row/Column blocks. A bit different UI to look at. 💪

screenflow

@getdave
Copy link
Copy Markdown
Contributor Author

getdave commented Mar 6, 2019

@youknowriad @talldan To confirm we're saying

  1. This should be opt in via the parent Block which uses InnerBlocks
  2. Opt-in should be via a prop passed to InnerBlocks (as opposed to a new API)

Please see my commits 0e1dd78 and 58c60f0 which address this by making it opt in via a prop on InnerBlocks (documented).

Demo - note that Columns is opting in, whereas Media + Text has not opted in. Both Blocks use InnerBlocks:

screen capture on 2019-03-06 at 12-09-19

Question: is the "prop drilling" ok here? Is there a better way?

@richtabor This does indeed look very similar. Are there any enhancements/learnings you'd like to share to benefit this PR? Much appreciated. @melchoyce did the design on this one I believe.

@richtabor
Copy link
Copy Markdown
Member

@richtabor This does indeed look very similar. Are there any enhancements/learnings you'd like to share to benefit this PR? Much appreciated.

The most important part here is ensuring that folks understand that they can click the appender to add a block.

Also, I've considered keeping the appender at the bottom of the column block, so that users can still easily add blocks after an initial implementation. I was finding that folks were unsure how to add blocks again, once the appender placeholder had been removed (as blocks were added).

@getdave
Copy link
Copy Markdown
Contributor Author

getdave commented Mar 6, 2019

@richtabor Thanks for your input.

The most important part here is ensuring that folks understand that they can click the appender to add a block.

I'm hopefull that the wording "Add a Block" should suffice (not intending to be sarcastic here - just an observation). I like the background colour on your version as this enhances the perceiability yet further, but I'll defer to @melchoyce on this one.

I've considered keeping the appender at the bottom of the column block

It's a nice idea and one I think could/should be explored in a follow on PR if that's ok with you?

@jasmussen
Copy link
Copy Markdown
Contributor

Could "Add Block" be sufficient, as oppposed to "Add a block"? Short and sweet, and it's always good to consider translations that nearly always balloon best intentions.

@melchoyce
Copy link
Copy Markdown
Contributor

We use rgba(139,139,150,.1) for placeholders; let's try that out here in a couple contexts to see how it works.

@getdave getdave mentioned this pull request Mar 6, 2019
15 tasks
@melchoyce
Copy link
Copy Markdown
Contributor

Oh, while I'm thinking about it — is there any way to figure out if the appender is on a dark background, and if so, flip the colors over to white for contrast?

@jasmussen
Copy link
Copy Markdown
Contributor

I think we have an SCSS variable for that opacity color, and be sure to test it in dark editor styles!

@talldan
Copy link
Copy Markdown
Contributor

talldan commented Apr 12, 2019

I've added a few extra commits to address the feedback.

Mostly, I haven't made any major changes, but one thing I'd like to do is move the HideWhenChildBlocks component to a separate PR, since it's one of the few remaining points of discussion, and I don't believe it's needed at this point.

I've removed it from this PR and have a separate branch that still has it.

Copy link
Copy Markdown
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

Let's ship and use it in the section block.

@talldan
Copy link
Copy Markdown
Contributor

talldan commented Apr 12, 2019

Great! Other PRs incoming:

@getdave
Copy link
Copy Markdown
Contributor Author

getdave commented Apr 12, 2019

@talldan Whoa I barely recognised the PR! Thanks for all your work to get this merged 👍

Could you link to the PRs referenced above?

@SchneiderSam
Copy link
Copy Markdown

Can't found this function in 5.5. confused...

@talldan
Copy link
Copy Markdown
Contributor

talldan commented May 1, 2019

Hey @SchneiderSam - it's being rolled out to blocks on a case by case basis. At the moment it's only been added to the group block.

It was implemented for the group block on a separate PR, but unfortunately missed 5.5 and 5.6. It should be in 5.7, which is due to be released on the 15th May:
#14943

@websevendev
Copy link
Copy Markdown

I used the renderAppender prop on <InnerBlocks> to render a button that says Add list item for a custom list block. Previously had to click on the appender button and then select the block from a selection of 1 block, so this prop is great.

How ever the button doesn't work when clicking it while the parent block is inactive, it just activates the block. End up needing to click twice most of the time.

Looking at why the normal appender works with one click, but my custom one doesn't, the difference seems to be <IgnoreNestedEvents>:

https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/block-list-appender/index.js#L30-L52

I don't think it's exposed, so I copied the <IgnoreNestedEvents> to my library and wrapped my appender button in it and then it works.

Perhaps expose the <IgnoreNestedEvents> for general use or add a prop to use it?

My code:

(hook)

import IgnoreNestedEvents from '../IgnoreNestedEvents';

export default function useAppender(element, blockName, clientId) {
	const append = useCallback(() => {
		const block = wp.blocks.createBlock(blockName);
		const index = wp.data.select('core/editor').getBlocksByClientId(clientId)[0].innerBlocks.length;
		wp.data.dispatch('core/editor').insertBlock(block, index, clientId);
	}, [blockName, clientId]);

	return () => {
		return (
			<IgnoreNestedEvents childHandledEvents={['onFocus', 'onClick', 'onKeyDown']}>
				<element.type {...element.props} onClick={append} />
			</IgnoreNestedEvents>
		);
	};
};

(used in block edit)

const Edit = ({clientId}) => {
	...
	const appender = useAppender(
		<button>Add list item</button>,
		'custom/listitem',
		clientId
	);
	...
	return (
		...
		<wp.blockEditor.InnerBlocks
			allowedBlocks={['custom/listitem']}
			templateInsertUpdatesSelection={false}
			renderAppender={appender}
		/>
		...
	);
};

@talldan
Copy link
Copy Markdown
Contributor

talldan commented Jun 14, 2019

Hey @websevendev,

I'm not entirely sure why IgnoreNestedEvents is not exposed. It might be that there are some considerations around its use, so the idea is not to promote it being used externally.

I think there might be two approaches to solve your problem:

  1. Expose IgnoreNestedEvents as you suggest. It would need some documentation.
  2. Add some improvements to InnerBlocks.ButtonBlockAppender so that you can pass in your own button text. You'd then be able to use that component instead of having to build your own button appender.

Is this an improvement that you might be willing to contribute? A good first step would be to make an issue, but a PR could also be an option. Let me know what you think.

@websevendev
Copy link
Copy Markdown

The benefits of <IgnoreNestedEvents> seem to be diminished now by #15537 since you can't directly click the button most of the time anyway, so it remains to be seen whether there's any reason to do anything.

Add some improvements to InnerBlocks.ButtonBlockAppender so that you can pass in your own button text. You'd then be able to use that component instead of having to build your own button appender.

My previous code example was a bit stripped down, it's not just a <button>, it's got an icon, matches the theme and can handle itself on a dark background, so for me just being able to control the text wouldn't be enough which is why renderAppender prop is great and InnerBlocks.ButtonBlockAppender should probably remain as simple as possible given that there's a powerful alternative.
image
There's possibilities to add multiple buttons, maybe duplicate last list item button, etc. So just exposing the IgnoreNestedEvents is probably the best or making it available with a prop

<wp.blockEditor.InnerBlocks
	renderAppender={appender}
	appenderIgnoreNestedEvents={true}
/>

or perhaps enabled by default and a prop to disable it? I wonder what the use cases would be for not using it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Inserter The main way to insert blocks using the + button in the editing interface [Feature] Nested / Inner Blocks Anything related to the experience of nested/inner blocks inside a larger container, like Group or P Needs Accessibility Feedback Need input from accessibility Needs Technical Feedback Needs testing from a developer perspective. [Status] In Progress Tracking issues with work in progress [Type] Feedback Issues that relate purely to feedback on a feature that isn't necessarily actionable

Projects

None yet

Development

Successfully merging this pull request may close these issues.