Blocks: Support block nesting (Add Columns block, InnerBlocks component) #3745

Merged
merged 13 commits into from Feb 6, 2018

Conversation

Member

aduth commented Nov 30, 2017

Closes #2995
Closes #428
Partially implements #219
Cherry-picks 1efbb9f and 2bd32f8 from #3743
Related: #3705, #2743
Partially reverts #2806 (temporarily)

This pull request seeks to explore support for generalized nested blocks, implementing a InnerBlocks component which effectively behaves as a sub-editor block listing (maintaining its own separate editor state). A block's children are available via its block.innerBlocks property, and can be accessed from a component's edit and save implementations (props.innerBlocks), and updated via edit's setInnerBlocks prop.

Included is a new Columns block which behaves similarly to the Text Columns block, except that it allows insertion of blocks within the rendered columns.

Nesting

What has been implemented:

  • Block parsing, creation, validation, and serialization of nested content
  • Editing block children, including insertion, rearrangement, toolbar and inspector controls

What doesn't work so well yet:

  • Front-end display
  • Reconciliation of selected blocks, particularly in rendering of toolbar and inspector slot contents
  • Reducing columns trims content instead of collapsing
  • General polish and UX behaviors
  • Tests and such

Testing instructions:

Verify that you can insert a new Columns block, and within it, insert, rearrange, edit, and delete child blocks. Verify that saving the post and refreshing the editor respects all changes which were made.

georgestephanis commented Dec 3, 2017

Summing up a brief discussion I had in person with @aduth , general wish-list and considerations (in no way is any of this a blocker or need-to-have for initial merge, just musings I wanted to get enumerated somewhere)

Another example (apart from layout use-cases) would be Contact Forms. Some bits for the future to support that use case:

  • Some blocks would not make sense in a non-nested context -- for example, Fields would not make sense unless you're adding them to a form. So registering blocks, but flagging them as disabled unless in a certain parent? Or adding them to a blacklist on the general context, to still allow them elsewhere? Unsure on implementation.
  • It would be useful to be able to define a default template for nested blocks (as per #3668) -- so for example, a contact form instantiation could start with Name, Email Address, and Message fields every time. (or a Recipe block could start with an Image, List of Ingredients, and Directions blocks)

There were other things not immediately coming to mind, I'll add them as they come up.

@aduth aduth referenced this pull request Dec 4, 2017

Merged

Keep and handle image alignment classes for raw transforms #3724

3 of 3 tasks complete
Member

aduth commented Dec 4, 2017

Some blocks would not make sense in a non-nested context -- for example, Fields would not make sense unless you're adding them to a form. So registering blocks, but flagging them as disabled unless in a certain parent? Or adding them to a blacklist on the general context, to still allow them elsewhere? Unsure on implementation.

Noting that it wasn't specifically needed for a columns block (which should allow any child type), but I had an in-progress change to be able to support this via a block.parents array:

diff --git a/editor/components/inserter/menu.js b/editor/components/inserter/menu.js
index 73bfb7c0..4dc344fb 100644
--- a/editor/components/inserter/menu.js
+++ b/editor/components/inserter/menu.js
@@ -126,6 +126,12 @@ export class InserterMenu extends Component {
                                return false;
                        }
 
+                       // If block defines parent nesting, disallow except when explicitly
+                       // provided as a whitelisted block type.
+                       if ( Array.isArray( block.parents ) ) {
+                               return includes( blockTypes, block.name );
+                       }
+
                        // Block types defined as either `true` or array:
                        //  - True: Allow
                        //  - Array: Check block name within whitelist
@@ -311,7 +317,7 @@ export class InserterMenu extends Component {
                                }
                                { isSearching &&
                                        <div role="menu" className="editor-inserter__search-results">
-                                               { this.renderCategories( this.getVisibleBlocksByCategory( getBlockTypes() ) ) }
+                                               { this.renderCategories( this.getVisibleBlocksByCategory( this.getBlockTypes() ) ) }
                                        </div>
                                }
                        </TabbableContainer>

The thinking being a block could define it's allowed parents for nesting context, and conversely, a parent would define supportable children via a prop on BlockChildren (types={ [ 'core/text', ... ] }).

Member

aduth commented Dec 4, 2017

It would be useful to be able to define a default template for nested blocks (as per #3668) -- so for example, a contact form instantiation could start with Name, Email Address, and Message fields every time. (or a Recipe block could start with an Image, List of Ingredients, and Directions blocks)

I think this would be a good idea: Perhaps as an initialValue or defaultValue on BlockChildren, or as part of the block API to define default children. There's also a general question as to whether children needs to be a separate value of a block, or can be an attribute as any other (in which case leveraging attributes default behavior).

@hedgefield hedgefield referenced this pull request Dec 5, 2017

Closed

Blocks behind blocks #3725

0 of 2 tasks complete
+ attributes = attributes || {};
+
+ // Trim content to avoid creation of intermediary freeform segments
+ innerHTML = innerHTML.trim();
@mtias

mtias Dec 7, 2017

Contributor

How was it being created otherwise?

@mtias

mtias Dec 7, 2017

Contributor

I see it was already present just in a different moment.

blocks/api/serializer.js
* @return {string} Save content
*/
-export function getSaveContent( blockType, attributes ) {
+export function getSaveContent( blockType, attributes, children = [] ) {
@mtias

mtias Dec 7, 2017

Contributor

What's the general thinking behind sometimes referring to innerBlocks and sometimes referring to children?

@aduth

aduth Dec 11, 2017

Member

What's the general thinking behind sometimes referring to innerBlocks and sometimes referring to children?

We should probably align it. innerBlocks is the naming provided from the parser, but children seemed a friendlier / more familiar name. Maybe too familiar though, since it conflicts with the React built-in prop of the same name; could mislead a developer to think they could render it as an element set (which it is not).

Do you have a preference one way or the other?

@mcsf

mcsf Jan 9, 2018

Contributor

I'd lean toward innerBlocks for the reasons you cite, plus the fact that with innerBlocks we reinforce the idea of "blocks" as the central object. Also, the notion of containment behind inner may be more fitting than that of descent behind children.

blocks/api/serializer.js
+ let content = renderToString( contentWithExtraProps );
+
+ // Drop raw HTML wrappers (support dangerous inner HTML without wrapper)
+ content = content.replace( /<\/?wp-raw-html>/g, '' );
@mtias

mtias Dec 7, 2017

Contributor

Is it better to drop after rendering to string? What can be included in wp-raw-html?

@aduth

aduth Dec 11, 2017

Member

Is it better to drop after rendering to string? What can be included in wp-raw-html?

This is a hacky workaround to the lack of <React.Fragment dangerouslySetInnerHTML /> support in React. It achieves the desired result by rendering a wrapper with a known name which is intended to be dropped during serialization. It would be used in cases where we want to render raw markup without a wrapper element (e.g. here for nested blocks, in #3048 for the saved output of Editable).

@aduth aduth referenced this pull request Dec 14, 2017

Merged

Parser: Client-side wpautop #4005

georgestephanis added a commit to Automattic/jetpack that referenced this pull request Dec 16, 2017

Initial start for making a Gutenblock for the Contact Form.
At this point it just represents the parent form -- it doesn't handle any fields yet.

That will largely depend on how WordPress/gutenberg#3745 progresses.
Member

aduth commented Jan 9, 2018

In rebasing this, I've found that our recent changes to treat the editor as a singleton store (#3832) conflict with the intent here to introduce nested block editing via separate store instances. @youknowriad do you have any suggestions on how I might proceed here? Maybe I just need to createStore with a direct reference to the reducer? I'll plan to spend some more time on this tomorrow.

Contributor

youknowriad commented Jan 9, 2018

@youknowriad do you have any suggestions on how I might proceed here? Maybe I just need to createStore with a direct reference to the reducer?

Yes, I think using "Provider" and "it just works" was hiding the fact that the "blocks" become "local state" with the changes introduce in the current PR.

I think we should have a separate store for blocks (not a singleton) instantiated in the BlockChildren component. Ideally it would be used in the root level as well. and the main "store" would only contain a "blocks" reducer maybe being updated with the onChange of the BlockChildren component.

I've previously explored the idea in #2065

Member

aduth commented Jan 9, 2018

I think we should have a separate store for blocks (not a singleton) instantiated in the BlockChildren component. Ideally it would be used in the root level as well. and the main "store" would only contain a "blocks" reducer maybe being updated with the onChange of the BlockChildren component.

What do you have in mind being managed in each of these cases? A primary benefit of creating the nested provider context is not needing to consider implications of hierarchy and nesting in the store itself, which is quite convenient. It's also not just blocks, but state such as selection, hover, etc.

Contributor

youknowriad commented Jan 9, 2018

@aduth

So the root store would handle "post", "layout" (since edit-post is still there), "saving", "metaboxes", "editor", "edits"...

The blocks store would be "blocks", "selection", "hover" (all what's block related)

Do we need some of these informations in the upper level? maybe, I don't know right now, this is discoverable while implementing.


This is not different from what you were doing originally. It's just making it clear that the "BlocksProvider" is about the blocks only since I assume the other parts of the store were useless in the "BlockChildren", am I wrong?

I believe a quicker alternative to mimic exactly what you were doing is to recreate the store, expose a createStore from "editor/store/index.js" and use it in BlockChildren. Maybe I'm wrong but I think the separate stores is nicer, it clarifies what handles what.

Member

jorgefilipecosta commented Jan 9, 2018

Hi @aduth, I have been testing this branch and generally, it works well, nice work 👍 There are some things to polish but I think this is a good shape and we may solve polish/solve some bugs.

Regarding the singleton store vs multiple stores discussion, while the multiple stores being used may make things simple at the start, I think it may cause us some problems in the future to make some features works as expected. E.g: undo may affect the content of multiple stores.

I think we may be able to use a single store. We can have a higher order reducer "H" that would apply to a reducer that has "multiple instances" "S" e.g: editor, blockSelection. We would dispatch an action to create an instance e.g:"{type: CREATE_EDITOR_CONTEXT, id: uuid}. The reducer "H" would create the instance for us e.g: assign to an object the reducer "S" passed to it to generate the empty state.
The actions affecting the reducer "S" would need to contain the uuid and our higher order reducer "H" would call the reducer with the right state branch. If no uuid is passed our higher order reducer "H" calls our reducer "S" on all branches.
The selectors selecting branches "S" would receive only the branch instead of the state, there would exist a selector to select the right branch based on the uuid.
With this approach, we would be able to apply actions to all the "contexts" e.g: remove all multi-selections, and we would retain the advantages of a single store, e.g: being able to debug with redux dev tools.

Contributor

youknowriad commented Jan 9, 2018

Just a small note that the separate store approach (which is just a convenient way to build a complex local state for a react component) brings us closer to the "Block Editor" being a generic JS module reusable in several contexts (inside WordPress aka Customizer for instance and outside Wordpress).

Contributor

mcsf commented Jan 9, 2018

With regards to nesting proper—ignoring the weaknesses cited in the description—this seems to work well.

BlockChildren is quite interesting. I like the idea that we can swap contexts at that membrane and let its element subtree behave without necessary knowledge of its own nesting. A priori this seems more viable than juggling enhanced reducers, although I used to feel more strongly about striving for a single store.

@aduth aduth changed the title from Blocks: Support block nesting (Add Columns block, BlockChildren component) to Blocks: Support block nesting (Add Columns block, InnerBlocks component) Jan 9, 2018

Member

aduth commented Jan 9, 2018

Force pushed a rebased copy of the branch, bringing the changes up to date, reconciling with the revised state approach, and being more consistent with the naming of "inner blocks" vs. "children" (deferring to the former, since the latter is overloaded already).

I'm observing some strange behaviors with toolbar interactions, which might be related to the block selection not working correctly anymore in the nested block (should be deselecting parent). Going to see if recent revisions in #3743 improve this.

Needs a plan for front-end styling, and for tests to be updated.

Member

aduth commented Jan 9, 2018

Noting another issue is that trying to "Show Advanced Settings" from a block in the visual editor will not display the sidebar. This is because since the nested block component maintains its own full copy of the editor store, it's also expected to manage the display of sidebars (which it doesn't). Toward @youknowriad's earlier points, we likely need to break this down further where the only state managed by InnerBlocks is that of the blocks it's showing.

Contributor

youknowriad commented Jan 10, 2018

Some notes:

  • Is it intentional to make the columns block "wide" or just a leftover?
  • The inspector shows only the container options, how do we show the inner block inspector
  • The columns block doesn't have a block toolbar, what behavior should it have if it had one (I assume showing the inner block's toolbar unless the "container" is selected)
  • I tried multi-selecting some children blocks and clicking backspace. It doesn't work

If we want to make this perfect before merging, it will take a lot of time. So we might want to make a decision here. Do we want to merge this (even a bit broken sometimes) and iterate on it (which could lead to a lot of issues created once released), or should we iterate on the PR which could lead to a giant PR.

Contributor

mtias commented Jan 10, 2018

I am fine with merging an initial implementation if we mark the "columns block" with some text that says it is experimental, and provided we don't break other areas of the editor.

Not sure if this is exactly the right place for this, but I wanted to hop in and just highlight another use case I've been running into. Being able to embed / nest one block inside of another programmatically in a more structured way.

For instance, let's say I have a grid of items. Each one has an image, a title, and then some "paragraph" text. What I want to be able to do is register a custom block that allows users to select how many columns in this grid they want to add, and then for each one, nest an "image" a "heading" and a "paragraph" block inside of a larger column container. Right now (I think) I'd have to recreate the functionality for each custom block, but it would be great just to do something like addEditInnerBlock( 'core/paragraph' );

I'm not sure if this means creating an InnerBlock component that takes the name of a block as a property, or if there's even a way to do it now. But I wanted to flag it because I think being able to make pre-structured, nested blocks will be a pretty common issue for developers.

Member

aduth commented Jan 16, 2018

I'm not sure if this means creating an InnerBlock component that takes the name of a block as a property,

Yes, this is how I imagine this working. A block author will be able to support nesting in their own blocks, limited to a subset of specific blocks if desired. Similarly, it might be desirable to allow blocks to opt to only be insertable within specific contexts.

Member

aduth commented Jan 16, 2018

Given some of the issues highlighted in previous comments, I've been iterating over the past week to move away from the nested provider context toward handling nested blocks in a single store. I've made some good progress and am hoping to propose the changes here this week.

@mtias mtias added this to the 2.1 milestone Jan 17, 2018

Owner

aaronjorbin commented Jan 17, 2018

Love the concept and where this is going. I would love to see the documentation updates included in one of the near-term updates to that PR. From my perspective, the doc updates can help with the DUX review a lot more than reading code. (also, this PR is awesome). Also Unit Tests.

Member

aduth commented Jan 20, 2018

I've pushed the latest revised approach, which includes migrating away from a nested editor provider and multiple controls toward including nested block awareness in the single editor store. It does this by allowing BlockList to be a reusable component leveraged by the public-facing InnerBlocks component, accepting a rootUID prop of the block from which nesting is expected to occur.

Further, I have eliminated all responsibility of a block implementer to introspect or segment inner blocks, instead enforcing inner blocks as a single continuous flow of markup. In order to create the effect of distinct columns in the Columns block, BlockList has also been enhanced to include understanding of a layouts configuration. The idea of layouts is described in exhaustive detail in the included documentation for InnerBlocks. While not implemented here, the expectation is that this could pair with changes proposed in #4314 to allow top-level layout configuration for wide and full width appearance, in place of current behaviors to implement using getEditWrapperProps.

The circular dependency between InnerBlocks and BlockList is no more, and has been satisfied instead with context-provided BlockList via partial application of a rootUID prop provided by the ancestor BlockList. Similarly, InnerBlocks.Content is will have its content provided via context at the serialization step via a new BlockContentProvider component.

The result of all this is that InnerBlocks is ideally very simple for block implementers to use. See the columns block implementation as a reference example resource, including its output markup. In subsequent pull requests this will be enhanced to support additional restrictions on the types of blocks which can be nested.

A major challenge in the implementation here was ensuring that blocks do not attempt to handle the events of nested content. The result of this is the IgnoreNestedEvents component, which is essentially a glorified stopEventPropagation without actually stopping event propagation. This should handle both DOM-based and virtual event bubbling.

Current Status:

User-Facing Behaviors: For the most part, all behaviors should be implemented and working reasonably well to preserve current block ordering and selection, insert a columns block, change number of columns, and add blocks within columns. A remaining expected behavior is to allow users to move blocks between columns, which as documented in the InnerBlocks component README.md is not much more than changing its layout position.

Block-Author API: InnerBlocks should but does not yet support restrictions on block types allowed within a block. We should probably warn authors who attempt to assign a conflicting layout attribute name to their block; we already have one example of this in our Latest Posts block.

Tests: I made my way through updating a large number of tests taking into account the changes made here, and also stubbing out test cases for testing newly-introduced nested context handling. There's still a number of tests to update, and effort is labor-intensive, but overall a non-concern.

Code: I'm largely happy with the direction here. As it impacts the implementation, I have a few further ideas for refactoring, but none of which is blocking:

  • Refactor insertion point to behave not on index / layout, but on before / after a specific UID, which is less complex to handle in nested context.
  • Decide if there is a better way to structure state to support pointers between levels of nesting (e.g. previous block, parent block). I had briefly worked on a linked list style structure before abandoning it, but something like this may serve better if previous / next / parent is a common operation.

Some of the repetition of rootUID and layout is quite unfortunate, but seems a natural consequence of including support for nested content.

Edit: One other issue I noted is that the contextual toolbar is not shown. The toolbar shows correctly when pinned in the header. I have not investigated the root cause here, but suspect it is not a major concern. To this point I should clarify that there's still room for a fair bit of polish here, but that I wanted to publish the current progress of state and BlockList refactoring.

Contributor

jasmussen commented Jan 20, 2018

Amazing work, Andrew. From just a quick glance, the most recent implementation seems to work far better than the previous implementation. 🔥

As a sidenote, it feels like now that child blocks are going to be more prevalent, improvements to the side UI is going to become more important. I'm working on that, and have a bunch of UI ideas, including #4539, but those should be able to benefit this UI later on.

@aduth aduth referenced this pull request Jan 20, 2018

Open

fix: Link modal z-index bug #4503

6 of 6 tasks complete

aduth added some commits Nov 30, 2017

Editor: Move slot provider to top-level
Nesting editors still want to make use of ancestored slots, e.g. inserter, toolbar, inspector controls
Block List: Move BlockList to layout file
Aiding in GitHub rebase history as explicit move
Revert "Editor: Move slot provider to top-level"
This reverts commit 7b6adced07889fe8eaf2b769f7708b0fc58033aa.
Member

aduth commented Feb 6, 2018

I've pushed a rebased branch resolving latest conflicts and bringing in changes for RawHTML (previously DangerousHTML) merged separately in #4786. This also resolves a previous bug where unselecting a block by clicking the editor background was not well.

This should work fairly well, save for a couple remaining issues:

  • Multi-selection shift expanding behavior differs from master, where shift-selecting upwards multiple times and then shift-selecting downward will reverse the selection direction from the originating block.
    • In master, it will collapse the expansion toward the originating block. This is arguably the correct behavior, but is made difficult by nesting because concepts of "previous" and "next" block require traversing nested hierarchy.
  • A few remaining transforms on nested blocks within Columns will cause the block to disappear (e.g. "Turn into List"). Below proposal implemented in b6a1041.
    • This is because the original block's layout attribute needs to be preserved through the transformation. I've addressed this in a few cases, but in a very ad-hoc fashion. I think this needs to be addressed at a framework level, perhaps in the switchToBlockType block factory function. Since this is at least an issue isolated to Columns, it may be fine to address separately.

@coderkevin The present "columns" block is experimental, very buggy, and questionable if it has a future in Gutenberg.

@GlennMartin1 The Columns block is technically experimental, yes, but mostly functional. I'd expect the issue raised by @coderkevin to be working correctly, and have asked him to give it another try with a fresh copy of the branch after the latest rebases.

Contributor

mtias commented Feb 6, 2018

This is working very well for me. Great job!

One thing to fix is that the grid elements are centered on the front-end.

image

The other would be to add the wide/full-width options to the columns block container, as it's what opens up so many possibilities.

Member

aduth commented Feb 6, 2018

@mtias Is it possible the centering is a style applied by your theme? I'd see no reason why it would be appearing as centered, and does not for me in Twenty Seventeen.

Left-aligned

@aduth can you please add 5 columns option as well?

Now we can have any possible number of columns and variations but 5 columns option.
I know my clients will need it for pricing tables.

Member

aduth commented Feb 6, 2018

@aduth can you please add 5 columns option as well?

There were already styles for 2-6 columns, so should have allowed these to be assigned. Fixed in 0c1e6f4.

The other would be to add the wide/full-width options to the columns block container

Added in a6ee8da. The reason I might have hesitated is that we may want to deprecate wide alignment in favor of wide layout, with the new layouting system implemented here.

Contributor

mtias commented Feb 6, 2018

@aduth yes, it's a margin auto applied to some elements. Seems fine to disregard.

Contributor

mtias commented Feb 6, 2018

The reason I might have hesitated is that we may want to deprecate wide alignment in favor of wide layout, with the new layouting system implemented here.

Yes, but we might apply that transformation across the board based on #4314

Contributor

mtias commented Feb 6, 2018

@jasmussen this is for later, but I think we should expose column count control in the toolbar (not only inspector) as it seems important enough to be a main interaction point.

Contributor

paulwilde commented Feb 6, 2018

For columns I think one consideration would be to allow for difference widths to apply per column. So say you had 2 columns, having the ability to drag from in between each column to make a 1/3 - 2/3 split. This would allow for having columns where you had an image in the smaller column with accompanying text in the larger one.

Another idea would be to add a blacklist (or whitelist) to set which blocks can be applied to each column width split. It doesn't make sense having the ability to having 3 cover image blocks side-by-side as an example, as it puts too much burden on developers to ensure every single configuration looks right for each block type.

Obviously these are desired features on top of the block nesting implementation. I'll create separate tickets about both once this gets merged.

Member

aduth commented Feb 6, 2018

@paulwilde Both valid ideas, and both of which appear to be quite compatible with what's currently been implemented here. I'd agree it'd be good to pursue these enhancements separately.

aduth added some commits Feb 6, 2018

Member

aduth commented Feb 6, 2018

While I fully expect some rough edges remain, I believe this is finally ready for merge as an initial implementation. Despite the large number of changes, there were actually many pull requests which were split off from this one as prerequisite blockers, noted below for future reference:

#4005
#4585
#4591
#4684
#4707
#4708
#4732
#4765
#4786

Major Changes

  • BlockList enhanced to support two additional props:
    • rootUID: The UID of the parent block from which nested blocks are to descend (undefined for top-level blocks)
      • Many selectors and action creators have been updated to require that a rootUID is specified as an argument
    • layouts: See related documentation
  • New InnerBlocks and InnerBlocks.Content components which can be used by block implementers to edit and save nested blocks, respectively
  • New (experimental) Columns block
  • New block API: wp.blocks.cloneBlock
  • Updated block API: wp.block.createBlock now accepts a third argument, innerBlocks
  • New filter: blocks.switchToBlockType.transformedBlock for filtering an individual transformed result of switchToBlockType
  • New "global" attribute layout: All blocks support this attribute, though it is currently only ever assigned in the Columns block
    • Subsequent pull requests will explore leveraging the layout attribute as a replacement for the current "wide" and "full" alignments
  • Changed block: The "Posts List" block layout attribute has been updated to postsLayout per the addition of the new global layout attribute (breaking change)
  • State selector changes
    • New selectors: getBlockRootUID, getAdjacentBlock
    • Renamed selectors: getBlockUids -> getBlockOrder
    • Removed selectors: isFirstBlock, isLastBlock

Known Bugs

Multi-select shift expansion behaves differently when collapsing a selection (see #3745 (comment))

@aduth aduth merged commit edd2818 into master Feb 6, 2018

2 checks passed

codecov/project 38.93% (+0.32%) compared to ba87962
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@aduth aduth deleted the add/block-children branch Feb 6, 2018

Contributor

jasmussen commented Feb 6, 2018

🔥🎉

gthayer pushed a commit to gthayer/gutenberg that referenced this pull request Feb 6, 2018

Blocks: Support block nesting (Add Columns block, InnerBlocks compone…
…nt) (#3745)

* Editor: Move slot provider to top-level

Nesting editors still want to make use of ancestored slots, e.g. inserter, toolbar, inspector controls

* Block API: Implement support for parsing, generating, serializing nested blocks

* Block List: Enable block edit to revise inner blocks

* Blocks: Add InnerBlocks component for nested editor

* Blocks: Add Columns block

* Revert "Editor: Move slot provider to top-level"

This reverts commit 7b6adced07889fe8eaf2b769f7708b0fc58033aa.

* Block List: Move BlockList to layout file

Aiding in GitHub rebase history as explicit move

* Framework: Add store and BlockList support for nested context

* Block: Stop propagation but don't handle child events

* Blocks: Rename latest posts layout attribute as postLayout

Otherwise conflicting with newly-introduced "layout" attribute applying to all blocks for nesting arrangement

* Blocks: Add wide / full align options

* Layout: Preserve layout attribute via transform hook

* Blocks: Add experimental modifier to Columns block title

aduth added a commit that referenced this pull request Feb 6, 2018

Why the insistence on defaulting the columns to having Paragraph blocks in them instead of simply having an insert block UI within the column as the default?

By defaulting to paragraph blocks already being in place it means people are constantly going to be deleting the default block after placing the actual blocks they want to use in the columns.

To make it even more annoying... if they don't do things in the correct order when doing what i've described above the process is going to be frustrating. For example if I add a Columns block to my page and then I edit a column and say I don't want a paragraph here I want an image... so I delete the Paragraph block that is there by default. It then adds another default Paragraph block. You have to add the block you actually want to use below that paragraph block and then delete the paragraph block.

I think the assumption that everything defaults to a Paragraph is a mistake. There are going to be all kinds of blocks created by Gutenberg and the insistence on defaulting to Paragraph is a very blog-centric way of looking at things. It would be a lot more helpful to look at things from a WordPress as a CMS standpoint.

It would be a much better user experience if columns allowed you to start from scratch and didn't make the assumption that the first block should be a Paragraph and instead simply provided an insert block UI so you could insert only the blocks you want to insert and not have to go back and clean up what Gutenberg has done for you by default.

Great work team.

New (experimental) Columns block
Note: This block is implemented using CSS grid styling (browser support)

CSS grid is the future, but should we look to use flexbox for now, it has much wider browser support? prefixed Flexbox works as far back as IE10.

Eventually we will also need the following features:

  • Mobile / tablet / desktop columns
  • Padding and margins for each column
Member

aduth commented Feb 7, 2018

Why the insistence on defaulting the columns to having Paragraph blocks in them instead of simply having an insert block UI within the column as the default?

Where was this insisted? The current behavior of the Columns block is implemented as it is largely as a consequence of it inheriting behaviors of the underlying BlockList components it reuses. In other words, the block is something of an embedded editor. As an initial exploration, the UX of the block is very unfinished, and I would expect there to be some iterations more toward what you're describing with optimizing layout editing.

+
+Because `InnerBlocks.Content` will generate a single continuous flow of block markup for nested content, it may be advisable to use [CSS Grid Layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) to assign layout positions. Be aware that CSS grid is [not suported in legacy browsers](https://caniuse.com/#feat=css-grid), and therefore you should consider how this might impact your block's appearance when viewed on the front end of a site in such browsers.
+
+Layouts can be assigned either either as an object (ungrouped layouts) or an array of objects (grouped layouts). These are documented below.
@jaswrks

jaswrks Feb 7, 2018

Contributor

Layouts can be assigned either eithereither.
Oh, already merged. So nevermind, I will fix in a PR. #4910

Contributor

jasmussen commented Feb 7, 2018

Why the insistence on defaulting the columns to having Paragraph blocks in them instead of simply having an insert block UI within the column as the default?

I responded in more detail to a similar question you asked here.

We already have insertion UI in the editor bar, and on the side, and using the slash command for the power users. These all work in a nested context, but require your caret to be where you want the content to be inserted, similar to most other editors that let you insert non-text content, like Word or Pages or Google Docs.

When #3627 gets addressed, the concern of "why is there already a paragraph block here" becomes moot, as any content you insert while the caret is an empty paragraph block replaces it.

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