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

Variations (formerly patterns) API for blocks #16283

Closed
11 of 16 tasks
youknowriad opened this issue Jun 25, 2019 · 48 comments
Closed
11 of 16 tasks

Variations (formerly patterns) API for blocks #16283

youknowriad opened this issue Jun 25, 2019 · 48 comments

Comments

@youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Jun 25, 2019

(Related design explorations for Block Patterns #17335.)

Description

A lot of block use the "setup state" pattern where some options/layouts are shown to the block user while inserterting the block:

  • The Columns block shows a number of layouts
  • The table block shows a setup state to choose rows and columns
  • The Cover block could benefit from a similar "initial layouts" state
  • A number of third party blocks use that pattern as well.

Capture d’écran 2019-06-25 à 2 41 47 PM

All these are similar in behavior. The user picks a variation and this variation applies initial attributes and innerBlocks content to the selected block.

The idea of this issue is to generalize that concept. Similarily to how we define block style variations, a block type should be able to define Block Variations the user could pick from.

API

The API could look something like this:

const blockTypeSettings = {
   // ...
   variations: [
       { name: __( 'Two columns; equal split' ), innerBlocks: [ ['core/column'], ['core/column'] ] },
       { name: __( 'Three columns; equal split' ), innerBlocks: [ ['core/column'], ['core/column'], ['core/column'] ] },
   ]

The variations can also define just attributes

 // ...
   variations: [
       { name: __( 'Big Quote' ), attributes: { size:'big' } },
       { name: __( 'Small Quote' ), attributes: { size:'small' } },
   ]

These are just examples to illustrate the API and not a final design.

We'd also probably need APIs to register/unregister these variations (similar to the style variations).

UI

In terms of UI, we could explore different possibilities:

  • Offer these variations as a setup state (Like a placeholder that shows up when you insert the block)
  • Offer these variations in the "Block Switcher" dropdown
  • Offer these variations in the inspector panel

Related #16129

Current Tasks

  1. Create an API for registering variations very close to block styles (#18270).
  2. Refactor Columns block to use this new API making the placeholder open for customization (#18283).
  3. Refactor more blocks that fit like:
    • Media+Text (#18343) – closed as UX wasn't great
    • Table – it doesn't seem to be a good idea
    • Cover – it doesn't seem to be a good idea
  4. Explore how we can modify the search logic in the inserter to match blocks through variations as proposed by @kjellr (#19243).
  5. Rename the API from patterns to variations (#19966).
  6. Blocks: Promote block variations to stable API (#20068).
  7. Add three e2e tests that use example scenarios listed in the description of #20068 (#20286).
  8. Block API docs should be updated to include details about this new API (#20145).
  9. Adding a tutorial for users would be fantastic.
  10. Extract common logic as an initial screen for blocks that define variations.
  11. Integrate variations with the / inserter.
  12. Explore how it could be embedded in the block inspector.
  13. Explore how blocks could inform about the variation applied.
@klihelp
Copy link

@klihelp klihelp commented Jun 25, 2019

Good idea the initial "setup state". Thank you for the grat setup state on the columns block.

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 3, 2019

👋 I'd like to get the design leg of this started with a few thoughts, examples, and questions.

As Riad noted above, this is an expansion of the columns work added in #15663:

Frame 2

Here are a handful of potential use cases for this same pattern in core blocks:

Example - Cover

Example - Latest Posts

Example - Media Text

Example - Table

This isn't necessarily limited to layout though — theoretically, blocks could use this to allow users to choose any sort of default state for their block. A couple possible ideas:

Example - Map

Example - Form

In many cases, it'll make sense to include these options in the setup state. Beyond that, they'll likely also need to live in either the Block Switcher menu, or in the Sidebar:

Layout Options in the Sidebar:
03

Layout Options in the Block Switcher:
Frame 2 1

Questions

  1. From a user perspective, how are these different from Block Styles? Depending on the treatment and placement of these options, I could see many of them being analogous: The two Quote block styles for instance feel very much like layouts. Should blocks be able to present Block Styles in the placeholder state?
  2. In the placeholder state, should we include the option of a multi-step process? In #15663, we opted to keep the layout options confined to a single screen. I'm of the mind that this is generally the best approach for simplicity's sake, but we know some 3rd party blocks have found uses for multi-step placeholders (I'd love to hear thoughts from @richtabor here, since I know CoBlocks makes use of this pattern).
  3. In addition to the placeholder, does it make sense to include these options in the Sidebar, or in the Block Switcher dropdown (or both)? I think the sidebar makes more sense personally. In the case of the columns block for instance, the sidebar allows for there to be separate controls for number of columns and column layout.
  4. You'll note in the comps above, one of the options is highlighted with darker, thicker border. This is to denote the default/suggested state. This may not be 100% necessary, but I do think it helps direct the user a little bit. The visual treatment is something that's being explored in #15906 (comment), as it has broader implications for other areas of the UI. Please weigh in there with any thoughts.

Anything I'm missing? Looking forward to discussion around this. 🙂

@youknowriad
Copy link
Contributor Author

@youknowriad youknowriad commented Jul 3, 2019

I love those explorations ❤️

From a user perspective, how are these different from Block Styles? Depending on the treatment and placement of these options, I could see many of them being analogous: The two Quote block styles for instance feel very much like layouts. Should blocks be able to present Block Styles in the placeholder state?

I think the main difference is that a block style is never a destructive change. All content/attributes/options are there while choosing a different pattern can result in sub-blocks being removed, content being changed...

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 3, 2019

I think the main difference is that a block style is never a destructive change. All content/attributes/options are there while choosing a different pattern can result in sub-blocks being removed, content being changed...

That's a good point. It makes me think that these will need a really clear name, to differentiate them from Block Styles... We'll have to think through that a bit too.

@mtias
Copy link
Contributor

@mtias mtias commented Jul 5, 2019

This is great, thanks for exploring the variations. The transforms menu is potentially becoming a bit unwieldy: it includes block transforms, block styles, and now these new layout options. How can we make it more clear and functional? Should we split things out of it?

@karmatosed
Copy link
Member

@karmatosed karmatosed commented Jul 5, 2019

This is great, thanks for exploring the variations. The transforms menu is potentially becoming a bit unwieldy: it includes block transforms, block styles, and now these new layout options. How can we make it more clear and functional? Should we split things out of it?

This is a really key point to me, right now they are being hidden. I have found so many people don't even notice there are styles. Hiding layout under this could lead to yet another really powerful feature being hidden.

Out there idea, are these all 3 separate enough to want different locations? I can see perhaps styling and layout being 'similar' but I do come back to maybe they just are all separate actions that need to be ensured to be surfaced.

@richtabor
Copy link
Member

@richtabor richtabor commented Jul 6, 2019

2. In the placeholder state, should we include the option of a multi-step process? In #15663, we opted to keep the layout options confined to a single screen. I'm of the mind that this is generally the best approach for simplicity's sake, but we know some 3rd party blocks have found uses for multi-step placeholders (I'd love to hear thoughts from @richtabor here, since I know CoBlocks makes use of this pattern).

I'd say having something in place for folks who need multi-step would be nice to have, further encouraging developers to utilize core components and patterns. I don't think we should back ourselves into a corner for simplicity's sake, as we know blocks will continue to get more and more advanced as Gutenberg, and more importantly JS WordPress developers, mature.

@chrisvanpatten
Copy link
Member

@chrisvanpatten chrisvanpatten commented Jul 6, 2019

I really like the look in the startup state, but have some doubts about putting these options in the block switcher, or allowing them in general after a block is set up. The idea that these could be destructive is a bit concerning. Even with a robust undo system, it seems like this could create more problems than it solves, and make people inherently less likely to use the feature.

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 8, 2019

The transforms menu is potentially becoming a bit unwieldy: it includes block transforms, block styles, and now these new layout options.

This is a really key point to me, right now they are being hidden. I have found so many people don't even notice there are styles. Hiding layout under this could lead to yet another really powerful feature being hidden.

Yeah — I mocked it up, but I don't think it makes sense for these to live in the block switcher, for many of the reasons above. The Block Switcher menu is becoming a bit of a "Junk drawer" of features: Switch to a different type of block, create a group, switch styles, etc. If blocks want to include the ability to adjust these layouts after the setup state (more on that below), I think the sidebar is the place for it.

The idea that these could be destructive is a bit concerning. Even with a robust undo system, it seems like this could create more problems than it solves, and make people inherently less likely to use the feature.

I think it's important to note that the ability to change layout post-placeholder state should be up to the block. For some (like media + text for instance), it makes sense to allow it. But for other blocks (like the form block example above), it won't. Block authors should be able to opt-out as needed.

@mtias
Copy link
Contributor

@mtias mtias commented Jul 8, 2019

Block transformations are already lossy, so in that sense I don't see layout transformations as that different.

One thing I'd also like to consider in the exploration is how to surface certain layout / pattern configurations before you insert a block, to help with discovery. For example, a "Form" block could have pre-defined sets using this new API for "Contact Us", "Feedback", and so on. Can you discover these by searching? Would a two step-insertion make sense in the inserter (so first click is on "Form" and second is on one of the pre-arranged templates).

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 8, 2019

One thing I'd also like to consider in the exploration is how to surface certain layout / pattern configurations before you insert a block, to help with discovery. For example, a "Form" block could have pre-defined sets using this new API for "Contact Us", "Feedback", and so on. Can you discover these by searching? Would a two step-insertion make sense in the inserter (so first click is on "Form" and second is on one of the pre-arranged templates).

Interesting. I can mock that up to see how it feels. 👍

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 9, 2019

For example, a "Form" block could have pre-defined sets using this new API for "Contact Us", "Feedback", and so on. Can you discover these by searching?

When there's a matching layout/pattern, a simple option would be to simply write out the template name underneath the block name. Then, if a user selects that item, we could serve up the matching template by default, and they could skip the layout-picking step:

Opt1-A

This still keeps it a one-step process when coming from a matching search.

Would a two step-insertion make sense in the inserter (so first click is on "Form" and second is on one of the pre-arranged templates).

One way to do this would be to swap out the entire panel to show the patterns after you select the main block:

2step-1

... or to have some sort of expanded drawer:

2step-2

Either of those two-step processes could also work well in the context of search, using the dark border to show the matching pattern result.

@chrisvanpatten
Copy link
Member

@chrisvanpatten chrisvanpatten commented Jul 9, 2019

Block transformations are already lossy, so in that sense I don't see layout transformations as that different.

I would challenge this a bit — when transforming a block to another type of block, I think the idea of data loss is intuitive and implicit. One block behaves differently from another block, with different properties and interfaces.

This feature is different: describing it as a 'template' implies that it's primarily a visual change, which will have little impact on the data of the block, just how it's visualized.

Now that said, @kjellr's examples in the above mocks are super interesting, and has me wondering… what if these patterns were simply treated in the block transform UI equally to any other transformations?

Really, what's the difference between transforming a block to another block and changing the 'pattern' of a block? To the end user, the effect is the same, and explaining the distinction between a block template/pattern and a separate block could be tricky, especially when the line is kind of fuzzy. A block pattern change that effectively resets the block (e.g. a change from 6 columns to 2 columns, which would likely lead to some major data loss) is hardly any different than changing a block anyway.

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 10, 2019

Really, what's the difference between transforming a block to another block and changing the 'pattern' of a block? To the end user, the effect is the same, and explaining the distinction between a block template/pattern and a separate block could be tricky, especially when the line is kind of fuzzy. A block pattern change that effectively resets the block (e.g. a change from 6 columns to 2 columns, which would likely lead to some major data loss) is hardly any different than changing a block anyway.

Yes, this is an interesting point. I'm thinking this may rely a bit on the way these "patterns" are described to the user. If they're presented as "layouts", I wouldn't expect data loss. But if they're presented as different "types" of a block for instance, that might make sense.

One thing I'm repeatedly realizing is that some of these examples are relatively simple (a pattern that changes the column measurements of a Media & Text block for instance), while others are more broader structural changes (like the Form examples above). Even within the Columns block, there could theoretically be patterns that merely adjust the width of existing columns, and others that add or remove columns entirely. These sort of feel like they're the same thing, but the latter is much more consequential.

@chrisvanpatten
Copy link
Member

@chrisvanpatten chrisvanpatten commented Jul 10, 2019

One thing I'm repeatedly realizing is that some of these examples are relatively simple (a pattern that changes the column measurements of a Media & Text block for instance), while others are more broader structural changes (like the Form examples above).

The Form example is the one that jumped out at me as well. At a certain point, you get into a "Ship of Theseus" debate — if you're effectively rebuilding the block, is it really the same block?

@jorgefilipecosta
Copy link
Member

@jorgefilipecosta jorgefilipecosta commented Jul 11, 2019

Really important discussion happening here, full of awesome ideas ❤️
It seems we did not arrive yet on the UI to use, would having a branch with a prototype help make the decision in this phase or it is still too soon? If yes, any thoughts on the first approach to try?

@mapk
Copy link
Contributor

@mapk mapk commented Jul 11, 2019

Lot's of great work happening here! I love the consistent setup screen for the blocks.

To get a v1 implemented is a consistent way, I'm suggesting...

  1. Provide setup screen for blocks that will not lose content if the layout is changed (ie. Media+Text, Columns, Cover, Latest Posts). Yes, I believe we can get the Columns block to work this way.
  2. Layout options should be duplicated in the Block Inspector sidebar.
  3. For each of those blocks, I believe "Layout" works well for the term used.

When thinking about more complex blocks like a Form block, I believe the setup screen works best for selecting a "type" of form, keeping the Inserter free of these things. In these cases where a change of form would most likely cause a loss of content, it would be best to not include the different forms in the Inspector sidebar. If the user wants a different form, they would insert another Form block from the Inserter.

This begins to get a bit foggy for me though in terms of Style variations and Layouts. Style variations are found in the block's transform tool, while Layouts would be found in the sidebar. The separation makes sense symantically, but currently the transform tool offers layout variants as well. How would these resolve?

With this fogginess, I question whether other blocks would also follow this pattern? ie. Quote block, Gallery block, etc.

@kjellr
Copy link
Contributor

@kjellr kjellr commented Jul 12, 2019

To get a v1 implemented is a consistent way, I'm suggesting...

  1. Provide setup screen for blocks that will not lose content if the layout is changed (ie. Media+Text, Columns, Cover, Latest Posts). Yes, I believe we can get the Columns block to work this way.
  2. Layout options should be duplicated in the Block Inspector sidebar.
  3. For each of those blocks, I believe "Layout" works well for the term used.

This makes sense. All of these are fairly non-controversial, and it makes sense to display these options in both the placeholder and the sidebar.

This begins to get a bit foggy for me though in terms of Style variations and Layouts. Style variations are found in the block's transform tool, while Layouts would be found in the sidebar. The separation makes sense symantically, but currently the transform tool offers layout variants as well. How would these resolve?

One quick clarificaion: Block Styles actually appear in both the transform menu and the sidebar. I'm not sure how that effects our direction here.

@mtias
Copy link
Contributor

@mtias mtias commented Jul 14, 2019

@kjellr these two-step inserter interactions are interesting, I think there is something to it to continue exploring as it addresses the main issue of discoverability, which none of the in-block solutions do.

Let's not get too caught up in the data portability issues, these templates / layouts / patterns are always going to be changing attributes or inner block content. They are less useful as a transformation, and more useful as initial setup conditions, but can work in both. This is also the case with the larger full page templates we support for CPTs and such.

Columns always had this issue — if you switched from 4 columns down to 2, we need to make a decision on what happens with the content of the 3rd and 4th columns. The same applies to many other attributes you could set that depend on other attributes.

@klihelp
Copy link

@klihelp klihelp commented Jul 15, 2019

Could be the data stored for until the edit page closed or the page save/update action?
If deciding to restore the 4 columns before saving the page the lost data would be restored.

Columns always had this issue — if you switched from 4 columns down to 2, we need to make a decision on what happens with the content of the 3rd and 4th columns. The same applies to many other attributes you could set that depend on other attributes.

@gziolo
Copy link
Member

@gziolo gziolo commented Jan 28, 2020

I updated the title and description to use variations rather than patterns as a name for this new API. I'm going to open a follow-up PR which updates all places in the code where patterns are in use to reflect that.

@gziolo
Copy link
Member

@gziolo gziolo commented Jan 31, 2020

#19966 renames block patterns to variations as proposed by @mtias in #16283 (comment):

I think we could combine configurations and layout variations under the same API and exclude block patterns from it. Configurations and layout are fairly similar, and the few differences could be achieved expressively with the API (available in the inserter, available in placeholders, etc). I'd propose we call this variations or configurations to clearly separate it from block patterns.

We are approaching WordPress 5.4 beta and it's about time to evaluate if parts of this new API should be marked as stable. There two parts of the API that share the same methods and field in the block definition. Let me tackle them separately:

  1. There is a block variation picker that is integrated with the Columns block.

    It uses __experimentalBlockVariationPicker component to render variations that are fetched from the core/blocks store. I still seek for an ergonomic way to handle block variations picker case, at the moment it requires changes in edit implementation but I want to make it automatic, in the sense where you only define variations and the rest is handled for you. The biggest challenge is how to find a way in the block editor to preserve the information between independent sessions. Columns block has its own specific implementation that depends on the number of columns set – no columns means that picker needs to be displayed. I don't feel we have enough time to mark it as stable.
  2. There is also an integration of variations with the inserter. There is no native implementation so far but @mcsf is seeking a way to refactor Social Icons block (#17280) to use it. This is how it looks like in action with some custom examples:
    patterns-api-inserter-2
    This part is very solid and addresses all comments raised so far. If we were able to land Social Icons block changes then I would be will confident that the API for the inserter bits is ready to be included in WordPress core.

Let me know what do you think.

@mcsf
Copy link
Contributor

@mcsf mcsf commented Jan 31, 2020

  1. It uses __experimentalBlockVariationPicker component […] I don't feel we have enough time to mark it as stable.

Agreed.

  1. There is also an integration of variations with the inserter. There is no native implementation so far but @mcsf is seeking a way to refactor Social Icons block (#17280) to use it. […] This part is very solid and addresses all comments raised so far. If we were able to land Social Icons block changes then I would be will confident that the API for the inserter bits is ready to be included in WordPress core.

I lost momentum this week (illness, other priorities), which is a shame. Would be great to pick up the pace on this and also explore Marcus's suggestion of turning Social Links into a variation of Navigation, figure out its viability.

@gziolo
Copy link
Member

@gziolo gziolo commented Feb 6, 2020

Now that #19887 is close to merging, I opened #20068 to mark the API for the inserter as stable. It also will work with the Columns block.

__experimentalBlockVariationPicker remains experimental.

@youknowriad
Copy link
Contributor Author

@youknowriad youknowriad commented Feb 27, 2020

@gziolo Should we close this issue and continue in smaller ones if needed?

@gziolo
Copy link
Member

@gziolo gziolo commented Feb 27, 2020

@gziolo Should we close this issue and continue in smaller ones if needed?

We can handle it this way if you prefer 4 issues rather than one :)

Tasks left:

  • Adding a tutorial for users would be fantastic.
  • Extract common logic as an initial screen for blocks that define variations.
  • Integrate variations with the / inserter.
  • Explore how it could be embedded in the block inspector.

@mcsf and @mtias how do you feel about the follow-up tasks proposed?

@mcsf
Copy link
Contributor

@mcsf mcsf commented Feb 27, 2020

how do you feel about the follow-up tasks proposed?

They seem good.

The only other thing I see is exploring what kind of awareness the editor should have of blocks that are a variation, and how the editor would validate whether a block modified by the user still conforms to the variation or not. This exploration would be focused less on technical feasibility and more on identifying pitfalls and defining limits to how smart the editor should be about this. This is because I think there could be some interesting convenient applications for users, but this could also be opening the door for something quite messy.

@mcsf
Copy link
Contributor

@mcsf mcsf commented Feb 28, 2020

Do we need a label for Variations? I'm noticing comments such as this one on Make and other participants trying to get an overview of Variations development.

@gziolo
Copy link
Member

@gziolo gziolo commented Mar 2, 2020

Issues filed:

  • Explore how blocks could inform about the variation applied (#20580)
  • Adding a tutorial for users would be fantastic (#20581)
  • Extract common logic as an initial screen for blocks that define variations (#20582)
  • Integrate variations with the / inserter (#20583)
  • Explore how it could be embedded in the block inspector (#20584)

@gziolo
Copy link
Member

@gziolo gziolo commented Mar 2, 2020

Do we need a label for Variations? I'm noticing comments such as this one on Make and other participants trying to get an overview of Variations development.

On the one hand, it might be a good idea now that I created 5 new issues. One the other hand, I don't expect that there is going to be a huge number of issues opened in the future.

Let's close this issue and continue in follow-ups as discussed.

@gziolo gziolo closed this Mar 2, 2020
Phase 2 automation moved this from Backlog to Done Mar 2, 2020
@mcsf mcsf mentioned this issue Jul 30, 2020
6 tasks
@badfeather
Copy link

@badfeather badfeather commented Jan 22, 2021

Feature request: Open up block settings to the variations API. My primary use-case for the variations is creating custom variants of core blocks for use in themes or plugins. For ease of content editing, it would be very helpful to change the block category of the variation to the theme's category namespace, e.g.:

	wp.blocks.registerBlockVariation(
		'core/heading',
		[
			{
				name: '[namespace]-heading',
				title: 'Section Heading',
				attributes: {
					className: 'section-title',
					level: 2
				},
				settings: {
					category: '[namespace]'
				}
			},
		]
	);

@gziolo
Copy link
Member

@gziolo gziolo commented Jan 22, 2021

I don't see a category on the list of settings to override:

https://github.com/WordPress/gutenberg/blob/master/docs/designers-developers/developers/block-api/block-registration.md#variations-optional

/**
* An object describing a variation defined for the block type.
*
* @typedef {Object} WPBlockVariation
*
* @property {string} name The unique and machine-readable name.
* @property {string} title A human-readable variation title.
* @property {string} [description] A detailed variation description.
* @property {WPIcon} [icon] An icon helping to visualize the variation.
* @property {boolean} [isDefault] Indicates whether the current variation is
* the default one. Defaults to `false`.
* @property {Object} [attributes] Values which override block attributes.
* @property {Array[]} [innerBlocks] Initial configuration of nested blocks.
* @property {Object} [example] Example provides structured data for
* the block preview. You can set to
* `undefined` to disable the preview shown
* for the block type.
* @property {WPBlockVariationScope[]} [scope] The list of scopes where the variation
* is applicable. When not provided, it
* assumes all available scopes.
* @property {string[]} [keywords] An array of terms (which can be translated)
* that help users discover the variation
* while searching.
* @property {Function} [isActive] A function that accepts a block's attributes
* and the variation's attributes and determines
* if a variation is active. This function doesn't
* try to find a match dynamically based on all
* block's attributes, as in many cases some
* attributes are irrelevant. An example would
* be for `embed` block where we only care about
* `providerNameSlug` attribute's value.
*/

It is doable, I can't think of any blocker.

@badfeather
Copy link

@badfeather badfeather commented Jan 25, 2021

A couple more questions/feature requests:

  1. For blocks that allow innerBlocks (columns, group), is there a way to specify an allowedBlocks attribute? If there isn't, this would be very useful.

  2. Can variations be nested in other variations? In other words, do they behave as standard blocks? And if so, how should they be referenced? Live via the variation name, or via the core name with some sort of variation attribute? If they can't, it would be great if they could!

I realize both of these questions fall somewhat into block patterns territory, but I'm looking for solutions that aren't pre-populated with content. Variations have the potential to be much more flexible.

@gziolo
Copy link
Member

@gziolo gziolo commented Jan 26, 2021

@badfeather, the use case with innerBlock enters a different territory. It internally leverages block templates as documented in:
https://github.com/WordPress/gutenberg/blob/master/docs/designers-developers/developers/block-api/block-templates.md
I'm not aware if you are able to define allowedBlocks through this API. I couldn't find anything like that in the docs for InnerBlocks that also defines template prop:
https://github.com/WordPress/gutenberg/tree/master/packages/block-editor/src/components/inner-blocks#template

I agree it would be great if you could have more control over it 👍🏻 There is even an open PR that tries to introduce something similar for the Columns block: #25778.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Overviews
Templates
Phase 2
  
Done
Linked pull requests

Successfully merging a pull request may close this issue.

None yet