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

Implement nesting in cover image block #5452

Closed
wants to merge 2 commits into from

Conversation

jorgefilipecosta
Copy link
Member

@jorgefilipecosta jorgefilipecosta commented Mar 6, 2018

Description

This PR implements the concepts described in #5448.
E.g:

<InnerBlocks
	template={ [
		[ 'core/paragraph', {
			align: 'center',
			fontSize: 37,
			placeholder: 'Write title…',
			textColor: '#fff',
		} ],
	] }
	allowedBlockNames={ [ 'core/button', 'core/heading', 'core/paragraph', 'core/subhead' ] }
/>

As future work template_lock will be added. This opens the path for a whole new type of blocks.
Blocks that make use of nesting and just apply a special arrangement/design to existing blocks.
E.g: we can have a package of landing page blocks, where all of the blocks are based on paragraphs, headings, buttons etc... arranging them in a special way and applying some styles. Themes may take advantage of this to create theme-specific blocks.

The fact that the InnerBlocks settings are just props has some advantages e.g: depending on the settings in the block inspector we may allow different blocks. But it is much more complex to implement when compared to Add a property to the block API, allowedRootBlockNames: [ ... ] .

Right now this implementation is not very clean ( being nice to myself ) we are using a property in the state the save the current InnerBlock settings of each block uid with custom settings. This was required because we need to globally know the allowed blocks, so the inserter can filter them. Maybe there is a better way to solve this problem. I'm thinking about an impossible way to improve this and I'm totally open to suggestions.

To define templates we are using the same logic as the one used in CPT's but this makes the data structure strange in javascript (until know templates were only defined in PHP so this was unnoticeable).

Cover image block was updated to make use of this concepts.

Know problems / related work

Using the autocomplete, we can insert allowed blocks. Autocomplete logic is different from other inserters and we already have some problems e.g: if the template lock the blocks, autocomplete inserter is not aware of the lock and then we get a big js error because onReplace does not exist in this case. In parallel, I will improve the logic of autocomplete inserter so changes required here are easier.

Testing

Test the cover image block try to add blocks remove see things work as expected. The design will benefit from #5658.
Test that templates also work with layouts by adding this template to columns block.

                    template={ [
                        [ 'core/paragraph', {
                            placeholder: 'Enter column 1 content…',
                            layout: 'column-1',
                        } ],
                        [ 'core/paragraph', {
                            placeholder: 'Enter column 2 content…',
                            layout: 'column-2',
                        } ],
                    ] }

Test that we are backcompatible with existing cover images by pasting the following code in the code editor and verifying it works as expected:

<!-- wp:cover-image {"url":"http://via.placeholder.com/350x150","id":10559} -->
<div class="wp-block-cover-image has-background-dim" style="background-image:url(http://via.placeholder.com/350x150)">
	<p class="wp-block-cover-image-text">Test</p>
</div>
<!-- /wp:cover-image -->

Screenshots (jpeg or gifs if applicable):

image

@aduth
Copy link
Member

aduth commented Mar 7, 2018

Related: #4097 (as far as prior art in restricting block types shown in the inserter)

@mtias mtias mentioned this pull request Mar 12, 2018
@phpbits
Copy link
Contributor

phpbits commented Mar 15, 2018

@jorgefilipecosta looking forward to this :) Thanks!

@jorgefilipecosta jorgefilipecosta force-pushed the add/nesting-cover-image branch 3 times, most recently from 386c376 to e3ee131 Compare March 22, 2018 21:47
@jorgefilipecosta jorgefilipecosta removed the [Status] In Progress Tracking issues with work in progress label Mar 22, 2018
@jorgefilipecosta jorgefilipecosta added [Type] Enhancement A suggestion for improvement. [Feature] Blocks Overall functionality of blocks labels Mar 22, 2018
@jorgefilipecosta jorgefilipecosta requested a review from a team March 22, 2018 22:20
Copy link
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.

Nice work here 👍

.editor-block-list__layout {
width: 100%;
.editor-block-list__block:not(.is-multi-selected) .wp-block-paragraph {
background: none;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we use a background on the paragraph block by default? I'm trying to understand why this is needed mainly to avoid the specificity to target the paragraph block's className in another block.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @youknowriad,
The reason why we set a white background is related to the contrast verification. If the background was never set getComputedStyles returns back background (if I remember correctly). So adding a background fixes the problem. If themes change the background it also works well because as long as a background is set we are able to retrieve it. The problem happens when no background is set. As we get back background color when background is not set we cannot differentiate and assume is white background because in fact it may be set to black. I will try to research this in more detail maybe there is a way to detect when the background is not set and in that case assume white.
Replicate from comment #5273 (comment).

A refactoring will happen to contrast checker in order to not rely on this rule. So it will be removed for now.

textColor: '#fff',
} ],
] }
allowedBlockNames={ [ 'core/button', 'core/heading', 'core/paragraph', 'core/subhead' ] }
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️ awesome API

Copy link
Member

Choose a reason for hiding this comment

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

Should we move it to the settings object in registerBlockType to make it extensible? :)

Copy link
Member

Choose a reason for hiding this comment

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

I meant both template and allowedBlockNames.

Copy link
Contributor

Choose a reason for hiding this comment

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

As noted in the description, this allows it to be customizable using toolbar/inspector controls.

But this makes me think of something. We should consider the template prop here as a defaultTemplate because we already have support for nested template. by setting a CPT template with a nested template for the cover image for instance. I think we should do something here where the template prop is used (override it by the global template if available or something)

Copy link
Member

Choose a reason for hiding this comment

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

I’m missing something here, how can I override those values for Cover Image block? 😄
I couldn't digest this from this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Okey, I know what you were referring to. You should be able to do it when providing the template for CPT as explained in here: https://wordpress.org/gutenberg/handbook/templates/#nested-templates.

Copy link
Member

Choose a reason for hiding this comment

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

Can we switch this API to be allowedBlocks?

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Mar 28, 2018

Choose a reason for hiding this comment

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

Hi @youknowriad,

But this makes me think of something. We should consider the template prop here as a defaultTemplate because we already have support for nested template. by setting a CPT template with a nested template for the cover image for instance. I think we should do something here where the template prop is used (override it by the global template if available or something).

When we have a CPT template and we set content for a nested block, ( e.g: we set a cover image with a header and a paragraph), during the post-load the cover image is created and the blocks are added inside it. So when our inner logic executes the cover image block already has content, so the template will not be used. It will be the same as when we load a post with a cover image that already has blocks inside it. The template only applies to empty blocks.
So both approaches can work together without problems if a global template exists it already overwrites this setting :)

Hi @mtias the prop was renamed to allowedBlocks.

@@ -976,13 +976,41 @@ export const reusableBlocks = combineReducers( {
},
} );

export const blockListSettings = ( state = {}, action ) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a docBlock to explain what's this reducer for?

insertTemplateBlocksIfInnerBlocksEmpty( template ) {
const { block, onInsertBlocks, uid } = this.props;
if ( template && ! block.innerBlocks.length ) {
onInsertBlocks( synchronizeBlocksWithTemplate( [], template ), 0, uid );
Copy link
Contributor

Choose a reason for hiding this comment

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

I think synchronizing the template is not needed here because we only synchronize templates if the template is locked but we don't support locking yet for inner Blocks.

Unless I'm missing something?

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Mar 27, 2018

Choose a reason for hiding this comment

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

Yes synchronizing the template is not needed. But I want to given a template parse and create the block structure it represents turns out that's exactly what synchronizeBlocksWithTemplate( [], template ) does so I did not want to duplicate code and used it :)

}

componentWillReceiveProps( nextProps ) {
updateNestedSettingsIfNeeded( {
Copy link
Contributor

Choose a reason for hiding this comment

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

As far As I understand we have the uid at this point, so we can call the action directly in this component to avoid the need to add a new callback to this HoC?

updateNestedSettingsIfNeeded( {
supportedBlocks: this.props.allowedBlockNames,
} );
insertTemplateBlockIfNeeded( this.props.template );
Copy link
Contributor

Choose a reason for hiding this comment

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

Same for this callback: can w call the action directly in this component to avoid the need to add a new callback to this HoC?

( state, { globalEnabledBlockTypes } ) => {
const insertionPoint = getBlockInsertionPoint( state );
const { rootUID } = insertionPoint;
const blockListSettings = getBlockListSettings( state, rootUID );
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could move this logic to a selector to test it properly:

getSupportBlocks( state, uid, enabledBlockTypes )

Copy link
Member

Choose a reason for hiding this comment

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

Second that 👍

@@ -79,19 +80,25 @@ class Inserter extends Component {
onClose();
};

return <InserterMenu onSelect={ onSelect } />;
return <InserterMenu onSelect={ onSelect } supportedBlockTypes={ supportedBlockTypes } />;
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this prop is not used in the current component, can we move to the menu component instead?

Also might be good to factorize, using the selector proposed above?

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Mar 27, 2018

Choose a reason for hiding this comment

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

Hi @youknowriad, the prop supportedBlockTypes is used to compute hasSupportedBlocks flag that's why it was computed in this component.
Regarding the factorize to selector, I will try to make this more generic maybe it will involve selector plus HoC that accesses the context.

@gziolo
Copy link
Member

gziolo commented Mar 23, 2018

To define templates we are using the same logic as the one used in CPT's but this makes the data structure strange in javascript (until know templates were only defined in PHP so this was unnoticeable).

It doesn't look that bad, it is similar to what is used in config files for libraries like Babel. See: https://github.com/WordPress/packages/blob/master/packages/babel-preset-default/index.js#L6-L11

url: {
type: 'string',
},
align: {
Copy link
Member

Choose a reason for hiding this comment

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

I think align should stay in here. Otherwise, it won't be possible to change the alignment of the container.

Copy link
Member Author

@jorgefilipecosta jorgefilipecosta Mar 27, 2018

Choose a reason for hiding this comment

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

Yes, the idea I have in mind is that it is no possible to change the alignment of the container. The container takes 100% width. So for example, if we want to right align text we apply the alignment on the nested paragraph block.

Edited: sorry I understood what you mean, you are totally right it was a mistake. This attribute should not be removed.

const controls = isSelected && [
<BlockControls key="controls">
<BlockAlignmentToolbar
Copy link
Member

Choose a reason for hiding this comment

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

The same applies in here, unless it is moved to the InnerBlock. In that case validAlignments and getEditWrapperProps might need to be removed.

textColor: '#fff',
} ],
] }
allowedBlockNames={ [ 'core/button', 'core/heading', 'core/paragraph', 'core/subhead' ] }
Copy link
Member

Choose a reason for hiding this comment

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

I’m missing something here, how can I override those values for Cover Image block? 😄
I couldn't digest this from this PR.

@jorgefilipecosta jorgefilipecosta self-assigned this Mar 23, 2018
@jorgefilipecosta jorgefilipecosta force-pushed the add/nesting-cover-image branch 3 times, most recently from 001ea8f to 0fc0bd5 Compare March 27, 2018 23:02
Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

I left a few comments to check.

[ 'core/paragraph', {
align: 'center',
fontSize: 37,
placeholder: 'Write title…',
Copy link
Member

Choose a reason for hiding this comment

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

Translation wrapper missing for placeholder.

template={ [
[ 'core/paragraph', {
align: 'center',
fontSize: 37,
Copy link
Member

Choose a reason for hiding this comment

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

Font size has now this t-shirt like sizes, is it one of them?

componentWillMount() {
INNER_BLOCK_LIST_CACHE[ uid ][ 1 ]++;
}
connect( ( state ) => {
Copy link
Member

Choose a reason for hiding this comment

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

It might be a good practice to stop using connect in favor of withSelect snd withDispatch HOCs.

( state, { globalEnabledBlockTypes } ) => {
const insertionPoint = getBlockInsertionPoint( state );
const { rootUID } = insertionPoint;
const blockListSettings = getBlockListSettings( state, rootUID );
Copy link
Member

Choose a reason for hiding this comment

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

Second that 👍

<!-- wp:core/cover-image {"url":"https://cldup.com/uuUqE_dXzy.jpg","dimRatio":40} -->
<div class="wp-block-cover-image has-background-dim-40 has-background-dim" style="background-image:url(https://cldup.com/uuUqE_dXzy.jpg)">
<p class="wp-block-cover-image-text">Guten Berg!</p>
<!-- wp:cover-image {"url":"https://cldup.com/uuUqE_dXzy.jpg","id":8398} -->
Copy link
Member

@gziolo gziolo Mar 28, 2018

Choose a reason for hiding this comment

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

has-undefined-content is here because contenAlign attribute was removed, but not its corresponding class name.

"hasParallax": false,
"dimRatio": 40
"dimRatio": 50
Copy link
Member

Choose a reason for hiding this comment

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

It might be nice to keep non-default dim to see the class name included in HTML.

<div class="wp-block-cover-image has-background-dim has-undefined-content" style="background-image:url(https://cldup.com/uuUqE_dXzy.jpg)">
<div class="wp-block-cover-image__inner-container">
<!-- wp:paragraph {"align":"center","placeholder":"Write title…","textColor":"#fff","fontSize":37} -->
<p style="color:#fff;text-align:center">Paragraph 1</p>
Copy link
Member

Choose a reason for hiding this comment

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

Is it expected that fot size is not printed as style?

@jorgefilipecosta
Copy link
Member Author

Hi @gziolo and @youknowriad thank you a lot for the feedback it was applied.
These changes need additional test cases which I will add in parallel. But the logic is ready for a new look.
Regarding extensibility, and allowing plugins to change the inner block settings, unfortunately, this PR does not address it (in order to not increase the size), but my plan is to creates a new filter that allows plugins to change all the props passed to InnerBlocks (allowedBlocks, template, etc...), the filter receives an object with the settings and can return a new one that changes them. There are other ways like a more granular allowedBlocks filter. after we land the core changes (or agree that they are the path to go) I will add a PR that allows plugins to change these settings :)

@jasmussen
Copy link
Contributor

Wanted to note that I'm doing some work in #6406 that might benefit some of the color/UI issues.

@jorgefilipecosta
Copy link
Member Author

Hi @mcsf, the rebase was done. I think this should ready. I'm solving some accessibility issues using other PR's (as they are not exclusive in this PR).
Regarding the design, many improvements are already present and @jasmussen is making things even better in a side PR. I was just waiting for a green light from the UX&UI perspective.

@jorgefilipecosta
Copy link
Member Author

jorgefilipecosta commented Apr 26, 2018

Hi @jasmussen, thank you for the improvements you are making they will make nesting in colored places like cover image better.
I personally think it would be ok to merge this even before your PR as I think the UI is already in an acceptable state for most backgrounds and merging would allow testing the UI changes in real use cases even easily, and could potentially allow some further works that take advantage of the mechanisms implemented here.

Copy link
Member

@karmatosed karmatosed left a comment

Choose a reason for hiding this comment

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

I think whilst it's not perfect visually getting this in and refining with the work @jasmussen is doing there, is the best route. As a result approving this and let's note though in merge that this is to be refined, just to set expectations.

@jasmussen
Copy link
Contributor

Since this is a pretty complex PR, should we wait for Matías technical thoughts? He's AFK but hopefully back next week. It feels like his comments starting in #5452 (comment) were sort of skeptical. Not saying because this isn't impressive work, just wondering.

lsl added a commit to lsl/jetpack that referenced this pull request May 1, 2018
Adds a set of Gutenberg "Form" blocks for use with the contact form
module.

Blocks:

* Form - The main block, consists of an InnerBlock for the <form>
element and a set template consisting of a default set of inner blocks
preconfigured as a contact form

* Text - General <input type=text>/<textarea> field with configurable
  label, rows

* Button - A <button type=submit> with configurable button text

Blocking Release:

* InnerBlock templating requires WordPress/gutenberg#5452.

* WordPress/gutenberg#5452 needs to override isPrivate settings to
  prevent form blocks being seen in the inserter when out of form
  context.

* WordPress/gutenberg#5452 is whitelist only meaning we cant add
  extra blocks in between form elements - not really a huge issue
  for this but likely an issue for other use cases.

Todo - Integration with grunion-contact-form:

* Captcha support should be relatively easy to achieve with a serverside
  rendered block

* A server side filter / hook could replace the form action

Todo - Handle duplicate field names gracefully

* Currently uses a variation on the label / field name to set
  id / for settings on label / inputs.

* Currently uses same variation for <input name=""> attributes
@jorgefilipecosta jorgefilipecosta changed the title Implemented nesting in cover image block. Implement nesting in cover image block May 2, 2018
@gziolo
Copy link
Member

gziolo commented May 10, 2018

@jorgefilipecosta - can you rebase with the recent changes from master?

@mtias - it waits for your feedback.

@ZebulanStanphill
Copy link
Member

ZebulanStanphill commented Jun 22, 2018

Should this be added to the Merge Proposal: Editor milestone? It seems like something that would be good for optimum recreation of existing content into Gutenberg block form.

lsl added a commit to Automattic/jetpack that referenced this pull request Jul 12, 2018
Context: #9452

@oskosk Requested this unfinished branch to be pushed up to
Automattic/jetpack for use in the Jetpck Beta Tester Plugin.

--

Adds a set of Gutenberg "Form" blocks for use with the contact form
module.

Blocks:

* Form - The main block, consists of an InnerBlock for the <form>
element and a set template consisting of a default set of inner blocks
preconfigured as a contact form

* Text - General <input type=text>/<textarea> field with configurable
  label, rows

* Button - A <button type=submit> with configurable button text

Blocking Release:

* Fix issues identified in original PR #9452

* InnerBlock templating requires WordPress/gutenberg#5452.

* WordPress/gutenberg#5452 needs to override isPrivate settings to
  prevent form blocks being seen in the inserter when out of form
  context.

* WordPress/gutenberg#5452 is whitelist only meaning we cant add
  extra blocks in between form elements - not really a huge issue
  for this but likely an issue for other use cases.

Todo - Integration with grunion-contact-form:

* Captcha support should be relatively easy to achieve with a serverside
  rendered block

* A server side filter / hook could replace the form action

Todo - Handle duplicate field names gracefully

* Currently uses a variation on the label / field name to set
  id / for settings on label / inputs.

* Currently uses same variation for <input name=""> attributes
@ZebulanStanphill ZebulanStanphill mentioned this pull request Jul 25, 2018
7 tasks
@ZebulanStanphill
Copy link
Member

I think the Cover Image block would be made redundant by the existence of a Container block. The Cover Image concept could just be a Reusable block inserted as a template. Think about it. Every setting for the Cover Image block would work for a Container block, and adding nesting to the Cover Image block just makes it into a sort of container block anyway.

See also:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Blocks Overall functionality of blocks [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.