diff --git a/docs/how-to-guides/block-tutorial/writing-your-first-block-type.md b/docs/how-to-guides/block-tutorial/writing-your-first-block-type.md
index c17045372a65ea..4a690984011e0f 100644
--- a/docs/how-to-guides/block-tutorial/writing-your-first-block-type.md
+++ b/docs/how-to-guides/block-tutorial/writing-your-first-block-type.md
@@ -64,8 +64,6 @@ The `block.json` file should be added to your plugin. To start a new plugin, cre
Create a basic `block.json` file there:
-{% codetabs %}
-{% JSX %}
```json
{
@@ -77,22 +75,6 @@ Create a basic `block.json` file there:
"editorScript": "file:./build/index.js"
}
```
-
-{% Plain %}
-
-```json
-{
- "apiVersion": 3,
- "title": "Example: Basic",
- "name": "gutenberg-examples/example-01-basic",
- "category": "layout",
- "icon": "universal-access-alt",
- "editorScript": "file:./block.js"
-}
-```
-
-{% end %}
-
### Step 2: Register block in plugin
With the `block.json` in place, the registration for the block is a single function call in PHP, this will setup the block and JavaScript file specified in the `editorScript` property to load in the editor.
@@ -118,8 +100,6 @@ The `edit` function is a component that is shown in the editor when the block is
The `save` function is a component that defines the final markup returned by the block and saved in `post_content`.
-{% codetabs %}
-{% JSX %}
Add the following in `src/index.js`
@@ -140,33 +120,12 @@ registerBlockType( 'gutenberg-examples/example-01-basic-esnext', {
} );
```
-{% Plain %}
-
-Add the following to `block.js`
-
-```js
-( function ( blocks, React ) {
- var el = React.createElement;
-
- blocks.registerBlockType( 'gutenberg-examples/example-01-basic', {
- edit: function () {
- return el( 'p', {}, 'Hello World (from the editor).' );
- },
- save: function () {
- return el( 'p', {}, 'Hola mundo (from the frontend).' );
- },
- } );
-} )( window.wp.blocks, window.React );
-```
-
-{% end %}
### Step 4: Build or add dependency
In order to register the block, an asset php file is required in the same directory as the directory used in `register_block_type()` and must begin with the script's filename.
-{% codetabs %}
-{% JSX %}
+
Build the scripts and asset file which is used to keep track of dependencies and the build version.
@@ -174,23 +133,6 @@ Build the scripts and asset file which is used to keep track of dependencies and
npm run build
```
-{% Plain %}
-
-Create the asset file to load the dependencies for the scripts. The name of this file should be the name of the js file then .asset.php. For this example, create `block.asset.php` with the following:
-
-```php
-
- array(
- 'react',
- 'wp-blocks',
- 'wp-polyfill'
- ),
- 'version' => '0.1'
- );
-```
-
-{% end %}
### Step 5: Confirm
diff --git a/docs/how-to-guides/internationalization.md b/docs/how-to-guides/internationalization.md
index c3194f309fca63..08ce46edb3f581 100644
--- a/docs/how-to-guides/internationalization.md
+++ b/docs/how-to-guides/internationalization.md
@@ -37,9 +37,6 @@ add_action( 'init', 'myguten_block_init' );
In your code, you can include the i18n functions. The most common function is **\_\_** (a double underscore) which provides translation of a simple string. Here is a basic block example:
-{% codetabs %}
-{% JSX %}
-
```js
import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
@@ -64,33 +61,6 @@ registerBlockType( 'myguten/simple', {
} );
```
-{% Plain %}
-
-```js
-const el = React.createElement;
-const { __ } = wp.i18n;
-const { registerBlockType } = wp.blocks;
-const { useBlockProps } = wp.blockEditor;
-
-registerBlockType( 'myguten/simple', {
- title: __( 'Simple Block', 'myguten' ),
- category: 'widgets',
-
- edit: function () {
- const blockProps = useBlockProps( { style: { color: 'red' } } );
-
- return el( 'p', blockProps, __( 'Hello World', 'myguten' ) );
- },
-
- save: function () {
- const blockProps = useBlockProps.save( { style: { color: 'red' } } );
- return el( 'p', blockProps, __( 'Hello World', 'myguten' ) );
- },
-} );
-```
-
-{% end %}
-
In the above example, the function will use the first argument for the string to be translated. The second argument is the text domain which must match the text domain slug specified by your plugin.
Common functions available, these mirror their PHP counterparts are:
diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md
index 024564b7c9eaeb..2a1161827c1369 100644
--- a/docs/how-to-guides/themes/theme-json.md
+++ b/docs/how-to-guides/themes/theme-json.md
@@ -69,8 +69,7 @@ To address this need, we've started to experiment with CSS Custom Properties, ak
- **Presets**: [color palettes](/docs/how-to-guides/themes/theme-support.md#block-color-palettes), [font sizes](/docs/how-to-guides/themes/theme-support.md#block-font-sizes), or [gradients](/docs/how-to-guides/themes/theme-support.md#block-gradient-presets) declared by the theme are converted to CSS Custom Properties and enqueued both the front-end and the editors.
-{% codetabs %}
-{% Input %}
+#### Input
```json
{
@@ -94,7 +93,7 @@ To address this need, we've started to experiment with CSS Custom Properties, ak
}
```
-{% Output %}
+#### Output
```css
body {
@@ -103,12 +102,10 @@ body {
}
```
-{% end %}
-
- **Custom properties**: there's also a mechanism to create your own CSS Custom Properties.
-{% codetabs %}
-{% Input %}
+
+#### Input
```json
{
@@ -124,7 +121,7 @@ body {
}
```
-{% Output %}
+#### Output
```css
body {
@@ -133,8 +130,6 @@ body {
}
```
-{% end %}
-
## Specification
This specification is the same for the three different origins that use this format: core, themes, and users. Themes can override core's defaults by creating a file called `theme.json`. Users, via the site editor, will also be able to override theme's or core's preferences via an user interface that is being worked on.
@@ -166,8 +161,8 @@ The tabs below show WordPress 5.8 supported settings and the ones supported by t
The settings section has the following structure:
-{% codetabs %}
-{% WordPress %}
+
+#### WordPress
```json
{
@@ -231,7 +226,7 @@ The settings section has the following structure:
}
```
-{% Gutenberg %}
+#### Gutenberg
```json
{
@@ -310,8 +305,6 @@ The settings section has the following structure:
}
```
-{% end %}
-
Each block can configure any of these settings separately, providing a more fine-grained control over what exists via `add_theme_support`. The settings declared at the top-level affect to all blocks, unless a particular block overwrites it. It's a way to provide inheritance and configure all blocks at once.
Note, however, that not all settings are relevant for all blocks. The settings section provides an opt-in/opt-out mechanism for themes, but it's the block's responsibility to add support for the features that are relevant to it. For example, if a block doesn't implement the `dropCap` feature, a theme can't enable it for such a block through `theme.json`.
@@ -376,8 +369,8 @@ The naming schema for the classes and the custom properties is as follows:
- Custom Properties: `--wp--preset--{preset-category}--{preset-slug}` such as `--wp--preset--color--black`
- Classes: `.has-{preset-slug}-{preset-category}` such as `.has-black-color`.
-{% codetabs %}
-{% Input %}
+
+#### Input
```json
{
@@ -490,7 +483,7 @@ The naming schema for the classes and the custom properties is as follows:
}
```
-{% Output %}
+#### Output
```css
/* Top-level custom properties */
@@ -540,8 +533,6 @@ body {
```
-{% end %}
-
To maintain backward compatibility, the presets declared via `add_theme_support` will also generate the CSS Custom Properties. If the `theme.json` contains any presets, these will take precedence over the ones declared via `add_theme_support`.
Preset classes are attached to the content of a post by some user action. That's why the engine will add `!important` to these, because user styles should take precedence over theme styles.
@@ -552,8 +543,7 @@ In addition to create CSS Custom Properties for the presets, the `theme.json` al
For example:
-{% codetabs %}
-{% Input %}
+#### Input
```json
{
@@ -578,7 +568,7 @@ For example:
}
```
-{% Output %}
+#### Output
```css
body {
@@ -592,7 +582,6 @@ body {
}
```
-{% end %}
Note that the name of the variable is created by adding `--` in between each nesting level and `camelCase` fields are transformed to `kebab-case`.
@@ -702,9 +691,9 @@ The tabs below show WordPress 5.8 supported styles and the ones supported by the
Each block declares which style properties it exposes via the [block supports mechanism](/docs/reference-guides/block-api/block-supports.md). The support declarations are used to automatically generate the UI controls for the block in the editor. Themes can use any style property via the `theme.json` for any block ― it's the theme's responsibility to verify that it works properly according to the block markup, etc.
-{% codetabs %}
-{% WordPress %}
+
+#### WordPress
```json
{
@@ -784,7 +773,7 @@ Each block declares which style properties it exposes via the [block supports me
}
```
-{% Gutenberg %}
+#### Gutenberg
```json
{
@@ -873,14 +862,11 @@ Each block declares which style properties it exposes via the [block supports me
}
```
-{% end %}
-
### Top-level styles
Styles found at the top-level will be enqueued using the `body` selector.
-{% codetabs %}
-{% Input %}
+#### Input
```json
{
@@ -893,7 +879,7 @@ Styles found at the top-level will be enqueued using the `body` selector.
}
```
-{% Output %}
+#### Output
```css
body {
@@ -901,7 +887,6 @@ body {
}
```
-{% end %}
### Block styles
@@ -909,8 +894,7 @@ Styles found within a block will be enqueued using the block selector.
By default, the block selector is generated based on its name such as `.wp-block-
`. For example, `.wp-block-group` for the `core/group` block. There are some blocks that want to opt-out from this default behavior. They can do so by explicitly telling the system which selector to use for them via the `__experimentalSelector` key within the `supports` section of its `block.json` file. Note that the block needs to be registered server-side for the `__experimentalSelector` field to be available to the style engine.
-{% codetabs %}
-{% Input %}
+#### Input
```json
{
@@ -935,7 +919,7 @@ By default, the block selector is generated based on its name such as `.wp-block
}
```
-{% Output %}
+#### Output
```css
body {
@@ -949,7 +933,6 @@ p { /* The core/paragraph opts out from the default behaviour and uses p as a se
}
```
-{% end %}
#### Referencing a style
@@ -996,8 +979,8 @@ Supported by WordPress:
If they're found in the top-level the element selector will be used. If they're found within a block, the selector to be used will be the element's appended to the corresponding block.
-{% codetabs %}
-{% Input %}
+
+#### Input
```json
{
@@ -1043,7 +1026,7 @@ If they're found in the top-level the element selector will be used. If they're
}
```
-{% Output %}
+#### Output
```css
body {
@@ -1066,8 +1049,6 @@ h3 {
}
```
-{% end %}
-
##### Element pseudo selectors
Pseudo selectors `:hover`, `:focus`, `:visited`, `:active`, `:link`, `:any-link` are supported by Gutenberg.
@@ -1236,8 +1217,8 @@ This is for clarity, but also because we want a mechanism to parse back a variab
For example:
-{% codetabs %}
-{% Input %}
+
+#### Input
```json
{
@@ -1253,7 +1234,7 @@ For example:
}
```
-{% Output %}
+#### Output
```css
body {
@@ -1262,7 +1243,6 @@ body {
}
```
-{% end %}
A few notes about this process:
diff --git a/docs/reference-guides/block-api/block-attributes.md b/docs/reference-guides/block-api/block-attributes.md
index 765d69584a6690..35ec1c1e7c64e4 100644
--- a/docs/reference-guides/block-api/block-attributes.md
+++ b/docs/reference-guides/block-api/block-attributes.md
@@ -375,7 +375,7 @@ Attribute definition:
From here, meta attributes can be read and written by a block using the same interface as any attribute:
-{% codetabs %}
+
{% JSX %}
```js
@@ -388,22 +388,6 @@ edit( { attributes, setAttributes } ) {
},
```
-{% Plain %}
-
-```js
-edit: function( props ) {
- function onChange( event ) {
- props.setAttributes( { author: event.target.value } );
- }
-
- return el( 'input', {
- value: props.attributes.author,
- onChange: onChange,
- } );
-},
-```
-
-{% end %}
#### Considerations
diff --git a/docs/reference-guides/block-api/block-deprecation.md b/docs/reference-guides/block-api/block-deprecation.md
index a1497ec346936d..4d69d9d46843cd 100644
--- a/docs/reference-guides/block-api/block-deprecation.md
+++ b/docs/reference-guides/block-api/block-deprecation.md
@@ -61,9 +61,6 @@ It's important to note that attributes
, supports
, and
### Example:
-{% codetabs %}
-{% JSX %}
-
```js
const { registerBlockType } = wp.blocks;
const attributes = {
@@ -101,46 +98,6 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', {
} );
```
-{% Plain %}
-
-```js
-var el = React.createElement,
- registerBlockType = wp.blocks.registerBlockType,
- attributes = {
- text: {
- type: 'string',
- default: 'some random value',
- },
- },
- supports = {
- className: false,
- };
-
-registerBlockType( 'gutenberg/block-with-deprecated-version', {
- // ... other block properties go here
-
- attributes: attributes,
-
- supports: supports,
-
- save: function ( props ) {
- return el( 'div', {}, props.attributes.text );
- },
-
- deprecated: [
- {
- attributes: attributes,
-
- save: function ( props ) {
- return el( 'p', {}, props.attributes.text );
- },
- },
- ],
-} );
-```
-
-{% end %}
-
In the example above we updated the markup of the block to use a `div` instead of `p`.
## Changing the attributes set
@@ -149,8 +106,6 @@ Sometimes, you need to update the attributes set to rename or modify old attribu
### Example:
-{% codetabs %}
-{% JSX %}
```js
const { registerBlockType } = wp.blocks;
@@ -192,50 +147,6 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', {
} );
```
-{% Plain %}
-
-```js
-var el = React.createElement,
- registerBlockType = wp.blocks.registerBlockType;
-
-registerBlockType( 'gutenberg/block-with-deprecated-version', {
- // ... other block properties go here
-
- attributes: {
- content: {
- type: 'string',
- default: 'some random value',
- },
- },
-
- save: function ( props ) {
- return el( 'div', {}, props.attributes.content );
- },
-
- deprecated: [
- {
- attributes: {
- text: {
- type: 'string',
- default: 'some random value',
- },
- },
-
- migrate: function ( attributes ) {
- return {
- content: attributes.text,
- };
- },
-
- save: function ( props ) {
- return el( 'p', {}, props.attributes.text );
- },
- },
- ],
-} );
-```
-
-{% end %}
In the example above we updated the markup of the block to use a `div` instead of `p` and rename the `text` attribute to `content`.
@@ -246,9 +157,6 @@ E.g: a block wants to migrate a title attribute to a paragraph innerBlock.
### Example:
-{% codetabs %}
-{% JSX %}
-
```js
const { registerBlockType } = wp.blocks;
@@ -292,49 +200,6 @@ registerBlockType( 'gutenberg/block-with-deprecated-version', {
} );
```
-{% Plain %}
-
-```js
-var el = React.createElement,
- registerBlockType = wp.blocks.registerBlockType;
-
-registerBlockType( 'gutenberg/block-with-deprecated-version', {
- // ... block properties go here
-
- deprecated: [
- {
- attributes: {
- title: {
- type: 'string',
- source: 'html',
- selector: 'p',
- },
- },
-
- migrate: function ( attributes, innerBlocks ) {
- const { title, ...restAttributes } = attributes;
-
- return [
- restAttributes,
- [
- createBlock( 'core/paragraph', {
- content: attributes.title,
- fontSize: 'large',
- } ),
- ].concat( innerBlocks ),
- ];
- },
-
- save: function ( props ) {
- return el( 'p', {}, props.attributes.title );
- },
- },
- ],
-} );
-```
-
-{% end %}
-
In the example above we updated the block to use an inner Paragraph block with a title instead of a title attribute.
_Above are example cases of block deprecation. For more, real-world examples, check for deprecations in the [core block library](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src). Core blocks have been updated across releases and contain simple and complex deprecations._
diff --git a/docs/reference-guides/block-api/block-edit-save.md b/docs/reference-guides/block-api/block-edit-save.md
index 35bbd5ae13e1e0..a8b6f9171bdef3 100644
--- a/docs/reference-guides/block-api/block-edit-save.md
+++ b/docs/reference-guides/block-api/block-edit-save.md
@@ -6,8 +6,6 @@ When registering a block with JavaScript on the client, the `edit` and `save` fu
The `edit` function describes the structure of your block in the context of the editor. This represents what the editor will render when the block is used.
-{% codetabs %}
-{% JSX %}
```jsx
import { useBlockProps } from '@wordpress/block-editor';
@@ -26,32 +24,12 @@ const blockSettings = {
};
```
-{% Plain %}
-
-```js
-var blockSettings = {
- apiVersion: 3,
-
- // ...
-
- edit: function () {
- var blockProps = wp.blockEditor.useBlockProps();
-
- return React.createElement( 'div', blockProps, 'Your block.' );
- },
-};
-```
-
-{% end %}
-
### block wrapper props
The first thing to notice here is the use of the `useBlockProps` React hook on the block wrapper element. In the example above, the block wrapper renders a "div" in the editor, but in order for the Gutenberg editor to know how to manipulate the block, add any extra classNames that are needed for the block... the block wrapper element should apply props retrieved from the `useBlockProps` react hook call. The block wrapper element should be a native DOM element, like `` and `
`, or a React component that forwards any additional props to native DOM elements. Using a `` or `` component, for instance, would be invalid.
If the element wrapper needs any extra custom HTML attributes, these need to be passed as an argument to the `useBlockProps` hook. For example to add a `my-random-classname` className to the wrapper, you can use the following code:
-{% codetabs %}
-{% JSX %}
```jsx
import { useBlockProps } from '@wordpress/block-editor';
@@ -72,25 +50,6 @@ const blockSettings = {
};
```
-{% Plain %}
-
-```js
-var blockSettings = {
- apiVersion: 3,
-
- // ...
-
- edit: function () {
- var blockProps = wp.blockEditor.useBlockProps( {
- className: 'my-random-classname',
- } );
-
- return React.createElement( 'div', blockProps, 'Your block.' );
- },
-};
-```
-
-{% end %}
### attributes
@@ -100,8 +59,6 @@ The `attributes` property surfaces all the available attributes and their corres
In this case, assuming we had defined an attribute of `content` during block registration, we would receive and use that value in our edit function:
-{% codetabs %}
-{% JSX %}
```js
edit: ( { attributes } ) => {
@@ -111,21 +68,6 @@ edit: ( { attributes } ) => {
};
```
-{% Plain %}
-
-```js
-edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
-
- return React.createElement(
- 'div',
- blockProps,
- props.attributes.content
- );
-}
-```
-
-{% end %}
The value of `attributes.content` will be displayed inside the `div` when inserting the block in the editor.
@@ -133,8 +75,6 @@ The value of `attributes.content` will be displayed inside the `div` when insert
The isSelected property is an boolean that communicates whether the block is currently selected.
-{% codetabs %}
-{% JSX %}
```jsx
edit: ( { attributes, isSelected } ) => {
@@ -151,35 +91,10 @@ edit: ( { attributes, isSelected } ) => {
};
```
-{% Plain %}
-
-```js
-edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
-
- return React.createElement(
- 'div',
- blockProps,
- [
- 'Your block.',
- props.isSelected ? React.createElement(
- 'span',
- null,
- 'Shows only when the block is selected.'
- )
- ]
- );
-}
-```
-
-{% end %}
-
### setAttributes
This function allows the block to update individual attributes based on user interactions.
-{% codetabs %}
-{% JSX %}
```jsx
edit: ( { attributes, setAttributes, isSelected } ) => {
@@ -201,40 +116,8 @@ edit: ( { attributes, setAttributes, isSelected } ) => {
};
```
-{% Plain %}
-
-```js
-edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
-
- // Simplify access to attributes
- let content = props.attributes.content;
- let mySetting = props.attributes.mySetting;
-
- // Toggle a setting when the user clicks the button
- let toggleSetting = () => props.setAttributes( { mySetting: ! mySetting } );
- return React.createElement(
- 'div',
- blockProps,
- [
- content,
- props.isSelected ? React.createElement(
- 'button',
- { onClick: toggleSetting },
- 'Toggle setting'
- ) : null
- ]
- );
-},
-```
-
-{% end %}
-
When using attributes that are objects or arrays it's a good idea to copy or clone the attribute prior to updating it:
-{% codetabs %}
-{% JSX %}
-
```js
// Good - a new array is created from the old list attribute and a new list item:
const { list } = attributes;
@@ -249,25 +132,6 @@ const addListItem = ( newListItem ) => {
};
```
-{% Plain %}
-
-```js
-// Good - cloning the old list
-var newList = attributes.list.slice();
-
-var addListItem = function ( newListItem ) {
- setAttributes( { list: newList.concat( [ newListItem ] ) } );
-};
-
-// Bad - the list from the existing attribute is modified directly to add the new list item:
-var list = attributes.list;
-var addListItem = function ( newListItem ) {
- list.push( newListItem );
- setAttributes( { list: list } );
-};
-```
-
-{% end %}
Why do this? In JavaScript, arrays and objects are passed by reference, so this practice ensures changes won't affect other code that might hold references to the same data. Furthermore, the Gutenberg project follows the philosophy of the Redux library that [state should be immutable](https://redux.js.org/faq/immutable-data#what-are-the-benefits-of-immutability)—data should not be changed directly, but instead a new version of the data created containing the changes.
@@ -275,8 +139,6 @@ Why do this? In JavaScript, arrays and objects are passed by reference, so this
The `save` function defines the way in which the different attributes should be combined into the final markup, which is then serialized into `post_content`.
-{% codetabs %}
-{% JSX %}
```jsx
save: () => {
@@ -286,21 +148,6 @@ save: () => {
};
```
-{% Plain %}
-
-```js
-save: function() {
- var blockProps = wp.blockEditor.useBlockProps.save();
-
- return React.createElement(
- 'div',
- blockProps,
- 'Your block.'
- );
-}
-```
-
-{% end %}
For most blocks, the return value of `save` should be an [instance of WordPress Element](/packages/element/README.md) representing how the block is to appear on the front of the site.
@@ -326,8 +173,6 @@ Like the `edit` function, when rendering static blocks, it's important to add th
As with `edit`, the `save` function also receives an object argument including attributes which can be inserted into the markup.
-{% codetabs %}
-{% JSX %}
```jsx
save: ( { attributes } ) => {
@@ -337,21 +182,7 @@ save: ( { attributes } ) => {
};
```
-{% Plain %}
-```js
-save: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps.save();
-
- return React.createElement(
- 'div',
- blockProps,
- props.attributes.content
- );
-}
-```
-
-{% end %}
When saving your block, you want to save the attributes in the same format specified by the attribute source definition. If no attribute source is specified, the attribute will be saved to the block's comment delimiter. See the [Block Attributes documentation](/docs/reference-guides/block-api/block-attributes.md) for more details.
@@ -361,8 +192,6 @@ Here are a couple examples of using attributes, edit, and save all together. For
### Saving Attributes to Child Elements
-{% codetabs %}
-{% JSX %}
```jsx
attributes: {
@@ -396,46 +225,6 @@ save: ( { attributes } ) => {
},
```
-{% Plain %}
-
-```js
-attributes: {
- content: {
- type: 'string',
- source: 'html',
- selector: 'p'
- }
-},
-
-edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
- var updateFieldValue = function( val ) {
- props.setAttributes( { content: val } );
- }
-
- return React.createElement(
- 'div',
- blockProps,
- React.createElement(
- wp.components.TextControl,
- {
- label: 'My Text Field',
- value: props.attributes.content,
- onChange: updateFieldValue,
-
- }
- )
- );
-},
-
-save: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps.save();
-
- return React.createElement( 'div', blockProps, props.attributes.content );
-},
-```
-
-{% end %}
### Saving Attributes via Serialization
@@ -443,8 +232,6 @@ Ideally, the attributes saved should be included in the markup. However, there a
This example could be for a dynamic block, such as the [Latest Posts block](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-library/src/latest-posts/index.js), which renders the markup server-side. The save function is still required, however in this case it simply returns null since the block is not saving content from the editor.
-{% codetabs %}
-{% JSX %}
```jsx
attributes: {
@@ -474,41 +261,6 @@ save: () => {
}
```
-{% Plain %}
-
-```js
-attributes: {
- postsToShow: {
- type: 'number',
- }
-},
-
-edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
-
- return React.createEleement(
- 'div',
- blockProps,
- React.createElement(
- wp.components.TextControl,
- {
- label: 'Number Posts to Show',
- value: props.attributes.postsToShow,
- onChange: function( val ) {
- props.setAttributes( { postsToShow: parseInt( val ) } );
- },
- }
- )
- );
-},
-
-save: function() {
- return null;
-}
-```
-
-{% end %}
-
## Validation
When the editor loads, all blocks within post content are validated to determine their accuracy in order to protect against content loss. This is closely related to the saving implementation of a block, as a user may unintentionally remove or modify their content if the editor is unable to restore a block correctly. During editor initialization, the saved markup for each block is regenerated using the attributes that were parsed from the post's content. If the newly-generated markup does not match what was already stored in post content, the block is marked as invalid. This is because we assume that unless the user makes edits, the markup should remain identical to the saved content.
diff --git a/docs/reference-guides/filters/autocomplete-filters.md b/docs/reference-guides/filters/autocomplete-filters.md
index 1ce47219529181..85581f62e4af01 100644
--- a/docs/reference-guides/filters/autocomplete-filters.md
+++ b/docs/reference-guides/filters/autocomplete-filters.md
@@ -8,8 +8,7 @@ The `Autocomplete` component found in `@wordpress/block-editor` applies this fil
Here is an example of using the `editor.Autocomplete.completers` filter to add an acronym completer. You can find full documentation for the autocompleter interface with the `Autocomplete` component in the `@wordpress/components` package.
-{% codetabs %}
-{% JSX %}
+
```jsx
// Our completer
@@ -45,48 +44,3 @@ wp.hooks.addFilter(
appendAcronymCompleter
);
```
-
-{% Plain %}
-
-```js
-// Our completer
-var acronymCompleter = {
- name: 'acronyms',
- triggerPrefix: '::',
- options: [
- { letters: 'FYI', expansion: 'For Your Information' },
- { letters: 'AFAIK', expansion: 'As Far As I Know' },
- { letters: 'IIRC', expansion: 'If I Recall Correctly' },
- ],
- getOptionKeywords: function ( abbr ) {
- var expansionWords = abbr.expansion.split( /\s+/ );
- return [ abbr.letters ].concat( expansionWords );
- },
- getOptionLabel: function ( acronym ) {
- return acronym.letters;
- },
- getOptionCompletion: function ( abbr ) {
- return React.createElement(
- 'abbr',
- { title: abbr.expansion },
- abbr.letters
- );
- },
-};
-
-// Our filter function
-function appendAcronymCompleter( completers, blockName ) {
- return blockName === 'my-plugin/foo'
- ? completers.concat( acronymCompleter )
- : completers;
-}
-
-// Adding the filter
-wp.hooks.addFilter(
- 'editor.Autocomplete.completers',
- 'my-plugin/autocompleters/acronyms',
- appendAcronymCompleter
-);
-```
-
-{% end %}
diff --git a/docs/reference-guides/filters/block-filters.md b/docs/reference-guides/filters/block-filters.md
index 912403c4838941..e269ba9ef19917 100644
--- a/docs/reference-guides/filters/block-filters.md
+++ b/docs/reference-guides/filters/block-filters.md
@@ -179,8 +179,6 @@ Used to modify the block's `edit` component. It receives the original block `Blo
_Example:_
-{% codetabs %}
-{% JSX %}
```js
const { createHigherOrderComponent } = wp.compose;
@@ -207,36 +205,6 @@ wp.hooks.addFilter(
);
```
-{% Plain %}
-
-```js
-var el = React.createElement;
-
-var withMyPluginControls = wp.compose.createHigherOrderComponent( function (
- BlockEdit
-) {
- return function ( props ) {
- return el(
- React.Fragment,
- {},
- el( BlockEdit, props ),
- el(
- wp.blockEditor.InspectorControls,
- {},
- el( wp.components.PanelBody, {}, 'My custom control' )
- )
- );
- };
-}, 'withMyPluginControls' );
-
-wp.hooks.addFilter(
- 'editor.BlockEdit',
- 'my-plugin/with-inspector-controls',
- withMyPluginControls
-);
-```
-
-{% end %}
Note that as this hook is run for _all blocks_, consuming it has potential for performance regressions particularly around block selection metrics.
@@ -267,9 +235,6 @@ Used to modify the block's wrapper component containing the block's `edit` compo
_Example:_
-{% codetabs %}
-{% JSX %}
-
```js
const { createHigherOrderComponent } = wp.compose;
@@ -294,39 +259,10 @@ wp.hooks.addFilter(
);
```
-{% Plain %}
-
-```js
-var el = React.createElement;
-
-var withClientIdClassName = wp.compose.createHigherOrderComponent( function (
- BlockListBlock
-) {
- return function ( props ) {
- var newProps = {
- ...props,
- className: 'block-' + props.clientId,
- };
-
- return el( BlockListBlock, newProps );
- };
-}, 'withClientIdClassName' );
-
-wp.hooks.addFilter(
- 'editor.BlockListBlock',
- 'my-plugin/with-client-id-class-name',
- withClientIdClassName
-);
-```
-
-{% end %}
-
Adding new properties to the block's wrapper component can be achieved by adding them to the `wrapperProps` property of the returned component.
_Example:_
-{% codetabs %}
-{% JSX %}
```js
const { createHigherOrderComponent } = wp.compose;
@@ -346,32 +282,6 @@ wp.hooks.addFilter(
);
```
-{% Plain %}
-
-```js
-var el = React.createElement;
-var hoc = wp.compose.createHigherOrderComponent;
-
-var withMyWrapperProp = hoc( function ( BlockListBlock ) {
- return function ( props ) {
- var newProps = {
- ...props,
- wrapperProps: {
- ...props.wrapperProps,
- 'data-my-property': 'the-value',
- },
- };
- return el( BlockListBlock, newProps );
- };
-}, 'withMyWrapperProp' );
-wp.hooks.addFilter(
- 'editor.BlockListBlock',
- 'my-plugin/with-my-wrapper-prop',
- withMyWrapperProp
-);
-```
-
-{% end %}
## Removing Blocks
@@ -379,8 +289,6 @@ wp.hooks.addFilter(
Adding blocks is easy enough, removing them is as easy. Plugin or theme authors have the possibility to "unregister" blocks.
-{% codetabs %}
-{% JSX %}
```js
// my-plugin.js
@@ -392,16 +300,6 @@ domReady( function () {
} );
```
-{% Plain %}
-
-```js
-// my-plugin.js
-wp.domReady( function () {
- wp.blocks.unregisterBlockType( 'core/verse' );
-} );
-```
-
-{% end %}
and load this script in the Editor
diff --git a/docs/reference-guides/richtext.md b/docs/reference-guides/richtext.md
index f908c7585bc1b8..1a4509318b72b7 100644
--- a/docs/reference-guides/richtext.md
+++ b/docs/reference-guides/richtext.md
@@ -25,8 +25,7 @@ There are a number of core blocks using the RichText component. The JavaScript e
## Example
-{% codetabs %}
-{% JSX %}
+
```jsx
import { registerBlockType } from '@wordpress/blocks';
@@ -66,46 +65,6 @@ registerBlockType( /* ... */, {
} );
```
-{% Plain %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- content: {
- type: 'string',
- source: 'html',
- selector: 'h2',
- },
- },
-
- edit: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps();
-
- return React.createElement( wp.blockEditor.RichText, Object.assign( blockProps, {
- tagName: 'h2', // The tag here is the element output and editable in the admin
- value: props.attributes.content, // Any existing content, either from the database or an attribute default
- allowedFormats: [ 'core/bold', 'core/italic' ], // Allow the content to be made bold or italic, but do not allow other formatting options
- onChange: function( content ) {
- props.setAttributes( { content: content } ); // Store updated content as a block attribute
- },
- placeholder: __( 'Heading...' ), // Display this text before any content has been added by the user
- } ) );
- },
-
- save: function( props ) {
- var blockProps = wp.blockEditor.useBlockProps.save();
-
- return React.createElement( wp.blockEditor.RichText.Content, Object.assign( blockProps, {
- tagName: 'h2', value: props.attributes.content // Saves Content added in the editor...
to the database for frontend display
- } ) );
- }
-} );
-```
-
-{% end %}
-
## Common Issues & Solutions
While using the RichText component a number of common issues tend to appear.
diff --git a/packages/block-editor/src/components/editable-text/README.md b/packages/block-editor/src/components/editable-text/README.md
index 86607349ae8176..aa5a2f4b1962b8 100644
--- a/packages/block-editor/src/components/editable-text/README.md
+++ b/packages/block-editor/src/components/editable-text/README.md
@@ -47,40 +47,6 @@ _Optional._ Called when the block can be removed. `forward` is true when the sel
## Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- content: {
- source: 'html',
- selector: 'div',
- },
- },
-
- edit: function( props ) {
- return React.createElement( wp.editor.EditableText, {
- className: props.className,
- value: props.attributes.content,
- onChange: function( content ) {
- props.setAttributes( { content: content } );
- }
- } );
- },
-
- save: function( props ) {
- return React.createElement( wp.editor.EditableText.Content, {
- value: props.attributes.content
- } );
- }
-} );
-```
-
-{% ESNext %}
-
```js
const { registerBlockType } = wp.blocks;
const { EditableText } = wp.editor;
@@ -110,5 +76,3 @@ registerBlockType( /* ... */, {
}
} );
```
-
-{% end %}
diff --git a/packages/block-editor/src/components/plain-text/README.md b/packages/block-editor/src/components/plain-text/README.md
index 4e59789fd612c7..aa15758118afdc 100644
--- a/packages/block-editor/src/components/plain-text/README.md
+++ b/packages/block-editor/src/components/plain-text/README.md
@@ -20,33 +20,6 @@ _Optional._ The component forwards the `ref` property to the `TextareaAutosize`
## Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- content: {
- type: 'string',
- },
- },
-
- edit: function( props ) {
- return React.createElement( wp.blockEditor.PlainText, {
- className: props.className,
- value: props.attributes.content,
- onChange: function( content ) {
- props.setAttributes( { content: content } );
- },
- } );
- },
-} );
-```
-
-{% ESNext %}
-
```js
import { registerBlockType } from '@wordpress/blocks';
import { PlainText } from '@wordpress/block-editor';
@@ -72,4 +45,3 @@ registerBlockType( /* ... */, {
} );
```
-{% end %}
diff --git a/packages/block-editor/src/components/rich-text/README.md b/packages/block-editor/src/components/rich-text/README.md
index d17f987a34cf0e..4251debfa16c54 100644
--- a/packages/block-editor/src/components/rich-text/README.md
+++ b/packages/block-editor/src/components/rich-text/README.md
@@ -80,41 +80,6 @@ trimmed.
## Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- content: {
- source: 'html',
- selector: 'h2',
- },
- },
-
- edit: function( props ) {
- return React.createElement( wp.blockEditor.RichText, {
- tagName: 'h2',
- className: props.className,
- value: props.attributes.content,
- onChange: function( content ) {
- props.setAttributes( { content: content } );
- }
- } );
- },
-
- save: function( props ) {
- return React.createElement( wp.blockEditor.RichText.Content, {
- tagName: 'h2', value: props.attributes.content
- } );
- }
-} );
-```
-
-{% ESNext %}
-
```js
import { registerBlockType } from '@wordpress/blocks';
import { RichText } from '@wordpress/block-editor';
@@ -146,7 +111,6 @@ registerBlockType( /* ... */, {
} );
```
-{% end %}
## RichTextToolbarButton
@@ -154,26 +118,6 @@ Slot to extend the format toolbar. Use it in the edit function of a `registerFor
### Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.richText.registerFormatType( /* ... */, {
- /* ... */
- edit: function( props ) {
- return React.createElement(
- wp.blockEditor.RichTextToolbarButton, {
- icon: 'editor-code',
- title: 'My formatting button',
- onClick: function() { /* ... */ }
- isActive: props.isActive,
- } );
- },
- /* ... */
-} );
-```
-
-{% ESNext %}
```js
import { registerFormatType } from '@wordpress/rich-text';
@@ -194,5 +138,3 @@ registerFormatType( /* ... */, {
/* ... */
} );
```
-
-{% end %}
diff --git a/packages/block-editor/src/components/url-input/README.md b/packages/block-editor/src/components/url-input/README.md
index 46f673ecd35545..b7c48c191cc282 100644
--- a/packages/block-editor/src/components/url-input/README.md
+++ b/packages/block-editor/src/components/url-input/README.md
@@ -36,41 +36,6 @@ This prop is passed directly to the `URLInput` component.
## Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- url: {
- type: 'string'
- },
- text: {
- type: 'string'
- }
- },
-
- edit: function( props ) {
- return React.createElement( wp.blockEditor.URLInputButton, {
- className: props.className,
- url: props.attributes.url,
- onChange: function( url, post ) {
- props.setAttributes( { url: url, text: (post && post.title) || 'Click here' } );
- }
- } );
- },
-
- save: function( props ) {
- return React.createElement( 'a', {
- href: props.attributes.url,
- }, props.attributes.text );
- }
-} );
-```
-
-{% ESNext %}
```js
import { registerBlockType } from '@wordpress/blocks';
@@ -103,7 +68,6 @@ registerBlockType( /* ... */, {
} );
```
-{% end %}
# `URLInput`
@@ -172,41 +136,6 @@ Start opting into the new margin-free styles that will become the default in a f
## Example
-{% codetabs %}
-{% ES5 %}
-
-```js
-wp.blocks.registerBlockType( /* ... */, {
- // ...
-
- attributes: {
- url: {
- type: 'string'
- },
- text: {
- type: 'string'
- }
- },
-
- edit: function( props ) {
- return React.createElement( wp.blockEditor.URLInput, {
- className: props.className,
- value: props.attributes.url,
- onChange: function( url, post ) {
- props.setAttributes( { url: url, text: (post && post.title) || 'Click here' } );
- }
- } );
- },
-
- save: function( props ) {
- return React.createElement( 'a', {
- href: props.attributes.url,
- }, props.attributes.text );
- }
-} );
-```
-
-{% ESNext %}
```js
import { registerBlockType } from '@wordpress/blocks';
@@ -240,5 +169,3 @@ registerBlockType( /* ... */, {
}
} );
```
-
-{% end %}
From 55285a8c85b3d802b894571fd616fddbdbb6dbbe Mon Sep 17 00:00:00 2001
From: Chad Chadbourne <13856531+chad1008@users.noreply.github.com>
Date: Fri, 17 Nov 2023 10:18:58 -0500
Subject: [PATCH 28/29] Tabs: Fix flaky unit tests (#55950)
---
packages/components/src/tabs/test/index.tsx | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/packages/components/src/tabs/test/index.tsx b/packages/components/src/tabs/test/index.tsx
index d2a035e436c194..fac8127c4cc0d8 100644
--- a/packages/components/src/tabs/test/index.tsx
+++ b/packages/components/src/tabs/test/index.tsx
@@ -524,6 +524,15 @@ describe( 'Tabs', () => {
await screen.findByRole( 'tab', { name: 'Alpha' } )
).toHaveFocus();
+ // This assertion ensures the component has had time to fully
+ // render, preventing flakiness.
+ // see https://github.com/WordPress/gutenberg/pull/55950
+ await waitFor( () =>
+ expect(
+ screen.getByRole( 'tab', { name: 'Beta' } )
+ ).toHaveAttribute( 'tabindex', '-1' )
+ );
+
// Because all other tabs should have `tabindex=-1`, pressing Tab
// should NOT move the focus to the next tab, which is Beta.
// Instead, focus should go to the currently selected tabpanel (alpha).
@@ -847,6 +856,16 @@ describe( 'Tabs', () => {
// onSelect should not be called since the disabled tab is
// highlighted, but not selected.
await user.keyboard( '[Tab]' );
+
+ // This assertion ensures focus has time to move to the first
+ // tab before the test proceeds, preventing flakiness.
+ // see https://github.com/WordPress/gutenberg/pull/55950
+ await waitFor( () =>
+ expect(
+ screen.getByRole( 'tab', { name: 'Alpha' } )
+ ).toHaveFocus()
+ );
+
await user.keyboard( '[ArrowLeft]' );
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
From bb8adcd41cea151b3529204249b899c3a53abada Mon Sep 17 00:00:00 2001
From: Chad Chadbourne <13856531+chad1008@users.noreply.github.com>
Date: Fri, 17 Nov 2023 11:04:25 -0500
Subject: [PATCH 29/29] Tabs: cleanup and improvements (#56224)
---
packages/components/CHANGELOG.md | 4 ++++
packages/components/src/tabs/index.tsx | 14 ++++++++++++--
.../components/src/tabs/stories/index.story.tsx | 8 ++++++++
packages/components/src/tabs/tab.tsx | 8 ++++----
packages/components/src/tabs/tabpanel.tsx | 6 +++---
5 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 37ad2a65da28af..eb6e595e304ecf 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Experimental
+
+- `Tabs`: Memoize and expose the component context ([#56224](https://github.com/WordPress/gutenberg/pull/56224)).
+
## 25.12.0 (2023-11-16)
### Bug Fix
diff --git a/packages/components/src/tabs/index.tsx b/packages/components/src/tabs/index.tsx
index 59eb83f5240590..12dd0b4fcc83f4 100644
--- a/packages/components/src/tabs/index.tsx
+++ b/packages/components/src/tabs/index.tsx
@@ -8,7 +8,7 @@ import * as Ariakit from '@ariakit/react';
* WordPress dependencies
*/
import { useInstanceId } from '@wordpress/compose';
-import { useLayoutEffect, useRef } from '@wordpress/element';
+import { useLayoutEffect, useMemo, useRef } from '@wordpress/element';
/**
* Internal dependencies
@@ -154,8 +154,16 @@ function Tabs( {
setSelectedId,
] );
+ const contextValue = useMemo(
+ () => ( {
+ store,
+ instanceId,
+ } ),
+ [ store, instanceId ]
+ );
+
return (
-
+
{ children }
);
@@ -164,4 +172,6 @@ function Tabs( {
Tabs.TabList = TabList;
Tabs.Tab = Tab;
Tabs.TabPanel = TabPanel;
+Tabs.Context = TabsContext;
+
export default Tabs;
diff --git a/packages/components/src/tabs/stories/index.story.tsx b/packages/components/src/tabs/stories/index.story.tsx
index 08e29589881707..ce8c8324edaee4 100644
--- a/packages/components/src/tabs/stories/index.story.tsx
+++ b/packages/components/src/tabs/stories/index.story.tsx
@@ -20,6 +20,14 @@ import Button from '../../button';
const meta: Meta< typeof Tabs > = {
title: 'Components (Experimental)/Tabs',
component: Tabs,
+ subcomponents: {
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
+ 'Tabs.TabList': Tabs.TabList,
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
+ 'Tabs.Tab': Tabs.Tab,
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
+ 'Tabs.TabPanel': Tabs.TabPanel,
+ },
parameters: {
actions: { argTypesRegex: '^on.*' },
controls: { expanded: true },
diff --git a/packages/components/src/tabs/tab.tsx b/packages/components/src/tabs/tab.tsx
index 03e5d80871c56a..4bfc99e8ef43b1 100644
--- a/packages/components/src/tabs/tab.tsx
+++ b/packages/components/src/tabs/tab.tsx
@@ -2,14 +2,14 @@
* WordPress dependencies
*/
-import { useContext, forwardRef } from '@wordpress/element';
+import { forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { TabProps } from './types';
import warning from '@wordpress/warning';
-import { TabsContext } from './context';
+import { useTabsContext } from './context';
import { Tab as StyledTab } from './styles';
import type { WordPressComponentProps } from '../context';
@@ -17,9 +17,9 @@ export const Tab = forwardRef<
HTMLButtonElement,
WordPressComponentProps< TabProps, 'button', false >
>( function Tab( { children, id, disabled, render, ...otherProps }, ref ) {
- const context = useContext( TabsContext );
+ const context = useTabsContext();
if ( ! context ) {
- warning( '`Tabs.TabList` must be wrapped in a `Tabs` component.' );
+ warning( '`Tabs.Tab` must be wrapped in a `Tabs` component.' );
return null;
}
const { store, instanceId } = context;
diff --git a/packages/components/src/tabs/tabpanel.tsx b/packages/components/src/tabs/tabpanel.tsx
index f477d1d3b4b437..8e8d72280a4935 100644
--- a/packages/components/src/tabs/tabpanel.tsx
+++ b/packages/components/src/tabs/tabpanel.tsx
@@ -6,7 +6,7 @@
* WordPress dependencies
*/
-import { forwardRef, useContext } from '@wordpress/element';
+import { forwardRef } from '@wordpress/element';
/**
* Internal dependencies
@@ -15,14 +15,14 @@ import type { TabPanelProps } from './types';
import { TabPanel as StyledTabPanel } from './styles';
import warning from '@wordpress/warning';
-import { TabsContext } from './context';
+import { useTabsContext } from './context';
import type { WordPressComponentProps } from '../context';
export const TabPanel = forwardRef<
HTMLDivElement,
WordPressComponentProps< TabPanelProps, 'div', false >
>( function TabPanel( { children, id, focusable = true, ...otherProps }, ref ) {
- const context = useContext( TabsContext );
+ const context = useTabsContext();
if ( ! context ) {
warning( '`Tabs.TabPanel` must be wrapped in a `Tabs` component.' );
return null;