diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 6b24148d024488..8bba4668114c1e 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -28,6 +28,7 @@ - `UnitControl`: Remove outer wrapper to normalize className placement ([#41860](https://github.com/WordPress/gutenberg/pull/41860)). - `ColorPalette`: Fix transparent checkered background pattern ([#45295](https://github.com/WordPress/gutenberg/pull/45295)). - `ToggleGroupControl`: Add `isDeselectable` prop to allow deselecting the selected option ([#45123](https://github.com/WordPress/gutenberg/pull/45123)). +- `FontSizePicker`: Improve hint text shown next to 'Font size' label ([#44966](https://github.com/WordPress/gutenberg/pull/44966)). ### Bug Fix diff --git a/packages/components/src/custom-select-control/index.js b/packages/components/src/custom-select-control/index.js index 4c8682f0c7cb3e..337c86e8422d45 100644 --- a/packages/components/src/custom-select-control/index.js +++ b/packages/components/src/custom-select-control/index.js @@ -79,6 +79,7 @@ export default function CustomSelectControl( props ) { value: _selectedItem, onMouseOver, onMouseOut, + __experimentalShowSelectedHint = false, } = props; const { @@ -194,6 +195,12 @@ export default function CustomSelectControl( props ) { } ) } > { itemToString( selectedItem ) } + { __experimentalShowSelectedHint && + selectedItem.__experimentalHint && ( + + { selectedItem.__experimentalHint } + + ) } { /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ } diff --git a/packages/components/src/custom-select-control/stories/index.js b/packages/components/src/custom-select-control/stories/index.js index 585c3809237c0d..4891bcf2109378 100644 --- a/packages/components/src/custom-select-control/stories/index.js +++ b/packages/components/src/custom-select-control/stories/index.js @@ -8,6 +8,7 @@ export default { component: CustomSelectControl, argTypes: { __next36pxDefaultSize: { control: { type: 'boolean' } }, + __experimentalShowSelectedHint: { control: { type: 'boolean' } }, size: { control: { type: 'radio', diff --git a/packages/components/src/custom-select-control/style.scss b/packages/components/src/custom-select-control/style.scss index e6ca7945abfe1e..b231218ef4b611 100644 --- a/packages/components/src/custom-select-control/style.scss +++ b/packages/components/src/custom-select-control/style.scss @@ -9,6 +9,11 @@ outline: 0; // focus ring is handled elsewhere } +.components-custom-select-control__hint { + color: $gray-600; + margin-left: 10px; +} + .components-custom-select-control__menu { // Hide when collapsed. &[aria-hidden="true"] { @@ -50,7 +55,7 @@ background: $gray-300; } .components-custom-select-control__item-hint { - color: $gray-700; + color: $gray-600; text-align: right; padding-right: $grid-unit-05; } diff --git a/packages/components/src/custom-select-control/test/index.js b/packages/components/src/custom-select-control/test/index.js index e5f7ef728ca0d8..413760b63ebed7 100644 --- a/packages/components/src/custom-select-control/test/index.js +++ b/packages/components/src/custom-select-control/test/index.js @@ -46,4 +46,43 @@ describe( 'CustomSelectControl', () => { expect( onKeyDown ).toHaveBeenCalledTimes( 0 ); } ); + + it( 'does not show selected hint by default', () => { + render( + + ); + expect( + screen.getByRole( 'button', { name: 'Custom select' } ) + ).not.toHaveTextContent( 'Hint' ); + } ); + + it( 'shows selected hint when __experimentalShowSelectedHint is set', () => { + render( + + ); + expect( + screen.getByRole( 'button', { name: 'Custom select' } ) + ).toHaveTextContent( 'Hint' ); + } ); } ); diff --git a/packages/components/src/font-size-picker/constants.ts b/packages/components/src/font-size-picker/constants.ts new file mode 100644 index 00000000000000..1e321aa90b0dfb --- /dev/null +++ b/packages/components/src/font-size-picker/constants.ts @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * List of T-shirt abbreviations. + * + * When there are 5 font sizes or fewer, we assume that the font sizes are + * ordered by size and show T-shirt labels. + */ +export const T_SHIRT_ABBREVIATIONS = [ + /* translators: S stands for 'small' and is a size label. */ + __( 'S' ), + /* translators: M stands for 'medium' and is a size label. */ + __( 'M' ), + /* translators: L stands for 'large' and is a size label. */ + __( 'L' ), + /* translators: XL stands for 'extra large' and is a size label. */ + __( 'XL' ), + /* translators: XXL stands for 'extra extra large' and is a size label. */ + __( 'XXL' ), +]; + +/** + * List of T-shirt names. + * + * When there are 5 font sizes or fewer, we assume that the font sizes are + * ordered by size and show T-shirt labels. + */ +export const T_SHIRT_NAMES = [ + __( 'Small' ), + __( 'Medium' ), + __( 'Large' ), + __( 'Extra Large' ), + __( 'Extra Extra Large' ), +]; diff --git a/packages/components/src/font-size-picker/font-size-picker-select.tsx b/packages/components/src/font-size-picker/font-size-picker-select.tsx index 192a0f5632f715..d3fc2ffe4a61fd 100644 --- a/packages/components/src/font-size-picker/font-size-picker-select.tsx +++ b/packages/components/src/font-size-picker/font-size-picker-select.tsx @@ -12,6 +12,7 @@ import type { FontSizePickerSelectProps, FontSizePickerSelectOption, } from './types'; +import { getCommonSizeUnit, isSimpleCssValue } from './utils'; const DEFAULT_OPTION: FontSizePickerSelectOption = { key: 'default', @@ -34,18 +35,27 @@ const FontSizePickerSelect = ( props: FontSizePickerSelectProps ) => { onSelectCustom, } = props; + const areAllSizesSameUnit = !! getCommonSizeUnit( fontSizes ); + const options: FontSizePickerSelectOption[] = [ DEFAULT_OPTION, ...fontSizes.map( ( fontSize ) => { - const [ quantity ] = parseQuantityAndUnitFromRawValue( - fontSize.size - ); + let hint; + if ( areAllSizesSameUnit ) { + const [ quantity ] = parseQuantityAndUnitFromRawValue( + fontSize.size + ); + if ( quantity !== undefined ) { + hint = String( quantity ); + } + } else if ( isSimpleCssValue( fontSize.size ) ) { + hint = String( fontSize.size ); + } return { key: fontSize.slug, name: fontSize.name || fontSize.slug, value: fontSize.size, - __experimentalHint: - quantity !== undefined ? String( quantity ) : undefined, + __experimentalHint: hint, }; } ), ...( disableCustomFontSizes ? [] : [ CUSTOM_OPTION ] ), @@ -68,6 +78,7 @@ const FontSizePickerSelect = ( props: FontSizePickerSelectProps ) => { ) } options={ options } value={ selectedOption } + __experimentalShowSelectedHint onChange={ ( { selectedItem, }: { diff --git a/packages/components/src/font-size-picker/font-size-picker-toggle-group.tsx b/packages/components/src/font-size-picker/font-size-picker-toggle-group.tsx index a7aed96e76ae6f..697d9e11b67e47 100644 --- a/packages/components/src/font-size-picker/font-size-picker-toggle-group.tsx +++ b/packages/components/src/font-size-picker/font-size-picker-toggle-group.tsx @@ -10,26 +10,9 @@ import { ToggleGroupControl, ToggleGroupControlOption, } from '../toggle-group-control'; +import { T_SHIRT_ABBREVIATIONS, T_SHIRT_NAMES } from './constants'; import type { FontSizePickerToggleGroupProps } from './types'; -/** - * In case we have at most five font sizes, show a `T-shirt size` alias as a - * label of the font size. The label assumes that the font sizes are ordered - * accordingly - from smallest to largest. - */ -const FONT_SIZES_ALIASES = [ - /* translators: S stands for 'small' and is a size label. */ - __( 'S' ), - /* translators: M stands for 'medium' and is a size label. */ - __( 'M' ), - /* translators: L stands for 'large' and is a size label. */ - __( 'L' ), - /* translators: XL stands for 'extra large' and is a size label. */ - __( 'XL' ), - /* translators: XXL stands for 'extra extra large' and is a size label. */ - __( 'XXL' ), -]; - const FontSizePickerToggleGroup = ( props: FontSizePickerToggleGroupProps ) => { const { fontSizes, value, __nextHasNoMarginBottom, size, onChange } = props; return ( @@ -46,8 +29,8 @@ const FontSizePickerToggleGroup = ( props: FontSizePickerToggleGroupProps ) => { ) ) } diff --git a/packages/components/src/font-size-picker/index.tsx b/packages/components/src/font-size-picker/index.tsx index 9c4c1cac988850..7aa5d31b1e9e94 100644 --- a/packages/components/src/font-size-picker/index.tsx +++ b/packages/components/src/font-size-picker/index.tsx @@ -23,7 +23,7 @@ import { useCustomUnits, } from '../unit-control'; import { VisuallyHidden } from '../visually-hidden'; -import { isSimpleCssValue } from './utils'; +import { getCommonSizeUnit } from './utils'; import { HStack } from '../h-stack'; import type { FontSizePickerProps } from './types'; import { @@ -36,6 +36,7 @@ import { import { Spacer } from '../spacer'; import FontSizePickerSelect from './font-size-picker-select'; import FontSizePickerToggleGroup from './font-size-picker-toggle-group'; +import { T_SHIRT_NAMES } from './constants'; const UnforwardedFontSizePicker = ( props: FontSizePickerProps, @@ -78,53 +79,29 @@ const UnforwardedFontSizePicker = ( const headerHint = useMemo( () => { if ( showCustomValueControl ) { - return `(${ __( 'Custom' ) })`; + return __( 'Custom' ); } - // If we have a custom value that is not available in the font sizes, - // show it as a hint as long as it's a simple CSS value. - if ( isCustomValue ) { - return ( - value !== undefined && - isSimpleCssValue( value ) && - `(${ value })` - ); + if ( ! shouldUseSelectControl ) { + if ( selectedFontSize ) { + return ( + selectedFontSize.name || + T_SHIRT_NAMES[ fontSizes.indexOf( selectedFontSize ) ] + ); + } + return ''; } - if ( shouldUseSelectControl ) { - return ( - selectedFontSize?.size !== undefined && - isSimpleCssValue( selectedFontSize?.size ) && - `(${ selectedFontSize?.size })` - ); + const commonUnit = getCommonSizeUnit( fontSizes ); + if ( commonUnit ) { + return `(${ commonUnit })`; } - if ( ! selectedFontSize ) { - return __( 'Default' ); - } - - // Calculate the `hint` for toggle group control. - let hint = selectedFontSize.name || selectedFontSize.slug; - const fontSizesContainComplexValues = fontSizes.some( - ( fontSize ) => ! isSimpleCssValue( fontSize.size ) - ); - if ( - ! fontSizesContainComplexValues && - typeof selectedFontSize.size === 'string' - ) { - const [ , unit ] = parseQuantityAndUnitFromRawValue( - selectedFontSize.size, - units - ); - hint += `(${ unit })`; - } - return hint; + return ''; }, [ showCustomValueControl, - isCustomValue, - selectedFontSize, - value, shouldUseSelectControl, + selectedFontSize, fontSizes, ] ); diff --git a/packages/components/src/font-size-picker/test/index.tsx b/packages/components/src/font-size-picker/test/index.tsx index cdcce2db1cc715..74e0eef2f94dcd 100644 --- a/packages/components/src/font-size-picker/test/index.tsx +++ b/packages/components/src/font-size-picker/test/index.tsx @@ -67,7 +67,119 @@ describe( 'FontSizePicker', () => { } ); - describe( 'with > 5 font sizes', () => { + describe( 'with > 5 homogeneous font sizes', () => { + const fontSizes = [ + { + slug: 'tiny', + name: 'Tiny', + size: '8px', + }, + { + slug: 'small', + name: 'Small', + size: '12px', + }, + { + slug: 'medium', + name: 'Medium', + size: '16px', + }, + { + slug: 'large', + name: 'Large', + size: '20px', + }, + { + slug: 'x-large', + name: 'Extra Large', + size: '30px', + }, + { + slug: 'xx-large', + // no name + size: '40px', + }, + ]; + + it( 'displays a select control', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); + render( + + ); + await user.click( + screen.getByRole( 'button', { name: 'Font size' } ) + ); + const options = screen.getAllByRole( 'option' ); + expect( options ).toHaveLength( 8 ); + expect( options[ 0 ] ).toHaveAccessibleName( 'Default' ); + expect( options[ 1 ] ).toHaveAccessibleName( 'Tiny 8' ); + expect( options[ 2 ] ).toHaveAccessibleName( 'Small 12' ); + expect( options[ 3 ] ).toHaveAccessibleName( 'Medium 16' ); + expect( options[ 4 ] ).toHaveAccessibleName( 'Large 20' ); + expect( options[ 5 ] ).toHaveAccessibleName( 'Extra Large 30' ); + expect( options[ 6 ] ).toHaveAccessibleName( 'xx-large 40' ); + expect( options[ 7 ] ).toHaveAccessibleName( 'Custom' ); + } ); + + test.each( [ + { value: undefined, expectedLabel: 'Size (px)' }, + { value: '8px', expectedLabel: 'Size (px)' }, + { value: '3px', expectedLabel: 'Size Custom' }, + ] )( + 'displays $expectedLabel as label when value is $value', + ( { value, expectedLabel } ) => { + render( + + ); + expect( + screen.getByLabelText( expectedLabel ) + ).toBeInTheDocument(); + } + ); + + test.each( [ + { option: 'Default', value: '8px', expectedValue: undefined }, + { option: 'Tiny 8', value: undefined, expectedValue: '8px' }, + ] )( + 'calls onChange( $expectedValue ) when $option is selected', + async ( { option, value, expectedValue } ) => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); + const onChange = jest.fn(); + render( + + ); + await user.click( + screen.getByRole( 'button', { name: 'Font size' } ) + ); + await user.click( + screen.getByRole( 'option', { name: option } ) + ); + expect( onChange ).toHaveBeenCalledTimes( 1 ); + expect( onChange ).toHaveBeenCalledWith( expectedValue ); + } + ); + + commonSelectTests( fontSizes ); + commonTests( fontSizes ); + } ); + + describe( 'with > 5 heterogeneous font sizes', () => { const fontSizes = [ { slug: 'tiny', @@ -117,12 +229,12 @@ describe( 'FontSizePicker', () => { const options = screen.getAllByRole( 'option' ); expect( options ).toHaveLength( 8 ); expect( options[ 0 ] ).toHaveAccessibleName( 'Default' ); - expect( options[ 1 ] ).toHaveAccessibleName( 'Tiny 8' ); - expect( options[ 2 ] ).toHaveAccessibleName( 'Small 1' ); - expect( options[ 3 ] ).toHaveAccessibleName( 'Medium 2' ); + expect( options[ 1 ] ).toHaveAccessibleName( 'Tiny 8px' ); + expect( options[ 2 ] ).toHaveAccessibleName( 'Small 1em' ); + expect( options[ 3 ] ).toHaveAccessibleName( 'Medium 2rem' ); expect( options[ 4 ] ).toHaveAccessibleName( 'Large' ); - expect( options[ 5 ] ).toHaveAccessibleName( 'Extra Large 30' ); - expect( options[ 6 ] ).toHaveAccessibleName( 'xx-large 40' ); + expect( options[ 5 ] ).toHaveAccessibleName( 'Extra Large 30px' ); + expect( options[ 6 ] ).toHaveAccessibleName( 'xx-large 40px' ); expect( options[ 7 ] ).toHaveAccessibleName( 'Custom' ); } ); @@ -148,11 +260,11 @@ describe( 'FontSizePicker', () => { test.each( [ { value: undefined, expectedLabel: 'Size' }, - { value: '8px', expectedLabel: 'Size (8px)' }, - { value: '1em', expectedLabel: 'Size (1em)' }, - { value: '2rem', expectedLabel: 'Size (2rem)' }, + { value: '8px', expectedLabel: 'Size' }, + { value: '1em', expectedLabel: 'Size' }, + { value: '2rem', expectedLabel: 'Size' }, { value: 'clamp(1.75rem, 3vw, 2.25rem)', expectedLabel: 'Size' }, - { value: '3px', expectedLabel: 'Size (Custom)' }, + { value: '3px', expectedLabel: 'Size Custom' }, ] )( 'displays $expectedLabel as label when value is $value', ( { value, expectedLabel } ) => { @@ -171,9 +283,9 @@ describe( 'FontSizePicker', () => { test.each( [ { option: 'Default', value: '8px', expectedValue: undefined }, - { option: 'Tiny 8', value: undefined, expectedValue: '8px' }, - { option: 'Small 1', value: '8px', expectedValue: '1em' }, - { option: 'Medium 2', value: '8px', expectedValue: '2rem' }, + { option: 'Tiny 8px', value: undefined, expectedValue: '8px' }, + { option: 'Small 1em', value: '8px', expectedValue: '1em' }, + { option: 'Medium 2rem', value: '8px', expectedValue: '2rem' }, { option: 'Large', value: '8px', @@ -205,28 +317,7 @@ describe( 'FontSizePicker', () => { } ); - it( 'shows custom input when Custom is selected', async () => { - const user = userEvent.setup( { - advanceTimers: jest.advanceTimersByTime, - } ); - const onChange = jest.fn(); - render( - - ); - await user.click( - screen.getByRole( 'button', { name: 'Font size' } ) - ); - await user.click( - screen.getByRole( 'option', { name: 'Custom' } ) - ); - expect( screen.getByLabelText( 'Custom' ) ).toBeInTheDocument(); - expect( onChange ).not.toHaveBeenCalled(); - } ); - + commonSelectTests( fontSizes ); commonTests( fontSizes ); } ); @@ -269,7 +360,7 @@ describe( 'FontSizePicker', () => { const options = screen.getAllByRole( 'radio' ); expect( options ).toHaveLength( 5 ); expect( options[ 0 ] ).toHaveTextContent( 'S' ); - expect( options[ 0 ] ).toHaveAccessibleName( 'S' ); + expect( options[ 0 ] ).toHaveAccessibleName( 'Small' ); expect( options[ 1 ] ).toHaveTextContent( 'M' ); expect( options[ 1 ] ).toHaveAccessibleName( 'Medium' ); expect( options[ 2 ] ).toHaveTextContent( 'L' ); @@ -281,9 +372,9 @@ describe( 'FontSizePicker', () => { } ); test.each( [ - { value: undefined, expectedLabel: 'Size Default' }, - { value: '12px', expectedLabel: 'Size small(px)' }, - { value: '40px', expectedLabel: 'Size Gigantosaurus(px)' }, + { value: undefined, expectedLabel: 'Size' }, + { value: '12px', expectedLabel: 'Size Small' }, + { value: '40px', expectedLabel: 'Size Gigantosaurus' }, ] )( 'displays $expectedLabel as label when value is $value', ( { value, expectedLabel } ) => { @@ -365,7 +456,7 @@ describe( 'FontSizePicker', () => { } ); test.each( [ - { value: undefined, expectedLabel: 'Size Default' }, + { value: undefined, expectedLabel: 'Size' }, { value: '12px', expectedLabel: 'Size Small' }, { value: '1em', expectedLabel: 'Size Medium' }, { value: '2rem', expectedLabel: 'Size Large' }, @@ -455,6 +546,30 @@ describe( 'FontSizePicker', () => { ); } + function commonSelectTests( fontSizes: FontSize[] ) { + it( 'shows custom input when Custom is selected', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); + const onChange = jest.fn(); + render( + + ); + await user.click( + screen.getByRole( 'button', { name: 'Font size' } ) + ); + await user.click( + screen.getByRole( 'option', { name: 'Custom' } ) + ); + expect( screen.getByLabelText( 'Custom' ) ).toBeInTheDocument(); + expect( onChange ).not.toHaveBeenCalled(); + } ); + } + function commonTests( fontSizes: FontSize[] ) { it( 'shows custom input when value is unknown', () => { render( diff --git a/packages/components/src/font-size-picker/test/utils.ts b/packages/components/src/font-size-picker/test/utils.ts index 91205062d57021..584820ae27a71a 100644 --- a/packages/components/src/font-size-picker/test/utils.ts +++ b/packages/components/src/font-size-picker/test/utils.ts @@ -1,38 +1,69 @@ /** * Internal dependencies */ -import { isSimpleCssValue } from '../utils'; - -const simpleCSSCases: [ number | string, boolean ][] = [ - // Test integers and non-integers. - [ 1, true ], - [ 1.25, true ], - [ '123', true ], - [ '1.5', true ], - [ '0.75', true ], - // CSS unit tests. - [ '20px', true ], - [ '0.8em', true ], - [ '2rem', true ], - [ '1.4vw', true ], - [ '0.4vh', true ], - // Invalid negative values, - [ '-5px', false ], - // Complex CSS values that should fail. - [ 'abs(-10px)', false ], - [ 'calc(10px + 1)', false ], - [ 'clamp(2.5rem, 4vw, 3rem)', false ], - [ 'max(4.5em, 3vh)', false ], - [ 'min(10px, 1rem)', false ], - [ 'minmax(30px, auto)', false ], - [ 'var(--wp--font-size)', false ], -]; +import { isSimpleCssValue, getCommonSizeUnit } from '../utils'; describe( 'isSimpleCssValue', () => { - test.each( simpleCSSCases )( - 'given %p as argument, returns %p', - ( cssValue, result ) => { - expect( isSimpleCssValue( cssValue ) ).toBe( result ); - } - ); + test.each( [ + // Test integers and non-integers. + [ 1, true ], + [ 1.25, true ], + [ '123', true ], + [ '1.5', true ], + [ '0.75', true ], + // CSS unit tests. + [ '20px', true ], + [ '0.8em', true ], + [ '2rem', true ], + [ '1.4vw', true ], + [ '0.4vh', true ], + // Invalid negative values, + [ '-5px', false ], + // Complex CSS values that should fail. + [ 'abs(-10px)', false ], + [ 'calc(10px + 1)', false ], + [ 'clamp(2.5rem, 4vw, 3rem)', false ], + [ 'max(4.5em, 3vh)', false ], + [ 'min(10px, 1rem)', false ], + [ 'minmax(30px, auto)', false ], + [ 'var(--wp--font-size)', false ], + ] )( 'given %p as argument, returns %p', ( cssValue, result ) => { + expect( isSimpleCssValue( cssValue ) ).toBe( result ); + } ); +} ); + +describe( 'getCommonSizeUnit', () => { + it( 'returns null when fontSizes is empty', () => { + expect( getCommonSizeUnit( [] ) ).toBe( null ); + } ); + + it( 'returns px when all sizes are px', () => { + expect( + getCommonSizeUnit( [ + { slug: 'small', size: '10px' }, + { slug: 'medium', size: '20px' }, + { slug: 'large', size: '30px' }, + ] ) + ).toBe( 'px' ); + } ); + + it( 'returns em when all sizes are em', () => { + expect( + getCommonSizeUnit( [ + { slug: 'small', size: '1em' }, + { slug: 'medium', size: '2em' }, + { slug: 'large', size: '3em' }, + ] ) + ).toBe( 'em' ); + } ); + + it( 'returns null when sizes are heterogeneous', () => { + expect( + getCommonSizeUnit( [ + { slug: 'small', size: '10px' }, + { slug: 'medium', size: '2em' }, + { slug: 'large', size: '3rem' }, + ] ) + ).toBe( null ); + } ); } ); diff --git a/packages/components/src/font-size-picker/utils.ts b/packages/components/src/font-size-picker/utils.ts index 3f0c6019da8067..d7e426230329e0 100644 --- a/packages/components/src/font-size-picker/utils.ts +++ b/packages/components/src/font-size-picker/utils.ts @@ -6,7 +6,8 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import type { FontSizePickerProps } from './types'; +import type { FontSizePickerProps, FontSize } from './types'; +import { parseQuantityAndUnitFromRawValue } from '../unit-control'; /** * Some themes use css vars for their font sizes, so until we @@ -21,3 +22,25 @@ export function isSimpleCssValue( const sizeRegex = /^[\d\.]+(px|em|rem|vw|vh|%)?$/i; return sizeRegex.test( String( value ) ); } + +/** + * If all of the given font sizes have the same unit (e.g. 'px'), return that + * unit. Otherwise return null. + * + * @param fontSizes List of font sizes. + * @return The common unit, or null. + */ +export function getCommonSizeUnit( fontSizes: FontSize[] ) { + const [ firstFontSize, ...otherFontSizes ] = fontSizes; + if ( ! firstFontSize ) { + return null; + } + const [ , firstUnit ] = parseQuantityAndUnitFromRawValue( + firstFontSize.size + ); + const areAllSizesSameUnit = otherFontSizes.every( ( fontSize ) => { + const [ , unit ] = parseQuantityAndUnitFromRawValue( fontSize.size ); + return unit === firstUnit; + } ); + return areAllSizesSameUnit ? firstUnit : null; +} diff --git a/test/e2e/specs/site-editor/style-variations.spec.js b/test/e2e/specs/site-editor/style-variations.spec.js index ba52eb25d9109a..56de0ac0de2194 100644 --- a/test/e2e/specs/site-editor/style-variations.spec.js +++ b/test/e2e/specs/site-editor/style-variations.spec.js @@ -100,7 +100,7 @@ test.describe( 'Global styles variations', () => { await expect( page.locator( 'css=.components-font-size-picker__header__hint' ) - ).toHaveText( 'Medium(px)' ); + ).toHaveText( 'Medium' ); } ); test( 'should apply custom colors and font sizes in a variation', async ( { @@ -141,7 +141,7 @@ test.describe( 'Global styles variations', () => { // we could provide accessible attributes to the source code in packages/components/src/font-size-picker/index.js. await expect( page.locator( 'css=.components-font-size-picker__header__hint' ) - ).toHaveText( '(Custom)' ); + ).toHaveText( 'Custom' ); await expect( page.locator( 'role=spinbutton[name="Custom"i]' )