diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 2126aaf847fde..b1adaf1a679b8 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 7294a83da106f..2c6540f89d049 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 cd45c3593e1a4..aa268d04d7f1f 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 97ebeb583e497..e0c29a37ea981 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 48f21603ad78e..a6bd55ff86ec8 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 606904de72bf0..6ecac215b0f61 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' => "A paragraph inside a group
A paragraph inside a group
A paragraph inside a group
A paragraph inside a group