diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 2206e37b3e9534..9e9c452ff5828a 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -10,6 +10,11 @@
- The `LinkedButton` to unlink sides in `BoxControl`, `BorderBoxControl` and `BorderRadiusControl` have changed from a rectangular primary button to an icon-only button, with a sentence case tooltip, and default-size icon for better legibility. The `Button` component has been fixed so when `isSmall` and `icon` props are set, and no text is present, the button shape is square rather than rectangular.
+### New Features
+
+- `MenuItem`: Add suffix prop for injecting non-icon and non-shortcut content to menu items ([#44260](https://github.com/WordPress/gutenberg/pull/44260)).
+- `ToolsPanel`: Add subheadings to ellipsis menu and reset text to default control menu items ([#44260](https://github.com/WordPress/gutenberg/pull/44260)).
+
### Internal
- `NavigationMenu` updated to ignore `react/exhaustive-deps` eslint rule ([#44090](https://github.com/WordPress/gutenberg/pull/44090)).
diff --git a/packages/components/src/menu-item/README.md b/packages/components/src/menu-item/README.md
index c9af4f057a2260..68affcd63b8bfd 100644
--- a/packages/components/src/menu-item/README.md
+++ b/packages/components/src/menu-item/README.md
@@ -79,3 +79,10 @@ If shortcut is a string, it is expecting the display text. If shortcut is an obj
- Default: `'menuitem'`
[Aria Spec](https://www.w3.org/TR/wai-aria-1.1/#aria-checked). If you need to have selectable menu items use menuitemradio for single select, and menuitemcheckbox for multiselect.
+
+### `suffix`
+
+- Type: `WPElement`
+- Required: No
+
+Allows for markup other than icons or shortcuts to be added to the menu item.
diff --git a/packages/components/src/menu-item/index.js b/packages/components/src/menu-item/index.js
index 7440b3b38ef351..907d54fb680541 100644
--- a/packages/components/src/menu-item/index.js
+++ b/packages/components/src/menu-item/index.js
@@ -26,6 +26,7 @@ export function MenuItem( props, ref ) {
shortcut,
isSelected,
role = 'menuitem',
+ suffix,
...buttonProps
} = props;
@@ -63,11 +64,16 @@ export function MenuItem( props, ref ) {
{ ...buttonProps }
>
{ children }
-
- { icon && iconPosition === 'right' && }
+ { ! suffix && (
+
+ ) }
+ { ! suffix && icon && iconPosition === 'right' && (
+
+ ) }
+ { suffix }
);
}
diff --git a/packages/components/src/menu-item/style.scss b/packages/components/src/menu-item/style.scss
index 18c2666025a5db..d0d11643d7c96e 100644
--- a/packages/components/src/menu-item/style.scss
+++ b/packages/components/src/menu-item/style.scss
@@ -8,6 +8,7 @@
// Ensure unchecked items have clearance for consistency
// with checked items containing an icon or shortcut.
padding-right: $grid-unit-60;
+ box-sizing: initial;
}
}
diff --git a/packages/components/src/menu-item/test/index.js b/packages/components/src/menu-item/test/index.js
index aa815dd7e4bb7e..fd074759ee3828 100644
--- a/packages/components/src/menu-item/test/index.js
+++ b/packages/components/src/menu-item/test/index.js
@@ -107,4 +107,40 @@ describe( 'MenuItem', () => {
expect( checkboxMenuItem ).toBeChecked();
expect( checkboxMenuItem ).toHaveAttribute( 'aria-checked', 'true' );
} );
+
+ it( 'should not render shortcut or right icon if suffix provided', () => {
+ render(
+
+ );
+
+ expect( screen.getByText( 'Suffix' ) ).toBeInTheDocument();
+ expect( screen.queryByText( 'Shortcut' ) ).not.toBeInTheDocument();
+ expect( screen.queryByText( 'Icon' ) ).not.toBeInTheDocument();
+ } );
+
+ it( 'should render left icon despite suffix being provided', () => {
+ render(
+
+ );
+
+ expect( screen.getByText( 'Icon' ) ).toBeInTheDocument();
+ expect( screen.getByText( 'Suffix' ) ).toBeInTheDocument();
+ expect( screen.queryByText( 'Shortcut' ) ).not.toBeInTheDocument();
+ } );
} );
diff --git a/packages/components/src/tools-panel/stories/index.js b/packages/components/src/tools-panel/stories/index.js
index 803a01eb341d35..b9d317a0fb71f2 100644
--- a/packages/components/src/tools-panel/stories/index.js
+++ b/packages/components/src/tools-panel/stories/index.js
@@ -29,11 +29,13 @@ export const _default = () => {
const [ height, setHeight ] = useState();
const [ minHeight, setMinHeight ] = useState();
const [ width, setWidth ] = useState();
+ const [ scale, setScale ] = useState();
const resetAll = () => {
setHeight( undefined );
setWidth( undefined );
setMinHeight( undefined );
+ setScale( undefined );
};
return (
@@ -79,6 +81,31 @@ export const _default = () => {
onChange={ ( next ) => setMinHeight( next ) }
/>
+ !! scale }
+ label="Scale"
+ onDeselect={ () => setScale( undefined ) }
+ >
+ setScale( next ) }
+ isBlock
+ >
+
+
+
+
+
diff --git a/packages/components/src/tools-panel/styles.ts b/packages/components/src/tools-panel/styles.ts
index 7fe2fc0a0a1a2c..f76bf47ec18c5b 100644
--- a/packages/components/src/tools-panel/styles.ts
+++ b/packages/components/src/tools-panel/styles.ts
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
+import styled from '@emotion/styled';
import { css } from '@emotion/react';
/**
@@ -12,7 +13,7 @@ import {
Wrapper as BaseControlWrapper,
} from '../base-control/styles/base-control-styles';
import { LabelWrapper } from '../input-control/styles/input-control-styles';
-import { COLORS, CONFIG } from '../utils';
+import { COLORS, CONFIG, rtl } from '../utils';
import { space } from '../ui/utils/space';
const toolsPanelGrid = {
@@ -145,3 +146,29 @@ export const ToolsPanelItemPlaceholder = css`
export const DropdownMenu = css`
min-width: 200px;
`;
+
+export const ResetLabel = styled.span`
+ color: var( --wp-admin-theme-color-darker-10 );
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 1.4;
+ ${ rtl( { marginLeft: space( 3 ) } ) }
+ text-transform: uppercase;
+`;
+
+export const DefaultControlsItem = css`
+ color: ${ COLORS.gray[ 900 ] };
+
+ &&[aria-disabled='true'] {
+ color: ${ COLORS.gray[ 700 ] };
+ opacity: 1;
+
+ &:hover {
+ color: ${ COLORS.gray[ 700 ] };
+ }
+
+ ${ ResetLabel } {
+ opacity: 0.3;
+ }
+ }
+`;
diff --git a/packages/components/src/tools-panel/tools-panel-header/component.tsx b/packages/components/src/tools-panel/tools-panel-header/component.tsx
index 62a40a966dcb7d..57416d4d0c311d 100644
--- a/packages/components/src/tools-panel/tools-panel-header/component.tsx
+++ b/packages/components/src/tools-panel/tools-panel-header/component.tsx
@@ -7,7 +7,7 @@ import type { ForwardedRef } from 'react';
* WordPress dependencies
*/
import { speak } from '@wordpress/a11y';
-import { check, reset, moreVertical, plus } from '@wordpress/icons';
+import { check, moreVertical, plus } from '@wordpress/icons';
import { __, _x, sprintf } from '@wordpress/i18n';
/**
@@ -20,12 +20,14 @@ import { HStack } from '../../h-stack';
import { Heading } from '../../heading';
import { useToolsPanelHeader } from './hook';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
+import { ResetLabel } from '../styles';
import type {
ToolsPanelControlsGroupProps,
ToolsPanelHeaderProps,
} from '../types';
const DefaultControlsGroup = ( {
+ itemClassName,
items,
toggleItem,
}: ToolsPanelControlsGroupProps ) => {
@@ -33,15 +35,17 @@ const DefaultControlsGroup = ( {
return null;
}
+ const resetSuffix = { __( 'Reset' ) };
+
return (
-
+
{ items.map( ( [ label, hasValue ] ) => {
if ( hasValue ) {
return (
@@ -67,8 +72,8 @@ const DefaultControlsGroup = ( {
return (