diff --git a/packages/block-library/src/columns/editor.scss b/packages/block-library/src/columns/editor.scss
index 4e8905f9bbca18..c795dab3085583 100644
--- a/packages/block-library/src/columns/editor.scss
+++ b/packages/block-library/src/columns/editor.scss
@@ -21,15 +21,13 @@
// Fullwide: show margin left/right to ensure there's room for the side UI.
// This is not a 1:1 preview with the front-end where these margins would presumably be zero.
-// @todo This could be revisited, by for example showing this margin only when the parent block was selected first.
-// Then at least an unselected columns block would be an accurate preview.
-.editor-block-list__block[data-align="full"] .wp-block-columns > .editor-inner-blocks {
+.editor-block-list__block[data-align="full"] [data-type="core/columns"][data-align="full"] .wp-block-columns > .editor-inner-blocks {
padding-left: $block-padding;
padding-right: $block-padding;
@include break-small() {
- padding-left: $block-padding + $block-padding + $block-side-ui-width + $block-side-ui-clearance + $block-side-ui-clearance;
- padding-right: $block-padding + $block-padding + $block-side-ui-width + $block-side-ui-clearance + $block-side-ui-clearance;
+ padding-left: $block-container-side-padding;
+ padding-right: $block-container-side-padding;
}
}
diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss
index 8422d2c82571de..bc8bbd83911ade 100644
--- a/packages/block-library/src/editor.scss
+++ b/packages/block-library/src/editor.scss
@@ -25,6 +25,7 @@
@import "./quote/editor.scss";
@import "./rss/editor.scss";
@import "./search/editor.scss";
+@import "./section/editor.scss";
@import "./shortcode/editor.scss";
@import "./spacer/editor.scss";
@import "./subhead/editor.scss";
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index 81a0e9bec632b4..23b8bb52d33f45 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -44,6 +44,7 @@ import * as pullquote from './pullquote';
import * as reusableBlock from './block';
import * as rss from './rss';
import * as search from './search';
+import * as section from './section';
import * as separator from './separator';
import * as shortcode from './shortcode';
import * as spacer from './spacer';
@@ -106,6 +107,7 @@ export const registerCoreBlocks = () => {
pullquote,
rss,
search,
+ section,
separator,
reusableBlock,
spacer,
diff --git a/packages/block-library/src/section/edit.js b/packages/block-library/src/section/edit.js
new file mode 100644
index 00000000000000..fcf17c40c48bce
--- /dev/null
+++ b/packages/block-library/src/section/edit.js
@@ -0,0 +1,48 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { Fragment } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import {
+ InspectorControls,
+ InnerBlocks,
+ PanelColorSettings,
+ withColors,
+} from '@wordpress/block-editor';
+
+function SectionEdit( { className, setBackgroundColor, backgroundColor } ) {
+ const styles = {
+ backgroundColor: backgroundColor.color,
+ };
+
+ const classes = classnames( className, backgroundColor.class, {
+ 'has-background': !! backgroundColor.color,
+ } );
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+export default withColors( 'backgroundColor' )( SectionEdit );
diff --git a/packages/block-library/src/section/editor.scss b/packages/block-library/src/section/editor.scss
new file mode 100644
index 00000000000000..f8cb11319ca0b0
--- /dev/null
+++ b/packages/block-library/src/section/editor.scss
@@ -0,0 +1,87 @@
+/**
+ * Section: All Alignment Settings
+ */
+.wp-block[data-type="core/section"] {
+
+ // Ensure not rendering outside the element
+ // as -1px causes overflow-x scrollbars
+ .editor-block-list__insertion-point {
+ left: 0;
+ right: 0;
+ }
+
+ // Only applied when background is added to cancel out padding
+ > .editor-block-list__block-edit > div > .wp-block-section.has-background > .editor-inner-blocks {
+ margin-top: -#{$block-padding*2 + $block-spacing};
+ margin-bottom: -#{$block-padding*2 + $block-spacing};
+ }
+
+ // Full Width Blocks
+ // specificity required to only target immediate child Blocks of Section
+ > .editor-block-list__block-edit > div > .wp-block-section > .editor-inner-blocks > .editor-block-list__layout > .wp-block[data-align="full"] {
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: $block-padding*2;
+ padding-right: $block-padding*2;
+
+ @include break-small() {
+ padding-left: $block-padding*4 + $block-spacing/2; // 58px
+ padding-right: $block-padding*4 + $block-spacing/2; // 58px
+ }
+ }
+
+ // Full Width Blocks with a background (ie: has padding)
+ > .editor-block-list__block-edit > div > .wp-block-section.has-background > .editor-inner-blocks > .editor-block-list__layout > .wp-block[data-align="full"] {
+ // note: using position `left` causes hoz scrollbars so
+ // we opt to use margin instead
+ // the 30px matches the hoz padding applied in `theme.scss`
+ // added when the Block has a background set
+ margin-left: -30px;
+
+ // 60px here is x2 the hoz padding from `theme.scss` added when
+ // the Block has a background set
+ // note: also duplicated below for full width Sections
+ width: calc(100% + 60px);
+ }
+}
+
+/**
+ * Section: Full Width Alignment
+ */
+.wp-block[data-type="core/section"][data-align="full"] {
+
+ // First tier of InnerBlocks must act like the container of the standard canvas
+ > .editor-block-list__block-edit > div > .wp-block-section > .editor-inner-blocks {
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: 0;
+ padding-right: 0;
+
+ > .editor-block-list__layout {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ }
+
+ // Full Width Blocks
+ // specificity required to only target immediate child Blocks of Section
+ > .editor-block-list__block-edit > div > .wp-block-section > .editor-inner-blocks > .editor-block-list__layout > .wp-block[data-align="full"] {
+ padding-right: 0;
+ padding-left: 0;
+ left: 0;
+ width: 100%;
+ max-width: none;
+
+ // Allow to be flush with the edges of the canvas
+ > .editor-block-list__block-edit {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ }
+
+ // Full Width Blocks with a background (ie: has padding)
+ // note: also duplicated above for all Section widths
+ > .editor-block-list__block-edit > div > .wp-block-section.has-background > .editor-inner-blocks > .editor-block-list__layout > .wp-block[data-align="full"] {
+ width: calc(100% + 60px);
+ }
+}
diff --git a/packages/block-library/src/section/index.js b/packages/block-library/src/section/index.js
new file mode 100644
index 00000000000000..d119bfa324a4d6
--- /dev/null
+++ b/packages/block-library/src/section/index.js
@@ -0,0 +1,66 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { Path, SVG } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import { InnerBlocks, getColorClassName } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+
+export const name = 'core/section';
+
+export const settings = {
+ title: __( 'Section' ),
+
+ icon: ,
+
+ category: 'layout',
+
+ description: __( 'A wrapping section acting as a container for other blocks.' ),
+
+ keywords: [ __( 'container' ), __( 'wrapper' ), __( 'row' ) ],
+
+ supports: {
+ align: [ 'wide', 'full' ],
+ anchor: true,
+ html: false,
+ },
+
+ attributes: {
+ backgroundColor: {
+ type: 'string',
+ },
+ customBackgroundColor: {
+ type: 'string',
+ },
+ },
+
+ edit,
+
+ save( { attributes } ) {
+ const { backgroundColor, customBackgroundColor } = attributes;
+
+ const backgroundClass = getColorClassName( 'background-color', backgroundColor );
+ const className = classnames( backgroundClass, {
+ 'has-background': backgroundColor || customBackgroundColor,
+ } );
+
+ const styles = {
+ backgroundColor: backgroundClass ? undefined : customBackgroundColor,
+ };
+
+ return (
+
+
+
+ );
+ },
+};
diff --git a/packages/block-library/src/section/theme.scss b/packages/block-library/src/section/theme.scss
new file mode 100644
index 00000000000000..fc3a7edd3bbcd3
--- /dev/null
+++ b/packages/block-library/src/section/theme.scss
@@ -0,0 +1,10 @@
+.wp-block-section {
+
+ &.has-background {
+ // Matches paragraph Block padding
+ // Todo: normalise with variables
+ padding: 20px 30px;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+}
diff --git a/packages/block-library/src/theme.scss b/packages/block-library/src/theme.scss
index 5aa1b7281537cf..501240a4a9bbdc 100644
--- a/packages/block-library/src/theme.scss
+++ b/packages/block-library/src/theme.scss
@@ -6,6 +6,7 @@
@import "./pullquote/theme.scss";
@import "./quote/theme.scss";
@import "./search/theme.scss";
+@import "./section/theme.scss";
@import "./separator/theme.scss";
@import "./table/theme.scss";
@import "./video/theme.scss";
diff --git a/packages/e2e-tests/fixtures/block-transforms.js b/packages/e2e-tests/fixtures/block-transforms.js
index bf1e218958c41a..57687da9444f80 100644
--- a/packages/e2e-tests/fixtures/block-transforms.js
+++ b/packages/e2e-tests/fixtures/block-transforms.js
@@ -144,6 +144,10 @@ export const EXPECTED_TRANSFORMS = {
originalBlock: 'Search',
availableTransforms: [],
},
+ core__section: {
+ originalBlock: 'Section',
+ availableTransforms: [],
+ },
core__separator: {
originalBlock: 'Separator',
availableTransforms: [],
diff --git a/packages/e2e-tests/fixtures/blocks/core__section.html b/packages/e2e-tests/fixtures/blocks/core__section.html
new file mode 100644
index 00000000000000..cf21a46b87991f
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__section.html
@@ -0,0 +1,10 @@
+
+
+
+
This is a section block.
+
+
+
+
Section block content.
+
+
diff --git a/packages/e2e-tests/fixtures/blocks/core__section.json b/packages/e2e-tests/fixtures/blocks/core__section.json
new file mode 100644
index 00000000000000..7f27655f472cd4
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__section.json
@@ -0,0 +1,36 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/section",
+ "isValid": true,
+ "attributes": {
+ "backgroundColor": "secondary",
+ "align": "full"
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "This is a section block.",
+ "dropCap": false
+ },
+ "innerBlocks": [],
+ "originalContent": "This is a section block.
"
+ },
+ {
+ "clientId": "_clientId_1",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "Section block content.",
+ "dropCap": false
+ },
+ "innerBlocks": [],
+ "originalContent": "Section block content.
"
+ }
+ ],
+ "originalContent": "\n\t\n\n\t
"
+ }
+]
diff --git a/packages/e2e-tests/fixtures/blocks/core__section.parsed.json b/packages/e2e-tests/fixtures/blocks/core__section.parsed.json
new file mode 100644
index 00000000000000..7cdcbf0c9c6eca
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__section.parsed.json
@@ -0,0 +1,46 @@
+[
+ {
+ "blockName": "core/section",
+ "attrs": {
+ "backgroundColor": "secondary",
+ "align": "full"
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/paragraph",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n\tThis is a section block.
\n\t",
+ "innerContent": [
+ "\n\tThis is a section block.
\n\t"
+ ]
+ },
+ {
+ "blockName": "core/paragraph",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n\tSection block content.
\n\t",
+ "innerContent": [
+ "\n\tSection block content.
\n\t"
+ ]
+ }
+ ],
+ "innerHTML": "\n\n\t\n\n\t
\n",
+ "innerContent": [
+ "\n\n\t",
+ null,
+ "\n\n\t",
+ null,
+ "
\n"
+ ]
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n",
+ "innerContent": [
+ "\n"
+ ]
+ }
+]
diff --git a/packages/e2e-tests/fixtures/blocks/core__section.serialized.html b/packages/e2e-tests/fixtures/blocks/core__section.serialized.html
new file mode 100644
index 00000000000000..79f0c5eb54cc12
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__section.serialized.html
@@ -0,0 +1,9 @@
+
+
+
This is a section block.
+
+
+
+
Section block content.
+
+
diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/section.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/section.test.js.snap
new file mode 100644
index 00000000000000..1e36e894ba4693
--- /dev/null
+++ b/packages/e2e-tests/specs/blocks/__snapshots__/section.test.js.snap
@@ -0,0 +1,17 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Section can be created using the block inserter 1`] = `
+"
+
+"
+`;
+
+exports[`Section can be created using the slash inserter 1`] = `
+"
+
+"
+`;
diff --git a/packages/e2e-tests/specs/blocks/section.test.js b/packages/e2e-tests/specs/blocks/section.test.js
new file mode 100644
index 00000000000000..90ae574d0fcbe7
--- /dev/null
+++ b/packages/e2e-tests/specs/blocks/section.test.js
@@ -0,0 +1,32 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ clickBlockAppender,
+ searchForBlock,
+ getEditedPostContent,
+ createNewPost,
+} from '@wordpress/e2e-test-utils';
+
+describe( 'Section', () => {
+ beforeEach( async () => {
+ await createNewPost();
+ } );
+
+ it( 'can be created using the block inserter', async () => {
+ await searchForBlock( 'Section' );
+ await page.click( '.editor-block-list-item-section' );
+ await page.keyboard.type( 'Section block' );
+
+ expect( await getEditedPostContent() ).toMatchSnapshot();
+ } );
+
+ it( 'can be created using the slash inserter', async () => {
+ await clickBlockAppender();
+ await page.keyboard.type( '/section' );
+ await page.keyboard.press( 'Enter' );
+ await page.keyboard.type( 'Section block' );
+
+ expect( await getEditedPostContent() ).toMatchSnapshot();
+ } );
+} );
diff --git a/packages/edit-post/src/components/visual-editor/style.scss b/packages/edit-post/src/components/visual-editor/style.scss
index 1ba9ce38e01139..d0ec876d553152 100644
--- a/packages/edit-post/src/components/visual-editor/style.scss
+++ b/packages/edit-post/src/components/visual-editor/style.scss
@@ -31,11 +31,10 @@
// Use specific selector to not affect nested block toolbars.
&[data-align="full"] > .block-editor-block-list__block-edit > .block-editor-block-contextual-toolbar {
height: 0; // This collapses the container to an invisible element without margin.
- width: 100%;
+ width: calc(100% - 1px); // -1px to account for inner element left: 1px value causing overflow-x scrollbars
margin-left: 0;
margin-right: 0;
text-align: center;
-
// This float rule takes the toolbar out of the flow, without it having to be absolute positioned.
// This is necessary because otherwise the mere presence of the toolbar can push down content.
// Pairs with relative rule on line 49.