From 3173e47446d36db5f354a36e1984cbc06143e8f9 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 19 Oct 2022 08:12:27 +1100 Subject: [PATCH] Fluid typography: add font size constraints (#44993) * Initial commit: - Adds default minimum font size limits so that text does not become smaller than 14px/0.875rem/0.875em. The consequence will be that all fluid font sizes will be no smaller than 14px/0.875rem/0.875em in any viewport width. - Users may define font-sizes that are smaller than 14px/0.875rem/0.875em. In this case the user-defined font size becomes the minimum. For example if a user defines 9px for a block, then the minimum font size for that block will be 9px. - Min font size should not be greater than max font size. - Round to 3 decimal places (JS) - Rounding computed min and max values * Rounding parsed min and max values * Updating JS rounding function so that values are rounded * Removing type coercion * Updating failing test * Remove reference to preset in favour of "fontSize" * Ensure that rem units are used when calculating min and max boundaries in em This is to keep it consistent with the original formula and ensure the fluid argument in clamp is working off a size relative to :root Fixing formatting and grammar Rolling back renaming $preset to $font_size in preference to a follow up. Do not clamp value if there is no min size and the font size is less than 14px * Oh Linter! You persistent crank. * For font sizes of < 14px that have no defined minimum sizes, use the font size to set the floor of the clamp() value. * Removing max < min check. It wasn't there before. It can be a follow up if required as it requires extra logic. * Checking for a zero-based linear factor. If we find one, default to `1` * Refer to correct JS var in JS file --- lib/block-supports/typography.php | 76 ++- .../src/components/font-sizes/fluid-utils.js | 125 ++++- .../components/font-sizes/test/fluid-utils.js | 10 +- .../global-styles/test/typography-utils.js | 504 +++++++++++------- .../test/use-global-styles-output.js | 2 +- phpunit/block-supports/typography-test.php | 162 ++++-- 6 files changed, 619 insertions(+), 260 deletions(-) diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 2126aaf847fdec..b1adaf1a679b81 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -328,8 +328,17 @@ function gutenberg_get_typography_value_and_unit( $raw_value, $options = array() $unit = $options['coerce_to']; } + /* + * No calculation is required if swapping between em and rem yet, + * since we assume a root size value. Later we might like to differentiate between + * :root font size (rem) and parent element font size (em) relativity. + */ + if ( ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) && ( 'em' === $unit || 'rem' === $unit ) ) { + $unit = $options['coerce_to']; + } + return array( - 'value' => $value, + 'value' => round( $value, 3 ), 'unit' => $unit, ); } @@ -357,14 +366,16 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) { $minimum_font_size_raw = isset( $args['minimum_font_size'] ) ? $args['minimum_font_size'] : null; $scale_factor = isset( $args['scale_factor'] ) ? $args['scale_factor'] : null; - // Grab the minimum font size and normalize it in order to use the value for calculations. + // Normalizes the minimum font size in order to use the value for calculations. $minimum_font_size = gutenberg_get_typography_value_and_unit( $minimum_font_size_raw ); - // We get a 'preferred' unit to keep units consistent when calculating, - // otherwise the result will not be accurate. + /* + * We get a 'preferred' unit to keep units consistent when calculating, + * otherwise the result will not be accurate. + */ $font_size_unit = isset( $minimum_font_size['unit'] ) ? $minimum_font_size['unit'] : 'rem'; - // Grab the maximum font size and normalize it in order to use the value for calculations. + // Grabs the maximum font size and normalize it in order to use the value for calculations. $maximum_font_size = gutenberg_get_typography_value_and_unit( $maximum_font_size_raw, array( @@ -372,12 +383,12 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) { ) ); - // Protect against unsupported units. + // Checks for mandatory min and max sizes, and protects against unsupported units. if ( ! $maximum_font_size || ! $minimum_font_size ) { return null; } - // Use rem for accessible fluid target font scaling. + // Uses rem for accessible fluid target font scaling. $minimum_font_size_rem = gutenberg_get_typography_value_and_unit( $minimum_font_size_raw, array( @@ -403,8 +414,9 @@ function gutenberg_get_computed_fluid_typography_value( $args = array() ) { // Borrowed from https://websemantics.uk/tools/responsive-font-calculator/. $view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit; $linear_factor = 100 * ( ( $maximum_font_size['value'] - $minimum_font_size['value'] ) / ( $maximum_viewport_width['value'] - $minimum_viewport_width['value'] ) ); - $linear_factor = round( $linear_factor, 3 ) * $scale_factor; - $fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor)"; + $linear_factor_scaled = round( $linear_factor * $scale_factor, 3 ); + $linear_factor_scaled = empty( $linear_factor_scaled ) ? 1 : $linear_factor_scaled; + $fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)"; return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)"; } @@ -437,7 +449,7 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty return $preset['size']; } - // Check if fluid font sizes are activated. + // Checks if fluid font sizes are activated. $typography_settings = gutenberg_get_global_settings( array( 'typography' ) ); $should_use_fluid_typography = isset( $typography_settings['fluid'] ) && true === $typography_settings['fluid'] ? true : $should_use_fluid_typography; @@ -451,6 +463,7 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty $default_minimum_font_size_factor = 0.75; $default_maximum_font_size_factor = 1.5; $default_scale_factor = 1; + $default_minimum_font_size_limit = '14px'; // Font sizes. $fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null; @@ -472,13 +485,48 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty return $preset['size']; } - // If no fluid min or max font sizes are available, create some using min/max font size factors. + // If no fluid max font size is available, create one using max font size factor. + if ( ! $maximum_font_size_raw ) { + $maximum_font_size_raw = round( $preferred_size['value'] * $default_maximum_font_size_factor, 3 ) . $preferred_size['unit']; + } + + // If no fluid min font size is available, create one using min font size factor. if ( ! $minimum_font_size_raw ) { - $minimum_font_size_raw = ( $preferred_size['value'] * $default_minimum_font_size_factor ) . $preferred_size['unit']; + $minimum_font_size_raw = round( $preferred_size['value'] * $default_minimum_font_size_factor, 3 ) . $preferred_size['unit']; } - if ( ! $maximum_font_size_raw ) { - $maximum_font_size_raw = ( $preferred_size['value'] * $default_maximum_font_size_factor ) . $preferred_size['unit']; + // Parses the minimum font size limit, so we can perform checks using it. + $minimum_font_size_limit = gutenberg_get_typography_value_and_unit( + $default_minimum_font_size_limit, + array( + 'coerce_to' => $preferred_size['unit'], + ) + ); + + if ( ! empty( $minimum_font_size_limit ) ) { + /* + * If a minimum size was not passed to this function + * and the user-defined font size is lower than $minimum_font_size_limit, + * then uses the user-defined font size as the minimum font-size. + */ + if ( ! isset( $fluid_font_size_settings['min'] ) && $preferred_size['value'] < $minimum_font_size_limit['value'] ) { + $minimum_font_size_raw = implode( '', $preferred_size ); + } else { + $minimum_font_size_parsed = gutenberg_get_typography_value_and_unit( + $minimum_font_size_raw, + array( + 'coerce_to' => $preferred_size['unit'], + ) + ); + + /* + * If the passed or calculated minimum font size is lower than $minimum_font_size_limit + * use $minimum_font_size_limit instead. + */ + if ( ! empty( $minimum_font_size_parsed ) && $minimum_font_size_parsed['value'] < $minimum_font_size_limit['value'] ) { + $minimum_font_size_raw = implode( '', $minimum_font_size_limit ); + } + } } $fluid_font_size_value = gutenberg_get_computed_fluid_typography_value( diff --git a/packages/block-editor/src/components/font-sizes/fluid-utils.js b/packages/block-editor/src/components/font-sizes/fluid-utils.js index 7294a83da106f8..2c6540f89d0494 100644 --- a/packages/block-editor/src/components/font-sizes/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/fluid-utils.js @@ -10,6 +10,7 @@ const DEFAULT_MINIMUM_VIEWPORT_WIDTH = '768px'; const DEFAULT_SCALE_FACTOR = 1; const DEFAULT_MINIMUM_FONT_SIZE_FACTOR = 0.75; const DEFAULT_MAXIMUM_FONT_SIZE_FACTOR = 1.5; +const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px'; /** * Computes a fluid font-size value that uses clamp(). A minimum and maxinmum @@ -53,11 +54,20 @@ export function getComputedFluidTypographyValue( { scaleFactor = DEFAULT_SCALE_FACTOR, minimumFontSizeFactor = DEFAULT_MINIMUM_FONT_SIZE_FACTOR, maximumFontSizeFactor = DEFAULT_MAXIMUM_FONT_SIZE_FACTOR, + minimumFontSizeLimit = DEFAULT_MINIMUM_FONT_SIZE_LIMIT, } ) { - // Calculate missing minimumFontSize and maximumFontSize from - // defaultFontSize if provided. - if ( fontSize && ( ! minimumFontSize || ! maximumFontSize ) ) { - // Parse default font size. + /* + * Caches minimumFontSize in minimumFontSizeValue + * so we can check if minimumFontSize exists later. + */ + let minimumFontSizeValue = minimumFontSize; + + /* + * Calculates missing minimumFontSize and maximumFontSize from + * defaultFontSize if provided. + */ + if ( fontSize ) { + // Parses default font size. const fontSizeParsed = getTypographyValueAndUnit( fontSize ); // Protect against invalid units. @@ -66,46 +76,95 @@ export function getComputedFluidTypographyValue( { } // If no minimumFontSize is provided, derive using min scale factor. - if ( ! minimumFontSize ) { - minimumFontSize = - fontSizeParsed.value * minimumFontSizeFactor + - fontSizeParsed.unit; + if ( ! minimumFontSizeValue ) { + minimumFontSizeValue = + roundToPrecision( + fontSizeParsed.value * minimumFontSizeFactor, + 3 + ) + fontSizeParsed.unit; + } + + // Parses the minimum font size limit, so we can perform checks using it. + const minimumFontSizeLimitParsed = getTypographyValueAndUnit( + minimumFontSizeLimit, + { + coerceTo: fontSizeParsed.unit, + } + ); + + if ( !! minimumFontSizeLimitParsed?.value ) { + /* + * If a minimum size was not passed to this function + * and the user-defined font size is lower than `minimumFontSizeLimit`, + * then uses the user-defined font size as the minimum font-size. + */ + if ( + ! minimumFontSize && + fontSizeParsed?.value < minimumFontSizeLimitParsed?.value + ) { + minimumFontSizeValue = `${ fontSizeParsed.value }${ fontSizeParsed.unit }`; + } else { + const minimumFontSizeParsed = getTypographyValueAndUnit( + minimumFontSizeValue, + { + coerceTo: fontSizeParsed.unit, + } + ); + + /* + * Otherwise, if the passed or calculated minimum font size is lower than `minimumFontSizeLimit` + * use `minimumFontSizeLimit` instead. + */ + if ( + !! minimumFontSizeParsed?.value && + minimumFontSizeParsed.value < + minimumFontSizeLimitParsed.value + ) { + minimumFontSizeValue = `${ minimumFontSizeLimitParsed.value }${ minimumFontSizeLimitParsed.unit }`; + } + } } // If no maximumFontSize is provided, derive using max scale factor. if ( ! maximumFontSize ) { maximumFontSize = - fontSizeParsed.value * maximumFontSizeFactor + - fontSizeParsed.unit; + roundToPrecision( + fontSizeParsed.value * maximumFontSizeFactor, + 3 + ) + fontSizeParsed.unit; } } // Return early if one of the provided inputs is not provided. - if ( ! minimumFontSize || ! maximumFontSize ) { + if ( ! minimumFontSizeValue || ! maximumFontSize ) { return null; } // Grab the minimum font size and normalize it in order to use the value for calculations. - const minimumFontSizeParsed = getTypographyValueAndUnit( minimumFontSize ); + const minimumFontSizeParsed = + getTypographyValueAndUnit( minimumFontSizeValue ); // We get a 'preferred' unit to keep units consistent when calculating, // otherwise the result will not be accurate. const fontSizeUnit = minimumFontSizeParsed?.unit || 'rem'; - // Grab the maximum font size and normalize it in order to use the value for calculations. + // Grabs the maximum font size and normalize it in order to use the value for calculations. const maximumFontSizeParsed = getTypographyValueAndUnit( maximumFontSize, { coerceTo: fontSizeUnit, } ); - // Protect against unsupported units. + // Checks for mandatory min and max sizes, and protects against unsupported units. if ( ! minimumFontSizeParsed || ! maximumFontSizeParsed ) { return null; } - // Use rem for accessible fluid target font scaling. - const minimumFontSizeRem = getTypographyValueAndUnit( minimumFontSize, { - coerceTo: 'rem', - } ); + // Uses rem for accessible fluid target font scaling. + const minimumFontSizeRem = getTypographyValueAndUnit( + minimumFontSizeValue, + { + coerceTo: 'rem', + } + ); // Viewport widths defined for fluid typography. Normalize units const maximumViewPortWidthParsed = getTypographyValueAndUnit( @@ -133,17 +192,20 @@ export function getComputedFluidTypographyValue( { 3 ); - const viewPortWidthOffset = minViewPortWidthOffsetValue + fontSizeUnit; - let linearFactor = + const viewPortWidthOffset = + roundToPrecision( minViewPortWidthOffsetValue, 3 ) + fontSizeUnit; + const linearFactor = 100 * ( ( maximumFontSizeParsed.value - minimumFontSizeParsed.value ) / ( maximumViewPortWidthParsed.value - minumumViewPortWidthParsed.value ) ); - linearFactor = roundToPrecision( linearFactor, 3 ) || 1; - const linearFactorScaled = linearFactor * scaleFactor; + const linearFactorScaled = roundToPrecision( + ( linearFactor || 1 ) * scaleFactor, + 3 + ); const fluidTargetFontSize = `${ minimumFontSizeRem.value }${ minimumFontSizeRem.unit } + ((1vw - ${ viewPortWidthOffset }) * ${ linearFactorScaled })`; - return `clamp(${ minimumFontSize }, ${ fluidTargetFontSize }, ${ maximumFontSize })`; + return `clamp(${ minimumFontSizeValue }, ${ fluidTargetFontSize }, ${ maximumFontSize })`; } /** @@ -199,8 +261,20 @@ export function getTypographyValueAndUnit( rawValue, options = {} ) { unit = coerceTo; } + /* + * No calculation is required if swapping between em and rem yet, + * since we assume a root size value. Later we might like to differentiate between + * :root font size (rem) and parent element font size (em) relativity. + */ + if ( + ( 'em' === coerceTo || 'rem' === coerceTo ) && + ( 'em' === unit || 'rem' === unit ) + ) { + unit = coerceTo; + } + return { - value: returnValue, + value: roundToPrecision( returnValue, 3 ), unit, }; } @@ -215,7 +289,8 @@ export function getTypographyValueAndUnit( rawValue, options = {} ) { * @return {number|undefined} Value rounded to standard precision. */ export function roundToPrecision( value, digits = 3 ) { + const base = Math.pow( 10, digits ); return Number.isFinite( value ) - ? parseFloat( value.toFixed( digits ) ) + ? parseFloat( Math.round( value * base ) / base ) : undefined; } 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 cd45c3593e1a48..aa268d04d7f1f2 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 @@ -33,7 +33,7 @@ describe( 'getComputedFluidTypographyValue()', () => { fontSize: '30px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.40625rem + ((1vw - 7.68px) * 2.704), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)' ); } ); @@ -42,7 +42,7 @@ describe( 'getComputedFluidTypographyValue()', () => { fontSize: '30px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.40625rem + ((1vw - 7.68px) * 2.704), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 2.704), 45px)' ); } ); @@ -53,7 +53,7 @@ describe( 'getComputedFluidTypographyValue()', () => { maximumViewPortWidth: '1000px', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.40625rem + ((1vw - 5px) * 4.5), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 5px) * 4.5), 45px)' ); } ); @@ -63,7 +63,7 @@ describe( 'getComputedFluidTypographyValue()', () => { scaleFactor: '2', } ); expect( fluidTypographyValues ).toBe( - 'clamp(22.5px, 1.40625rem + ((1vw - 7.68px) * 5.408), 45px)' + 'clamp(22.5px, 1.406rem + ((1vw - 7.68px) * 5.409), 45px)' ); } ); @@ -74,7 +74,7 @@ describe( 'getComputedFluidTypographyValue()', () => { maximumFontSizeFactor: '2', } ); expect( fluidTypographyValues ).toBe( - 'clamp(15px, 0.9375rem + ((1vw - 7.68px) * 5.409), 60px)' + 'clamp(15px, 0.938rem + ((1vw - 7.68px) * 5.409), 60px)' ); } ); diff --git a/packages/edit-site/src/components/global-styles/test/typography-utils.js b/packages/edit-site/src/components/global-styles/test/typography-utils.js index 97ebeb583e497f..e0c29a37ea9811 100644 --- a/packages/edit-site/src/components/global-styles/test/typography-utils.js +++ b/packages/edit-site/src/components/global-styles/test/typography-utils.js @@ -5,206 +5,352 @@ import { getTypographyFontSizeValue } from '../typography-utils'; describe( 'typography utils', () => { describe( 'getTypographyFontSizeValue', () => { - it( 'should return the expected values', () => { - [ - // Default return non-fluid value. - { - preset: { - size: '28px', - }, - typographySettings: undefined, - expected: '28px', + [ + { + message: 'Default return non-fluid value.', + preset: { + size: '28px', }, - // Default return value where font size is 0. - { - preset: { - size: 0, - }, - typographySettings: undefined, - expected: 0, + typographySettings: undefined, + expected: '28px', + }, + + { + message: 'Default return value where font size is 0.', + preset: { + size: 0, }, - // Default return value where font size is '0'. - { - preset: { - size: '0', - }, - typographySettings: undefined, - expected: '0', + typographySettings: undefined, + expected: 0, + }, + + { + message: "Default return value where font size is '0'.", + preset: { + size: '0', }, + typographySettings: undefined, + expected: '0', + }, - // Default return non-fluid value where `size` is undefined. - { - preset: { - size: undefined, - }, - typographySettings: undefined, - expected: undefined, - }, - // Should return non-fluid value when fluid is `false`. - { - preset: { - size: '28px', - fluid: false, - }, - typographySettings: { - fluid: true, - }, - expected: '28px', + { + message: + 'Default return non-fluid value where `size` is undefined.', + preset: { + size: undefined, }, - // Should coerce integer to `px` and return fluid value. - { - preset: { - size: 33, - fluid: true, - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(24.75px, 1.546875rem + ((1vw - 7.68px) * 2.975), 49.5px)', + typographySettings: undefined, + expected: undefined, + }, + + { + message: 'return non-fluid value when fluid is `false`.', + preset: { + size: '28px', + fluid: false, }, + typographySettings: { + fluid: true, + }, + expected: '28px', + }, - // Should coerce float to `px` and return fluid value. - { - preset: { - size: 100.23, - fluid: true, - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(75.1725px, 4.69828125rem + ((1vw - 7.68px) * 9.035), 150.345px)', - }, - // Should return incoming value when already clamped. - { - preset: { - size: 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', - fluid: false, - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', - }, - // Should return incoming value with unsupported unit. - { - preset: { - size: '1000%', - fluid: false, - }, - typographySettings: { - fluid: true, - }, - expected: '1000%', + { + message: + 'Should coerce integer to `px` and return fluid value.', + preset: { + size: 33, + fluid: true, }, - // Should return fluid value. - { - preset: { - size: '1.75rem', - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(1.3125rem, 1.3125rem + ((1vw - 0.48rem) * 2.524), 2.625rem)', + typographySettings: { + fluid: true, }, - // Should return fluid value for floats with units. - { - preset: { - size: '100.175px', - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(75.13125px, 4.695703125rem + ((1vw - 7.68px) * 9.03), 150.2625px)', - }, - // Should return default fluid values with empty fluid array. - { - preset: { - size: '28px', - fluid: [], - }, - typographySettings: { - fluid: true, - }, - expected: - 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + expected: + 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 2.975), 49.5px)', + }, + + { + message: 'coerce float to `px` and return fluid value.', + preset: { + size: 100.23, + fluid: true, + }, + typographySettings: { + fluid: true, }, + expected: + 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 9.035), 150.345px)', + }, - // Should return default fluid values with null values. - { - preset: { - size: '28px', - fluid: null, - }, - typographySettings: { - fluid: true, + { + message: 'return incoming value when already clamped.', + preset: { + size: 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + fluid: false, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + }, + + { + message: 'return incoming value with unsupported unit.', + preset: { + size: '1000%', + fluid: false, + }, + typographySettings: { + fluid: true, + }, + expected: '1000%', + }, + + { + message: 'return fluid value.', + preset: { + size: '1.75rem', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 2.523), 2.625rem)', + }, + + { + message: 'return fluid value for floats with units.', + preset: { + size: '100.175px', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 9.03), 150.263px)', + }, + + { + message: + 'Should return default fluid values with empty fluid array.', + preset: { + size: '28px', + fluid: [], + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + }, + + { + message: 'return default fluid values with null value.', + preset: { + size: '28px', + fluid: null, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + }, + + { + message: + 'return clamped value if min font size is greater than max.', + preset: { + size: '3rem', + fluid: { + min: '5rem', + max: '32px', }, - expected: - 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', - }, - - // Should return size with invalid fluid units. - { - preset: { - size: '10em', - fluid: { - min: '20vw', - max: '50%', - }, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(5rem, 5rem + ((1vw - 0.48rem) * -5.769), 32px)', + }, + + { + message: 'return size with invalid fluid units.', + preset: { + size: '10em', + fluid: { + min: '20vw', + max: '50%', }, - typographySettings: { - fluid: true, + }, + typographySettings: { + fluid: true, + }, + expected: '10em', + }, + + { + message: + 'return clamped using font size where no min is given and size is less than default min size.', + preset: { + size: '3px', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(3px, 0.188rem + ((1vw - 7.68px) * 0.18), 4.5px)', + }, + + { + message: + 'return fluid clamp value with different min max units.', + preset: { + size: '28px', + fluid: { + min: '20px', + max: '50rem', }, - expected: '10em', - }, - // Should return fluid clamp value. - { - preset: { - size: '28px', - fluid: { - min: '20px', - max: '50rem', - }, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(20px, 1.25rem + ((1vw - 7.68px) * 93.75), 50rem)', + }, + // + { + message: + ' Should return clamp value with default fluid max value.', + preset: { + size: '28px', + fluid: { + min: '2.6rem', }, - typographySettings: { - fluid: true, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * 0.048), 42px)', + }, + + { + message: + 'Should return clamp value with default fluid min value.', + preset: { + size: '28px', + fluid: { + max: '80px', }, - expected: - 'clamp(20px, 1.25rem + ((1vw - 7.68px) * 93.75), 50rem)', - }, - // Should return clamp value with default fluid max value. - { - preset: { - size: '28px', - fluid: { - min: '2.6rem', - }, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 7.091), 80px)', + }, + + { + message: 'adjust computed min in px to min limit.', + preset: { + size: '14px', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', + }, + + { + message: 'adjust computed min in rem to min limit.', + preset: { + size: '1.1rem', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 1.49), 1.65rem)', + }, + + { + message: 'adjust computed min in em to min limit.', + preset: { + size: '1.1em', + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(0.875em, 0.875rem + ((1vw - 0.48em) * 1.49), 1.65em)', + }, + + { + message: 'adjust fluid min value in px to min limit', + preset: { + size: '20px', + fluid: { + min: '12px', }, - typographySettings: { - fluid: true, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 1.923), 30px)', + }, + + { + message: 'adjust fluid min value in rem to min limit.', + preset: { + size: '1.5rem', + fluid: { + min: '0.5rem', }, - expected: - 'clamp(2.6rem, 2.6rem + ((1vw - 0.48rem) * 0.048), 42px)', - }, - // Should return clamp value with default fluid min value. - { - preset: { - size: '28px', - fluid: { - max: '80px', - }, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 2.644), 2.25rem)', + }, + + { + message: 'adjust fluid min value but honor max value.', + preset: { + size: '1.5rem', + fluid: { + min: '0.5rem', + max: '5rem', }, - typographySettings: { - fluid: true, + }, + typographySettings: { + fluid: true, + }, + expected: + 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 7.933), 5rem)', + }, + + { + message: + 'return a fluid font size a min and max font sizes are equal.', + preset: { + size: '4rem', + fluid: { + min: '30px', + max: '30px', }, - expected: - 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 7.091), 80px)', }, - ].forEach( ( { preset, typographySettings, expected } ) => { + typographySettings: { + fluid: true, + }, + expected: 'clamp(30px, 1.875rem + ((1vw - 7.68px) * 1), 30px)', + }, + ].forEach( ( { message, preset, typographySettings, expected } ) => { + it( `should ${ message }`, () => { expect( getTypographyFontSizeValue( preset, typographySettings ) ).toBe( expected ); diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js index 48f21603ad78e8..a6bd55ff86ec8f 100644 --- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js @@ -782,7 +782,7 @@ describe( 'global styles renderer', () => { 'padding-bottom: 33px', 'padding-left: 33px', 'font-family: sans-serif', - 'font-size: clamp(10.5px, 0.65625rem + ((1vw - 7.68px) * 1.262), 21px)', + 'font-size: clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', ] ); } ); diff --git a/phpunit/block-supports/typography-test.php b/phpunit/block-supports/typography-test.php index 606904de72bf0b..6ecac215b0f61c 100644 --- a/phpunit/block-supports/typography-test.php +++ b/phpunit/block-supports/typography-test.php @@ -292,8 +292,8 @@ public function test_should_generate_classname_for_font_family() { * * @dataProvider data_generate_font_size_preset_fixtures * - * @param array $font_size_preset { - * Required. fontSizes preset value as seen in theme.json. + * @param array $font_size { + * Required. A font size as represented in the fontSizes preset format as seen in theme.json. * * @type string $name Name of the font size preset. * @type string $slug Kebab-case unique identifier for the font size preset. @@ -302,8 +302,8 @@ public function test_should_generate_classname_for_font_family() { * @param bool $should_use_fluid_typography An override to switch fluid typography "on". Can be used for unit testing. * @param string $expected_output Expected output of gutenberg_get_typography_font_size_value(). */ - public function test_gutenberg_get_typography_font_size_value( $font_size_preset, $should_use_fluid_typography, $expected_output ) { - $actual = gutenberg_get_typography_font_size_value( $font_size_preset, $should_use_fluid_typography ); + public function test_gutenberg_get_typography_font_size_value( $font_size, $should_use_fluid_typography, $expected_output ) { + $actual = gutenberg_get_typography_font_size_value( $font_size, $should_use_fluid_typography ); $this->assertSame( $expected_output, $actual ); } @@ -316,7 +316,7 @@ public function test_gutenberg_get_typography_font_size_value( $font_size_preset public function data_generate_font_size_preset_fixtures() { return array( 'default_return_value' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', ), 'should_use_fluid_typography' => false, @@ -324,7 +324,7 @@ public function data_generate_font_size_preset_fixtures() { ), 'size: int 0' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => 0, ), 'should_use_fluid_typography' => true, @@ -332,7 +332,7 @@ public function data_generate_font_size_preset_fixtures() { ), 'size: string 0' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '0', ), 'should_use_fluid_typography' => true, @@ -340,7 +340,7 @@ public function data_generate_font_size_preset_fixtures() { ), 'default_return_value_when_size_is_undefined' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => null, ), 'should_use_fluid_typography' => false, @@ -348,7 +348,7 @@ public function data_generate_font_size_preset_fixtures() { ), 'default_return_value_when_fluid_is_false' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => false, ), @@ -357,16 +357,16 @@ public function data_generate_font_size_preset_fixtures() { ), 'default_return_value_when_value_is_already_clamped' => array( - 'font_size_preset' => array( - 'size' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'font_size' => array( + 'size' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', 'fluid' => false, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', ), 'default_return_value_with_unsupported_unit' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '1000%', 'fluid' => false, ), @@ -375,57 +375,69 @@ public function data_generate_font_size_preset_fixtures() { ), 'return_fluid_value' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '1.75rem', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(1.3125rem, 1.3125rem + ((1vw - 0.48rem) * 2.524), 2.625rem)', + 'expected_output' => 'clamp(1.313rem, 1.313rem + ((1vw - 0.48rem) * 2.523), 2.625rem)', ), 'return_fluid_value_with_floats_with_units' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '100.175px', ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.13125px, 4.695703125rem + ((1vw - 7.68px) * 9.03), 150.2625px)', + 'expected_output' => 'clamp(75.131px, 4.696rem + ((1vw - 7.68px) * 9.03), 150.263px)', ), 'return_fluid_value_with_integer_coerced_to_px' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => 33, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(24.75px, 1.546875rem + ((1vw - 7.68px) * 2.975), 49.5px)', + 'expected_output' => 'clamp(24.75px, 1.547rem + ((1vw - 7.68px) * 2.975), 49.5px)', ), 'return_fluid_value_with_float_coerced_to_px' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => 100.23, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(75.1725px, 4.69828125rem + ((1vw - 7.68px) * 9.035), 150.345px)', + 'expected_output' => 'clamp(75.173px, 4.698rem + ((1vw - 7.68px) * 9.035), 150.345px)', ), 'return_default_fluid_values_with_empty_fluid_array' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => array(), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', ), 'return_default_fluid_values_with_null_value' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => null, ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 2.524), 42px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', + ), + + 'return_clamped_value_if_min_font_size_is_greater_than_max' => array( + 'font_size' => array( + 'size' => '3rem', + 'fluid' => array( + 'min' => '5rem', + 'max' => '32px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(5rem, 5rem + ((1vw - 0.48rem) * -5.769), 32px)', ), 'return_size_with_invalid_fluid_units' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '10em', 'fluid' => array( 'min' => '20vw', @@ -436,8 +448,16 @@ public function data_generate_font_size_preset_fixtures() { 'expected_output' => '10em', ), - 'return_fluid_clamp_value' => array( - 'font_size_preset' => array( + 'return_clamped_size_where_no_min_is_given_and_less_than_default_min_size' => array( + 'font_size' => array( + 'size' => '3px', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(3px, 0.188rem + ((1vw - 7.68px) * 0.18), 4.5px)', + ), + + 'return_fluid_clamp_value_with_different_min_max_units' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => array( 'min' => '20px', @@ -449,7 +469,7 @@ public function data_generate_font_size_preset_fixtures() { ), 'return_clamp_value_with_default_fluid_max_value' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => array( 'min' => '2.6rem', @@ -460,14 +480,84 @@ public function data_generate_font_size_preset_fixtures() { ), 'default_return_clamp_value_with_default_fluid_min_value' => array( - 'font_size_preset' => array( + 'font_size' => array( 'size' => '28px', 'fluid' => array( 'max' => '80px', ), ), 'should_use_fluid_typography' => true, - 'expected_output' => 'clamp(21px, 1.3125rem + ((1vw - 7.68px) * 7.091), 80px)', + 'expected_output' => 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 7.091), 80px)', + ), + + 'should_adjust_computed_min_in_px_to_min_limit' => array( + 'font_size' => array( + 'size' => '14px', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.841), 21px)', + ), + + 'should_adjust_computed_min_in_rem_to_min_limit' => array( + 'font_size' => array( + 'size' => '1.1rem', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 1.49), 1.65rem)', + ), + + 'default_return_clamp_value_with_replaced_fluid_min_value_in_em' => array( + 'font_size' => array( + 'size' => '1.1em', + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875em, 0.875rem + ((1vw - 0.48em) * 1.49), 1.65em)', + ), + + 'should_adjust_fluid_min_value_in_px_to_min_limit' => array( + 'font_size' => array( + 'size' => '20px', + 'fluid' => array( + 'min' => '12px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 1.923), 30px)', + ), + + 'should_adjust_fluid_min_value_in_rem_to_min_limit' => array( + 'font_size' => array( + 'size' => '1.5rem', + 'fluid' => array( + 'min' => '0.5rem', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 2.644), 2.25rem)', + ), + + 'should_adjust_fluid_min_value_but_honor_max_value' => array( + 'font_size' => array( + 'size' => '1.5rem', + 'fluid' => array( + 'min' => '0.5rem', + 'max' => '5rem', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(0.875rem, 0.875rem + ((1vw - 0.48rem) * 7.933), 5rem)', + ), + + 'should_return_fluid_value_when_min_and_max_font_sizes_are_equal' => array( + 'font_size' => array( + 'size' => '4rem', + 'fluid' => array( + 'min' => '30px', + 'max' => '30px', + ), + ), + 'should_use_fluid_typography' => true, + 'expected_output' => 'clamp(30px, 1.875rem + ((1vw - 7.68px) * 1), 30px)', ), ); } @@ -540,7 +630,7 @@ public function data_generate_block_supports_font_size_fixtures() { 'return_value_with_fluid_typography' => array( 'font_size_value' => '50px', 'should_use_fluid_typography' => true, - 'expected_output' => 'font-size:clamp(37.5px, 2.34375rem + ((1vw - 7.68px) * 4.507), 75px);', + 'expected_output' => 'font-size:clamp(37.5px, 2.344rem + ((1vw - 7.68px) * 4.507), 75px);', ), ); } @@ -616,13 +706,13 @@ public function data_generate_replace_inline_font_styles_with_fluid_values_fixtu 'block_content' => '

A paragraph inside a group

', 'font_size_value' => '20px', 'should_use_fluid_typography' => true, - 'expected_output' => '

A paragraph inside a group

', + 'expected_output' => '

A paragraph inside a group

', ), 'return_content_with_first_match_replace_only' => array( - 'block_content' => "
\n \n

A paragraph inside a group

", - 'font_size_value' => '1em', + 'block_content' => "
\n \n

A paragraph inside a group

", + 'font_size_value' => '1.5em', 'should_use_fluid_typography' => true, - 'expected_output' => "
\n \n

A paragraph inside a group

", + 'expected_output' => "
\n \n

A paragraph inside a group

", ), ); }