diff --git a/packages/block-editor/src/components/font-sizes/README.md b/packages/block-editor/src/components/font-sizes/README.md index 69354b90f2a351..2a54ec7bccbe0f 100644 --- a/packages/block-editor/src/components/font-sizes/README.md +++ b/packages/block-editor/src/components/font-sizes/README.md @@ -2,7 +2,7 @@ FontSizePicker is a React component that renders a UI that allows users to select a font size. The component renders a user interface that allows the user to select predefined (common) font sizes and contains an option that allows users to select custom font sizes (by choosing the value) if that functionality is enabled. -There is an equivalent component exposed under @wordpress/components. The difference between this component and the @wordpress components one is that this component does not require the `fontSizes` and `disableCustomFontSizes` properties. The editor settings are used to compute the value of these props. +There is an equivalent component exposed under @wordpress/components. The difference between this component and the @wordpress components one is that this component does not require the `fontSizes` and `pickerMode` properties. The editor settings are used to compute the value of these props. ## Usage diff --git a/packages/block-editor/src/components/font-sizes/font-size-picker.js b/packages/block-editor/src/components/font-sizes/font-size-picker.js index 959b2c23806ded..91d97f70d72db4 100644 --- a/packages/block-editor/src/components/font-sizes/font-size-picker.js +++ b/packages/block-editor/src/components/font-sizes/font-size-picker.js @@ -18,7 +18,7 @@ function FontSizePicker( props ) { ); } diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 12ceadeb758df2..b60481a7d5594f 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -209,7 +209,9 @@ export default function TypographyPanel( { // Font Size const hasFontSizeEnabled = useHasFontSizeControl( settings ); - const disableCustomFontSizes = ! settings?.typography?.customFontSize; + const fontSizePickerMode = ! settings?.typography?.customFontSize + ? 'predefined' + : 'both'; const mergedFontSizes = getMergedFontSizes( settings ); const fontSize = decodeValue( inheritedValue?.typography?.fontSize ); @@ -443,7 +445,7 @@ export default function TypographyPanel( { value={ fontSize } onChange={ setFontSize } fontSizes={ mergedFontSizes } - disableCustomFontSizes={ disableCustomFontSizes } + pickerMode={ fontSizePickerMode } withReset={ false } withSlider size="__unstable-large" diff --git a/packages/components/src/font-size-picker/README.md b/packages/components/src/font-size-picker/README.md index 2a342d3de9114d..3206cc7a7deb3e 100644 --- a/packages/components/src/font-size-picker/README.md +++ b/packages/components/src/font-size-picker/README.md @@ -48,13 +48,6 @@ const MyFontSizePicker = () => { The component accepts the following props: -### `disableCustomFontSizes`: `boolean` - -If `true`, it will not be possible to choose a custom fontSize. The user will be forced to pick one of the pre-defined sizes passed in fontSizes. - -- Required: no -- Default: `false` - ### `fallbackFontSize`: `number` If no value exists, this prop defines the starting position for the font size picker slider. Only relevant if `withSlider` is `true`. @@ -80,6 +73,13 @@ If onChange is called without any parameter, it should reset the value, attendin - Required: Yes +### `pickerMode`: `'predefined' | 'custom' | 'all` + +When set to `predefined`, the user will be only able to pick a font size from the predefined list passed via the `fontSizes` prop. When set to `custom`, the user will be only able to choose a custom font size. When set to `all`, the user will be able to access all UIs through a toggle. + +- Required: No +- Default: `'all'` + ### `size`: `'default' | '__unstable-large'` Size of the control. @@ -102,14 +102,23 @@ The current font size value. ### `withReset`: `boolean` -If `true`, a reset button will be displayed alongside the input field when a custom font size is active. Has no effect when `disableCustomFontSizes` is `true`. +If `true`, a reset button will be displayed alongside the input field when a custom font size is active. Has no effect when `pickerMode` is `'predefined'` or `withSlider` is `true`. - Required: no - Default: `true` ### `withSlider`: `boolean` -If `true`, a slider will be displayed alongside the input field when a custom font size is active. Has no effect when `disableCustomFontSizes` is `true`. +If `true`, a slider will be displayed alongside the input field when a custom font size is active. Has no effect when `pickerMode` is `predefined`. + +- Required: no +- Default: `false` + +### `disableCustomFontSizes`: `boolean` + +_Note: this prop is deprecated. Please use the `pickerMode` prop instead._ + +If `true`, it will not be possible to choose a custom font size. The user will be forced to pick one of the pre-defined sizes passed via the `fontSizes` prop. - Required: no - Default: `false` 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 c5dca6bdb37a04..4799c3c95ffacb 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 @@ -30,7 +30,7 @@ const FontSizePickerSelect = ( props: FontSizePickerSelectProps ) => { __next40pxDefaultSize, fontSizes, value, - disableCustomFontSizes, + pickerMode, size, onChange, onSelectCustom, @@ -59,7 +59,7 @@ const FontSizePickerSelect = ( props: FontSizePickerSelectProps ) => { __experimentalHint: hint, }; } ), - ...( disableCustomFontSizes ? [] : [ CUSTOM_OPTION ] ), + ...( pickerMode === 'all' ? [ CUSTOM_OPTION ] : [] ), ]; const selectedOption = value diff --git a/packages/components/src/font-size-picker/index.native.js b/packages/components/src/font-size-picker/index.native.js index b12a27296ca297..48af2092e36092 100644 --- a/packages/components/src/font-size-picker/index.native.js +++ b/packages/components/src/font-size-picker/index.native.js @@ -23,6 +23,7 @@ const DEFAULT_FONT_SIZE = 16; function FontSizePicker( { fontSizes = [], + // Can this be kept as-is? disableCustomFontSizes = false, onChange, value: selectedValue, diff --git a/packages/components/src/font-size-picker/index.tsx b/packages/components/src/font-size-picker/index.tsx index cb17b7a16e3637..4cc50400d963b6 100644 --- a/packages/components/src/font-size-picker/index.tsx +++ b/packages/components/src/font-size-picker/index.tsx @@ -8,7 +8,7 @@ import type { ForwardedRef } from 'react'; */ import { __ } from '@wordpress/i18n'; import { settings } from '@wordpress/icons'; -import { useState, useMemo, forwardRef } from '@wordpress/element'; +import { useState, forwardRef, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -23,7 +23,6 @@ import { } from '../unit-control'; import { VisuallyHidden } from '../visually-hidden'; import { getCommonSizeUnit } from './utils'; -import type { FontSizePickerProps } from './types'; import { Container, Header, @@ -35,9 +34,62 @@ 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'; +import type { + FontSize, + FontSizePickerProps, + FontSizePickerMode, + FontSizePickerType, +} from './types'; const DEFAULT_UNITS = [ 'px', 'em', 'rem', 'vw', 'vh' ]; +const shouldUseSelectOverToggle = ( howManyfontSizes: number ) => + howManyfontSizes > 5; + +const getPickerType = ( + pickerMode: FontSizePickerMode, + isCustomValue: boolean, + fontSizes: FontSize[] +): FontSizePickerType => { + if ( + pickerMode === 'custom' || + ( pickerMode !== 'predefined' && isCustomValue ) + ) { + return 'custom'; + } + + return shouldUseSelectOverToggle( fontSizes.length ) + ? 'select' + : 'togglegroup'; +}; + +const getHeaderHint = ( + currentPickerType: FontSizePickerType, + selectedFontSize: FontSize | undefined, + fontSizes: FontSize[] +) => { + if ( currentPickerType === 'custom' ) { + return __( 'Custom' ); + } + + if ( ! shouldUseSelectOverToggle( fontSizes.length ) ) { + if ( selectedFontSize ) { + return ( + selectedFontSize.name || + T_SHIRT_NAMES[ fontSizes.indexOf( selectedFontSize ) ] + ); + } + return ''; + } + + const commonUnit = getCommonSizeUnit( fontSizes ); + if ( commonUnit ) { + return `(${ commonUnit })`; + } + + return ''; +}; + const UnforwardedFontSizePicker = ( props: FontSizePickerProps, ref: ForwardedRef< any > @@ -46,61 +98,52 @@ const UnforwardedFontSizePicker = ( __next40pxDefaultSize = false, fallbackFontSize, fontSizes = [], - disableCustomFontSizes = false, onChange, + pickerMode = 'all', size = 'default', units: unitsProp = DEFAULT_UNITS, value, withSlider = false, withReset = true, + + // deprecated + disableCustomFontSizes, } = props; - const units = useCustomUnits( { - availableUnits: unitsProp, - } ); + let computedPickerMode = pickerMode; + if ( disableCustomFontSizes !== undefined ) { + computedPickerMode = disableCustomFontSizes ? 'predefined' : 'all'; + } - const shouldUseSelectControl = fontSizes.length > 5; const selectedFontSize = fontSizes.find( ( fontSize ) => fontSize.size === value ); const isCustomValue = !! value && ! selectedFontSize; - const [ showCustomValueControl, setShowCustomValueControl ] = useState( - ! disableCustomFontSizes && isCustomValue + const [ currentPickerType, setCurrentPickerType ] = useState( + getPickerType( computedPickerMode, isCustomValue, fontSizes ) ); - const headerHint = useMemo( () => { - if ( showCustomValueControl ) { - return __( 'Custom' ); - } - - if ( ! shouldUseSelectControl ) { - if ( selectedFontSize ) { - return ( - selectedFontSize.name || - T_SHIRT_NAMES[ fontSizes.indexOf( selectedFontSize ) ] - ); - } - return ''; - } - - const commonUnit = getCommonSizeUnit( fontSizes ); - if ( commonUnit ) { - return `(${ commonUnit })`; - } + useEffect( () => { + setCurrentPickerType( + getPickerType( computedPickerMode, isCustomValue, fontSizes ) + ); + }, [ computedPickerMode, isCustomValue, fontSizes ] ); - return ''; - }, [ - showCustomValueControl, - shouldUseSelectControl, - selectedFontSize, - fontSizes, - ] ); + const units = useCustomUnits( { + availableUnits: unitsProp, + } ); - if ( fontSizes.length === 0 && disableCustomFontSizes ) { + if ( fontSizes.length === 0 && computedPickerMode === 'predefined' ) { return null; } + const headerHint = getHeaderHint( + currentPickerType, + selectedFontSize, + fontSizes + ); + // If neither the value or first font size is a string, then FontSizePicker // operates in a legacy "unitless" mode where UnitControl can only be used // to select px values and onChange() is always called with number values. @@ -130,56 +173,60 @@ const UnforwardedFontSizePicker = ( ) } - { ! disableCustomFontSizes && ( + { /* Show toggle button only when all picker modes are enabled */ } + { computedPickerMode === 'all' && ( { - setShowCustomValueControl( - ! showCustomValueControl + setCurrentPickerType( + getPickerType( + currentPickerType === 'custom' + ? 'predefined' + : 'custom', + isCustomValue, + fontSizes + ) ); } } - isPressed={ showCustomValueControl } + isPressed={ currentPickerType === 'custom' } size="small" /> ) }
- { !! fontSizes.length && - shouldUseSelectControl && - ! showCustomValueControl && ( - { - if ( newValue === undefined ) { - onChange?.( undefined ); - } else { - onChange?.( - hasUnits - ? newValue - : Number( newValue ), - fontSizes.find( - ( fontSize ) => - fontSize.size === newValue - ) - ); - } - } } - onSelectCustom={ () => - setShowCustomValueControl( true ) + { currentPickerType === 'select' && ( + { + if ( newValue === undefined ) { + onChange?.( undefined ); + } else { + onChange?.( + hasUnits ? newValue : Number( newValue ), + fontSizes.find( + ( fontSize ) => + fontSize.size === newValue + ) + ); } - /> - ) } - { ! shouldUseSelectControl && ! showCustomValueControl && ( + } } + onSelectCustom={ () => + setCurrentPickerType( 'custom' ) + } + /> + ) } + + { currentPickerType === 'togglegroup' && ( ) } - { ! disableCustomFontSizes && showCustomValueControl && ( + + { currentPickerType === 'custom' && ( = ( { export const Default: StoryFn< typeof FontSizePicker > = FontSizePickerWithState.bind( {} ); Default.args = { - disableCustomFontSizes: false, fontSizes: [ { name: 'Small', @@ -98,14 +97,14 @@ WithSlider.args = { }; /** - * With custom font sizes disabled via the `disableCustomFontSizes` prop, the user will + * With the `pickerMode` set to `'predefined'`, the user will * only be able to pick one of the predefined sizes passed in `fontSizes`. */ export const WithCustomSizesDisabled: StoryFn< typeof FontSizePicker > = FontSizePickerWithState.bind( {} ); WithCustomSizesDisabled.args = { ...Default.args, - disableCustomFontSizes: true, + pickerMode: 'predefined', }; /** diff --git a/packages/components/src/font-size-picker/test/index.tsx b/packages/components/src/font-size-picker/test/index.tsx index 9bb3b2d8677b69..20f47389f5e3db 100644 --- a/packages/components/src/font-size-picker/test/index.tsx +++ b/packages/components/src/font-size-picker/test/index.tsx @@ -539,16 +539,30 @@ describe( 'FontSizePicker', () => { expect( onChange ).toHaveBeenCalledWith( '80px' ); } ); - it( 'does not allow custom values when disableCustomFontSizes is set', () => { - render( + it( 'does not allow custom values when the pickerMode mode is not "all"', () => { + const { rerender } = render( ); expect( screen.queryByRole( 'button', { name: 'Set custom size' } ) ).not.toBeInTheDocument(); + + rerender( + + ); + expect( + screen.queryByRole( 'button', { name: 'Set custom size' } ) + ).not.toBeInTheDocument(); + + rerender( + + ); + expect( + screen.getByRole( 'button', { name: 'Set custom size' } ) + ).toBeVisible(); } ); it( 'does not display a slider by default', async () => { @@ -640,4 +654,10 @@ describe( 'FontSizePicker', () => { expect( units[ 2 ] ).toHaveAccessibleName( 'ex' ); } ); } + + describe( 'pickerModes', () => { + it( 'should pass', async () => { + expect( 5 ).toBeGreaterThan( 4 ); + } ); + } ); } ); diff --git a/packages/components/src/font-size-picker/types.ts b/packages/components/src/font-size-picker/types.ts index 6b4ed4b7ee75a5..41da1ece9e5b40 100644 --- a/packages/components/src/font-size-picker/types.ts +++ b/packages/components/src/font-size-picker/types.ts @@ -1,9 +1,26 @@ +export type FontSizePickerMode = 'predefined' | 'custom' | 'all'; + +export type FontSizePickerType = 'select' | 'togglegroup' | 'custom'; + export type FontSizePickerProps = { /** - * If `true`, it will not be possible to choose a custom fontSize. The user - * will be forced to pick one of the pre-defined sizes passed in fontSizes. + * When set to `predefined`, the user will be only able to pick a font size + * from the predefined list passed via the `fontSizes` prop. When set to + * `custom`, the user will be only able to choose a custom font size. When + * set to `all`, the user will be able to access all UIs through a toggle. + * + * @default 'all' + */ + pickerMode?: FontSizePickerMode; + /** + * _Note: this prop is deprecated. Please use the `pickerMode` prop instead._ + * + * If `true`, it will not be possible to choose a custom font size. The user + * will be forced to pick one of the pre-defined sizes passed via the + * `fontSizes` prop. * * @default false + * @deprecated */ disableCustomFontSizes?: boolean; /** @@ -29,7 +46,7 @@ export type FontSizePickerProps = { /** * Available units for custom font size selection. * - * @default `[ 'px', 'em', 'rem' ]` + * @default [ 'px', 'em', 'rem' ] */ units?: string[]; /** @@ -37,8 +54,8 @@ export type FontSizePickerProps = { */ value?: number | string; /** - * If `true`, the UI will contain a slider, instead of a numeric text input - * field. If `false`, no slider will be present. + * If `true`, a slider will be displayed alongside the input field when a + * custom font size is active. Has no effect when `pickerMode` is `predefined`. * * @default false */ @@ -46,7 +63,7 @@ export type FontSizePickerProps = { /** * If `true`, a reset button will be displayed alongside the input field * when a custom font size is active. Has no effect when - * `disableCustomFontSizes` or `withSlider` is `true`. + * `pickerMode` is `'predefined'` or `withSlider` is `true`. * * @default true */ @@ -98,9 +115,7 @@ export type FontSizePickerSelectProps = Pick< 'value' | 'size' > & { fontSizes: NonNullable< FontSizePickerProps[ 'fontSizes' ] >; - disableCustomFontSizes: NonNullable< - FontSizePickerProps[ 'disableCustomFontSizes' ] - >; + pickerMode: NonNullable< FontSizePickerProps[ 'pickerMode' ] >; onChange: NonNullable< FontSizePickerProps[ 'onChange' ] >; onSelectCustom: () => void; __next40pxDefaultSize: boolean;