Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
fbfa318
Initial commit
sethrubenstein Mar 30, 2025
d26e8b7
Moving back to a manually constructed list in the parent block
sethrubenstein Mar 30, 2025
6c020e0
Moving more toward iAPI for easier use
sethrubenstein Apr 1, 2025
d60a969
Moving more toward iAPI for easier use
sethrubenstein Apr 1, 2025
2f30c9d
Some final cleanup before submitting pr
sethrubenstein Apr 1, 2025
47c2063
Fixes php linting errors and forgot to hook on style functions for co…
sethrubenstein Apr 1, 2025
a9df24d
Adding fixtures/tests updating block library version number
sethrubenstein Apr 1, 2025
2836077
Why is PHPCS reporting this as misformatted??
sethrubenstein Apr 1, 2025
cf93391
Whoops mis-replaced this file
sethrubenstein Apr 1, 2025
fed61c1
iAPI bug fix, merge artifact - ensuring dynamic styles generation wor…
sethrubenstein Apr 1, 2025
fa90dbe
iAPI bug fix, merge artifact - ensuring dynamic styles generation wor…
sethrubenstein Apr 1, 2025
329f3a2
Fixing failed tabs fixtures and removing old readme files from prc-bl…
sethrubenstein Apr 1, 2025
2ebfd35
PHPCS linting
sethrubenstein Apr 1, 2025
863f737
I was really overthinking that, untill I was up late overthinking it.
sethrubenstein Apr 2, 2025
88df184
Accessibility improvements and cleanup
sethrubenstein Apr 2, 2025
8b21aee
Regen tabs fixtures
sethrubenstein Apr 2, 2025
759836b
Cool, that slotfill key scoping solution from @Mamaduka works
sethrubenstein Apr 9, 2025
9045eec
Moving tabs icons into icon library proper as requested by @Infinite-…
sethrubenstein Apr 9, 2025
3d69321
Some updates for better keyboard handler support
sethrubenstein Apr 14, 2025
598981a
#34079 Fixing accesibility on frontend tab render
sethrubenstein Apr 23, 2025
b38e279
Refactor tabs, pulling the latest out of prc-block-library/tabs for G…
sethrubenstein Sep 10, 2025
ef1eaaf
Update included context to match core block library pattern
sethrubenstein Sep 10, 2025
6832705
Making tabs & tab blocks experimental
sethrubenstein Sep 22, 2025
cee89d3
Merge branch 'trunk' into core/tabs
sethrubenstein Sep 22, 2025
9816923
Fix tabs experimental placement
sethrubenstein Sep 22, 2025
9684714
Pull custom event. Per work on core/dialog
sethrubenstein Sep 22, 2025
b60598b
Resolving comments in review
sethrubenstein Sep 25, 2025
779349c
Moving to requestAnimationFrame and cancelAnimationFrame for cleaning…
sethrubenstein Sep 25, 2025
b08e10b
alphabetical order
sethrubenstein Sep 25, 2025
4e45b8c
alphabetical order
sethrubenstein Sep 25, 2025
be1830a
Merge branch 'trunk' into core/tabs
sethrubenstein Sep 25, 2025
1402c06
Refactor color styles function to remove unnecessary parameter
sethrubenstein Sep 25, 2025
0017090
Merge branch 'core/tabs' of github.com:pewresearch/gutenberg into cor…
sethrubenstein Sep 25, 2025
9e734b9
List order
sethrubenstein Sep 25, 2025
d166b86
list order
sethrubenstein Sep 25, 2025
98a3c55
Order
sethrubenstein Sep 25, 2025
040fb5e
Order
sethrubenstein Sep 25, 2025
0e596b5
Updates:
sethrubenstein Oct 23, 2025
bbf0700
Updates
sethrubenstein Oct 23, 2025
ea2322e
Updated to private store method that we implemented in core/dialog
sethrubenstein Oct 23, 2025
907bf94
Fix initial mount of tabs and focusing on first tab richtext
sethrubenstein Oct 23, 2025
62eb812
Cleanup
sethrubenstein Oct 23, 2025
0510796
Cleanup
sethrubenstein Oct 23, 2025
76c06bb
Remove tests for now
sethrubenstein Oct 23, 2025
79e09d9
Cleanup
sethrubenstein Oct 23, 2025
684d6bd
Cleanup for coding standards
sethrubenstein Oct 23, 2025
1406cd3
Move away from slotfill, can restore if desired.
sethrubenstein Oct 23, 2025
8b36b2f
Replaced dynamic tab label with sprintf, replaced hard coded formats …
sethrubenstein Oct 24, 2025
3e8e4f5
Make markup render correctly when not focused in tabslist
sethrubenstein Oct 24, 2025
c4973ad
Add StaticLabel component to TabsList for improved tab label rendering
sethrubenstein Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactor tabs, pulling the latest out of prc-block-library/tabs for G…
…utenberg Core.

These changes bring in the following functionality:
- Tab labels can contain richtext now. We strip html tags when adding these to the iAPI.
- Issues with React focus issues in the editor have been resolved.
- Added ContrastChecker support to all color pairings.
- Updated default styles for tabs, links, button
- Better styles support for colors and block gap
- Focus on making tabs as extensible by 3rd party devs as possible, added a browser event to signal to other js applications that tabs are initialized (we use this to add react component driven content to our IAPI driven tabs)
  • Loading branch information
sethrubenstein committed Sep 10, 2025
commit b38e2793fc957411a0637dcfe8c42d4dc7699265
6 changes: 3 additions & 3 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ Content for a tab in a tabbed interface. ([Source](https://github.com/WordPress/
- **Name:** core/tab
- **Category:** design
- **Parent:** core/tabs
- **Supports:** anchor, layout (type, ~~allowEditing~~), typography (fontSize), ~~html~~, ~~reusable~~
- **Supports:** anchor, layout (allowJustification, allowOrientation, allowSizingOnChildren, allowSwitching, allowVerticalAlignment, ~~allowInheriting~~), spacing (blockGap, padding, ~~margin~~), typography (fontSize), ~~html~~, ~~reusable~~
- **Attributes:** label

## Table
Expand Down Expand Up @@ -932,8 +932,8 @@ Display content in a tabbed interface to help users navigate detailed content wi
- **Name:** core/tabs
- **Category:** design
- **Allowed Blocks:** core/tab
- **Supports:** align, color (~~background~~, ~~text~~), interactivity, spacing (blockGap, margin, ~~padding~~), typography (fontFamily, fontSize), ~~html~~
- **Attributes:** activeTabIndex, customTabActiveColor, customTabActiveTextColor, customTabHoverColor, customTabHoverTextColor, customTabInactiveColor, customTabTextColor, orientation, tabActiveColor, tabActiveTextColor, tabHoverColor, tabHoverTextColor, tabInactiveColor, tabTextColor
- **Supports:** align, color (~~background~~, ~~text~~), interactivity, spacing (blockGap, margin, ~~padding~~), typography (fontSize), ~~html~~
- **Attributes:** activeTabIndex, customTabActiveColor, customTabActiveTextColor, customTabHoverColor, customTabHoverTextColor, customTabInactiveColor, customTabTextColor, orientation, tabActiveColor, tabActiveTextColor, tabHoverColor, tabHoverTextColor, tabInactiveColor, tabTextColor, tabsId

## Tag Cloud

Expand Down
48 changes: 48 additions & 0 deletions packages/block-library/src/tab/add-tab-toolbar-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks';
import {
BlockControls,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';

/**
* "Add Tab" button in the block toolbar for the tab block.
* @param {Object} props
* @param {Object} props.attributes The block attributes.
* @param {string} props.tabsClientId The client ID of the parent tabs block.
* @return {JSX.Element} The toolbar control element.
*/
export default function AddTabToolbarControl( { attributes, tabsClientId } ) {
const { insertBlock } = useDispatch( blockEditorStore );

const { className, fontFamily, fontSize } = attributes;

const addTab = () => {
const newTabBlock = createBlock( 'core/tab', {
label: __( 'New Tab' ),
className,
fontFamily,
fontSize,
} );
insertBlock( newTabBlock, undefined, tabsClientId );
};

return (
<BlockControls group="block">
<ToolbarGroup>
<ToolbarButton
className="components-toolbar__control"
label={ __( 'Add Tab' ) }
onClick={ addTab }
showTooltip
text="Add Tab"
/>
</ToolbarGroup>
</BlockControls>
);
}
18 changes: 13 additions & 5 deletions packages/block-library/src/tab/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"description": "Content for a tab in a tabbed interface.",
"version": "1.0.0",
"category": "design",
"textdomain": "default",
"attributes": {
"label": {
"type": "string",
Expand All @@ -18,8 +19,17 @@
"html": false,
"reusable": false,
"layout": {
"type": "constrained",
"allowEditing": false
"allowSwitching": true,
"allowInheriting": false,
"allowVerticalAlignment": true,
"allowJustification": true,
"allowOrientation": true,
"allowSizingOnChildren": true
},
"spacing": {
"blockGap": true,
"padding": true,
"margin": false
},
"typography": {
"fontSize": true,
Expand All @@ -32,10 +42,8 @@
}
},
"providesContext": {
"tab/label": "label",
"tab/slug": "slug"
"tab/label": "label"
},
"textdomain": "tab",
"editorScript": "file:./index.js",
"style": "file:./style-index.css"
}
65 changes: 36 additions & 29 deletions packages/block-library/src/tab/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { decodeEntities } from '@wordpress/html-entities';
/**
* Internal dependencies
*/
import AddTabToolbarControl from './add-tab-toolbar-control';
import slugFromLabel from './slug-from-label';

export default function Controls( {
Expand All @@ -27,34 +28,40 @@ export default function Controls( {
const { updateBlockAttributes } = useDispatch( blockEditorStore );

return (
<InspectorControls>
<PanelBody title={ __( 'Tab Settings' ) }>
<TextControl
label={ __( 'Tab Label' ) }
value={ decodeEntities( label ) }
onChange={ ( value ) => {
setAttributes( {
label: value,
anchor: slugFromLabel( value, blockIndex ),
} );
} }
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
<ToggleControl
label={ __( 'Is Default Tab' ) }
checked={ isDefaultTab }
onChange={ ( value ) => {
updateBlockAttributes( tabsClientId, {
activeTabIndex: value ? blockIndex : 0,
} );
} }
help={ __(
'If toggled, this tab will be selected when the page loads.'
) }
__nextHasNoMarginBottom
/>
</PanelBody>
</InspectorControls>
<>
<AddTabToolbarControl
tabsClientId={ tabsClientId }
attributes={ attributes }
/>
<InspectorControls>
<PanelBody title={ __( 'Tab Settings' ) }>
<TextControl
label={ __( 'Tab Label' ) }
value={ decodeEntities( label ) }
onChange={ ( value ) => {
setAttributes( {
label: value,
anchor: slugFromLabel( value, blockIndex ),
} );
} }
__next40pxDefaultSize
__nextHasNoMarginBottom
/>
<ToggleControl
label={ __( 'Is Default Tab' ) }
checked={ isDefaultTab }
onChange={ ( value ) => {
updateBlockAttributes( tabsClientId, {
activeTabIndex: value ? blockIndex : 0,
} );
} }
help={ __(
'If toggled, this tab will be selected when the page loads.'
) }
__nextHasNoMarginBottom
/>
</PanelBody>
</InspectorControls>
</>
);
}
39 changes: 17 additions & 22 deletions packages/block-library/src/tab/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function Edit( {
clientId,
isSelected,
setAttributes,
__unstableLayoutClassNames: layoutClassNames,
} ) {
const { selectBlock } = useDispatch( blockEditorStore );

Expand Down Expand Up @@ -142,17 +143,6 @@ export default function Edit( {
tabsHasSelectedBlock,
] );

/**
* This hook focuses the label when the block is selected and does not have any inner blocks selected, usually when the block is mounted by the user by clicking on the label.
*/
useEffect( () => {
if ( isSelected && ! hasInnerBlocksSelected ) {
timeoutRef.current = setTimeout( () => {
labelRef.current.focus();
}, 0 );
}
}, [ isSelected, hasInnerBlocksSelected ] );

// Use a custom anchor, if set. Otherwise fall back to the slug generated from the label text.
const tabPanelId = useMemo(
() => anchor || slugFromLabel( label, blockIndex ),
Expand All @@ -176,7 +166,8 @@ export default function Edit( {
tabIndex: isSelectedTab ? 0 : -1,
className: clsx(
tabContentTypographyProps.className,
'tabs__tab-editor-content'
'tabs__tab-editor-content',
layoutClassNames
),
style: {
...tabContentTypographyProps.style,
Expand All @@ -189,14 +180,14 @@ export default function Edit( {

return (
<>
<Controls
attributes={ attributes }
setAttributes={ setAttributes }
tabsClientId={ tabsClientId }
blockIndex={ blockIndex }
isDefaultTab={ isDefaultTab }
/>
<div { ...blockProps }>
<Controls
attributes={ attributes }
setAttributes={ setAttributes }
tabsClientId={ tabsClientId }
blockIndex={ blockIndex }
isDefaultTab={ isDefaultTab }
/>
<TabFill tabsClientId={ tabsClientId }>
<button
aria-controls={ tabPanelId }
Expand All @@ -216,7 +207,8 @@ export default function Edit( {
selectBlock( clientId );
} }
onKeyDown={ ( event ) => {
if ( event.key === 'Enter' ) {
// If shift is also pressed, do not select the block.
if ( event.key === 'Enter' && ! event.shiftKey ) {
event.preventDefault();
selectBlock( clientId );
timeoutRef.current = setTimeout( () => {
Expand All @@ -228,8 +220,11 @@ export default function Edit( {
<RichText
ref={ labelRef }
tagName="span"
allowedFormats={ [] }
withoutInteractiveFormatting
allowedFormats={ [
'core/bold',
'core/italic',
'core/strikethrough',
] }
placeholder={ __( 'Add tab label…' ) }
value={ decodeEntities( label ) }
onChange={ ( value ) =>
Expand Down
19 changes: 19 additions & 0 deletions packages/block-library/src/tab/icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* WordPress dependencies
*/
import { SVG, Path } from '@wordpress/components';

export default (
<SVG
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M5.5498 10.3501V6.3501H9.8498V10.3501H11.3498V6.1001C11.3498 5.40974 10.7902 4.8501 10.0998 4.8501H5.2998C4.60945 4.8501 4.0498 5.40974 4.0498 6.1001V10.3501H5.5498ZM20 12.6001H4V14.1001L20 14.1001V12.6001ZM14 17.1001H4V18.6001H14V17.1001Z"
/>
</SVG>
);
8 changes: 3 additions & 5 deletions packages/block-library/src/tab/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/**
* WordPress dependencies
*/
import { tab as icon } from '@wordpress/icons';

/**
* Internal dependencies
*/
import initBlock from '../utils/init-block';
import edit from './edit';
import save from './save';
import initBlock from '../utils/init-block';
import metadata from './block.json';
import icon from './icon';

const { name } = metadata;

Expand All @@ -21,6 +21,4 @@ export const settings = {
save,
};

export const init = () => {
initBlock( { name, metadata, settings } );
};
export const init = () => initBlock( { name, metadata, settings } );
Loading
Loading