From 697ea9a948e2f980611cdde761619d04614f285b Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 15 Jun 2023 16:17:57 +1000 Subject: [PATCH] Fluid typography: custom font-sizes should use max viewport width (#51516) * This commit: - ensures that custom font size calculations take into account any layout.wideSize values - guards against unsupported units for min and max viewport widths in the backend - adds tests to cover changes * Apply suggestions from code review Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> * Update typography-utils.js Reinstate return break --------- Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- lib/block-supports/typography.php | 6 + .../components/font-sizes/test/fluid-utils.js | 173 ++++++++++-------- packages/block-editor/src/hooks/font-size.js | 29 ++- .../src/hooks/use-typography-props.js | 31 ++-- phpunit/block-supports/typography-test.php | 83 +++++++++ 5 files changed, 211 insertions(+), 111 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index f1d29217e38830..469b429f9b3ddf 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -381,6 +381,7 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) { 'coerce_to' => $font_size_unit, ) ); + $minimum_viewport_width = gutenberg_get_typography_value_and_unit( $minimum_viewport_width_raw, array( @@ -388,6 +389,11 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) { ) ); + // Protects against unsupported units in min and max viewport widths. + if ( ! $minimum_viewport_width || ! $maximum_viewport_width ) { + return null; + } + // Build CSS rule. // Borrowed from https://websemantics.uk/tools/responsive-font-calculator/. $view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit; diff --git a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js index a50737a6dc0a86..a0095073261cc7 100644 --- a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js @@ -17,6 +17,13 @@ describe( 'getComputedFluidTypographyValue()', () => { delete logged[ key ]; } } ); + it( 'should return `null` when given a font size is not a support value+unit', () => { + const fluidTypographyValues = getComputedFluidTypographyValue( { + fontSize: + 'clamp(18.959px, 1.185rem + ((1vw - 3.2px) * 0.863), 30px)', + } ); + expect( fluidTypographyValues ).toBeNull(); + } ); it( 'should return a fluid font size when given a min and max font size', () => { const fluidTypographyValues = getComputedFluidTypographyValue( { @@ -67,91 +74,105 @@ describe( 'getComputedFluidTypographyValue()', () => { ); } ); - describe( 'getTypographyValueAndUnit', () => { - it( 'should return the expected return values', () => { - [ - { - value: null, - expected: null, - }, - { - value: false, - expected: null, - }, - { - value: true, - expected: null, - }, - { - value: [ '10' ], - expected: null, - }, - { - value: '10vh', - expected: null, - }, - { - value: 'calc(2 * 10px)', - expected: null, - }, - { - value: 'clamp(15px, 0.9375rem + ((1vw - 7.68px) * 5.409), 60px)', - expected: null, - }, - { - value: '10', - expected: { - value: 10, - unit: 'px', - }, + it( 'should return null when maximumViewPortWidth is not a supported value or unit', () => { + const fluidTypographyValues = getComputedFluidTypographyValue( { + fontSize: '30px', + maximumViewPortWidth: 'min(calc(100% - 60px), 1200px)', + } ); + expect( fluidTypographyValues ).toBeNull(); + } ); + + it( 'should return `null` font size when minimumViewPortWidth is not a supported value or unit', () => { + const fluidTypographyValues = getComputedFluidTypographyValue( { + fontSize: '33px', + minimumViewPortWidth: 'calc(100% - 60px)', + } ); + expect( fluidTypographyValues ).toBeNull(); + } ); +} ); + +describe( 'getTypographyValueAndUnit', () => { + it( 'should return the expected return values', () => { + [ + { + value: null, + expected: null, + }, + { + value: false, + expected: null, + }, + { + value: true, + expected: null, + }, + { + value: [ '10' ], + expected: null, + }, + { + value: '10vh', + expected: null, + }, + { + value: 'calc(2 * 10px)', + expected: null, + }, + { + value: 'clamp(15px, 0.9375rem + ((1vw - 7.68px) * 5.409), 60px)', + expected: null, + }, + { + value: '10', + expected: { + value: 10, + unit: 'px', }, - { + }, + { + value: 11, + expected: { value: 11, - expected: { - value: 11, - unit: 'px', - }, + unit: 'px', }, - { + }, + { + value: 11.234, + expected: { value: 11.234, - expected: { - value: 11.234, - unit: 'px', - }, + unit: 'px', }, - { - value: '12rem', - expected: { - value: 12, - unit: 'rem', - }, + }, + { + value: '12rem', + expected: { + value: 12, + unit: 'rem', }, - { - value: '12px', - expected: { - value: 12, - unit: 'px', - }, + }, + { + value: '12px', + expected: { + value: 12, + unit: 'px', }, - { - value: '12em', - expected: { - value: 12, - unit: 'em', - }, + }, + { + value: '12em', + expected: { + value: 12, + unit: 'em', }, - { - value: '12.74em', - expected: { - value: 12.74, - unit: 'em', - }, + }, + { + value: '12.74em', + expected: { + value: 12.74, + unit: 'em', }, - ].forEach( ( { value, expected } ) => { - expect( getTypographyValueAndUnit( value ) ).toEqual( - expected - ); - } ); + }, + ].forEach( ( { value, expected } ) => { + expect( getTypographyValueAndUnit( value ) ).toEqual( expected ); } ); } ); } ); diff --git a/packages/block-editor/src/hooks/font-size.js b/packages/block-editor/src/hooks/font-size.js index e03b73c56331db..54d6836a1ef0ad 100644 --- a/packages/block-editor/src/hooks/font-size.js +++ b/packages/block-editor/src/hooks/font-size.js @@ -15,7 +15,6 @@ import { getFontSizeClass, getFontSizeObjectByValue, FontSizePicker, - getComputedFluidTypographyValue, } from '../components/font-sizes'; import { TYPOGRAPHY_SUPPORT_KEY } from './typography'; import { @@ -25,6 +24,10 @@ import { } from './utils'; import useSetting from '../components/use-setting'; import { store as blockEditorStore } from '../store'; +import { + getTypographyFontSizeValue, + getFluidTypographyOptionsFromSettings, +} from '../components/global-styles/typography-utils'; export const FONT_SIZE_SUPPORT_KEY = 'typography.fontSize'; @@ -289,23 +292,15 @@ function addEditPropsForFluidCustomFontSizes( blockType ) { // BlockListContext.Provider. If we set fontSize using editor. // BlockListBlock instead of using getEditWrapperProps then the value is // clobbered when the core/style/addEditProps filter runs. - const fluidTypographyConfig = + const fluidTypographySettings = getFluidTypographyOptionsFromSettings( select( blockEditorStore ).getSettings().__experimentalFeatures - ?.typography?.fluid; - - const fluidTypographySettings = - typeof fluidTypographyConfig === 'object' - ? fluidTypographyConfig - : {}; - - const newFontSize = - fontSize && !! fluidTypographyConfig - ? getComputedFluidTypographyValue( { - fontSize, - minimumFontSizeLimit: - fluidTypographySettings?.minFontSize, - } ) - : null; + ); + const newFontSize = fontSize + ? getTypographyFontSizeValue( + { size: fontSize }, + fluidTypographySettings + ) + : null; if ( newFontSize === null ) { return wrapperProps; diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js index d6a6b0629143db..f3f7d531974c52 100644 --- a/packages/block-editor/src/hooks/use-typography-props.js +++ b/packages/block-editor/src/hooks/use-typography-props.js @@ -9,7 +9,10 @@ import classnames from 'classnames'; */ import { getInlineStyles } from './style'; import { getFontSizeClass } from '../components/font-sizes'; -import { getComputedFluidTypographyValue } from '../components/font-sizes/fluid-utils'; +import { + getTypographyFontSizeValue, + getFluidTypographyOptionsFromSettings, +} from '../components/global-styles/typography-utils'; /* * This utility is intended to assist where the serialization of the typography @@ -27,24 +30,16 @@ import { getComputedFluidTypographyValue } from '../components/font-sizes/fluid- */ export function getTypographyClassesAndStyles( attributes, settings ) { let typographyStyles = attributes?.style?.typography || {}; - const fluidTypographySettings = settings?.typography?.fluid; + const fluidTypographySettings = + getFluidTypographyOptionsFromSettings( settings ); - if ( - !! fluidTypographySettings && - ( true === fluidTypographySettings || - Object.keys( fluidTypographySettings ).length !== 0 ) - ) { - const newFontSize = - getComputedFluidTypographyValue( { - fontSize: attributes?.style?.typography?.fontSize, - minimumFontSizeLimit: fluidTypographySettings?.minFontSize, - maximumViewPortWidth: settings?.layout?.wideSize, - } ) || attributes?.style?.typography?.fontSize; - typographyStyles = { - ...typographyStyles, - fontSize: newFontSize, - }; - } + typographyStyles = { + ...typographyStyles, + fontSize: getTypographyFontSizeValue( + { size: attributes?.style?.typography?.fontSize }, + fluidTypographySettings + ), + }; const style = getInlineStyles( { typography: typographyStyles } ); const fontFamilyClassName = !! attributes?.fontFamily diff --git a/phpunit/block-supports/typography-test.php b/phpunit/block-supports/typography-test.php index 5a2f52f780f1df..e3e136eabc6921 100644 --- a/phpunit/block-supports/typography-test.php +++ b/phpunit/block-supports/typography-test.php @@ -887,4 +887,87 @@ public function data_invalid_size_wp_get_typography_value_and_unit() { 'size: array' => array( array( '10' ) ), ); } + + /** + * Tests computed font size values. + * + * @covers ::gutenberg_get_computed_fluid_typography_value + * + * @dataProvider data_get_computed_fluid_typography_value + * + * @param array $args { + * Optional. An associative array of values to calculate a fluid formula for font size. Default is empty array. + * + * @type string $maximum_viewport_width Maximum size up to which type will have fluidity. + * @type string $minimum_viewport_width Minimum viewport size from which type will have fluidity. + * @type string $maximum_font_size Maximum font size for any clamp() calculation. + * @type string $minimum_font_size Minimum font size for any clamp() calculation. + * @type int $scale_factor A scale factor to determine how fast a font scales within boundaries. + * } + * @param string $expected_output Expected value of style property from gutenberg_apply_typography_support(). + */ + public function test_get_computed_fluid_typography_value( $args, $expected_output ) { + $actual = gutenberg_get_computed_fluid_typography_value( $args ); + $this->assertSame( $expected_output, $actual ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_computed_fluid_typography_value() { + return array( + 'returns clamped value with valid args' => array( + 'args' => array( + 'minimum_viewport_width' => '320px', + 'maximum_viewport_width' => '1000px', + 'minimum_font_size' => '50px', + 'maximum_font_size' => '100px', + 'scale_factor' => 1, + ), + 'expected_output' => 'clamp(50px, 3.125rem + ((1vw - 3.2px) * 7.353), 100px)', + ), + 'returns `null` when `maximum_viewport_width` is an unsupported unit' => array( + 'args' => array( + 'minimum_viewport_width' => '320px', + 'maximum_viewport_width' => 'calc(100% - 60px)', + 'minimum_font_size' => '50px', + 'maximum_font_size' => '100px', + 'scale_factor' => 1, + ), + 'expected_output' => null, + ), + 'returns `null` when `minimum_viewport_width` is an unsupported unit' => array( + 'args' => array( + 'minimum_viewport_width' => 'calc(100% - 60px)', + 'maximum_viewport_width' => '1000px', + 'minimum_font_size' => '50px', + 'maximum_font_size' => '100px', + 'scale_factor' => 1, + ), + 'expected_output' => null, + ), + 'returns `null` when `minimum_font_size` is an unsupported unit' => array( + 'args' => array( + 'minimum_viewport_width' => '320em', + 'maximum_viewport_width' => '1000em', + 'minimum_font_size' => '10vw', + 'maximum_font_size' => '100em', + 'scale_factor' => 1, + ), + 'expected_output' => null, + ), + 'returns `null` when `maximum_font_size` is an unsupported unit' => array( + 'args' => array( + 'minimum_viewport_width' => '320em', + 'maximum_viewport_width' => '1000em', + 'minimum_font_size' => '50px', + 'maximum_font_size' => '100%', + 'scale_factor' => 1, + ), + 'expected_output' => null, + ), + ); + } }