diff --git a/packages/block-editor/src/components/font-appearance-control/index.js b/packages/block-editor/src/components/font-appearance-control/index.js index 18e814ad23ddb4..418d43e322ac89 100644 --- a/packages/block-editor/src/components/font-appearance-control/index.js +++ b/packages/block-editor/src/components/font-appearance-control/index.js @@ -3,57 +3,12 @@ */ import { CustomSelectControl } from '@wordpress/components'; import { useMemo } from '@wordpress/element'; -import { __, _x, sprintf } from '@wordpress/i18n'; - -const FONT_STYLES = [ - { - name: _x( 'Regular', 'font style' ), - value: 'normal', - }, - { - name: _x( 'Italic', 'font style' ), - value: 'italic', - }, -]; - -const FONT_WEIGHTS = [ - { - name: _x( 'Thin', 'font weight' ), - value: '100', - }, - { - name: _x( 'Extra Light', 'font weight' ), - value: '200', - }, - { - name: _x( 'Light', 'font weight' ), - value: '300', - }, - { - name: _x( 'Regular', 'font weight' ), - value: '400', - }, - { - name: _x( 'Medium', 'font weight' ), - value: '500', - }, - { - name: _x( 'Semi Bold', 'font weight' ), - value: '600', - }, - { - name: _x( 'Bold', 'font weight' ), - value: '700', - }, - { - name: _x( 'Extra Bold', 'font weight' ), - value: '800', - }, - { - name: _x( 'Black', 'font weight' ), - value: '900', - }, -]; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { getFontStylesAndWeights } from '../../utils/get-font-styles-and-weights'; /** * Adjusts font appearance field label in case either font styles or weights @@ -76,7 +31,7 @@ const getFontAppearanceLabel = ( hasFontStyles, hasFontWeights ) => { }; /** - * Control to display unified font style and weight options. + * Control to display font style and weight options of the active font. * * @param {Object} props Component props. * @@ -87,6 +42,7 @@ export default function FontAppearanceControl( props ) { onChange, hasFontStyles = true, hasFontWeights = true, + fontFamilyFaces, value: { fontStyle, fontWeight }, ...otherProps } = props; @@ -97,43 +53,22 @@ export default function FontAppearanceControl( props ) { name: __( 'Default' ), style: { fontStyle: undefined, fontWeight: undefined }, }; + const { fontStyles, fontWeights, combinedStyleAndWeightOptions } = + getFontStylesAndWeights( fontFamilyFaces ); - // Combines both font style and weight options into a single dropdown. + // Generates select options for combined font styles and weights. const combineOptions = () => { const combinedOptions = [ defaultOption ]; - - FONT_STYLES.forEach( ( { name: styleName, value: styleValue } ) => { - FONT_WEIGHTS.forEach( - ( { name: weightName, value: weightValue } ) => { - const optionName = - styleValue === 'normal' - ? weightName - : sprintf( - /* translators: 1: Font weight name. 2: Font style name. */ - __( '%1$s %2$s' ), - weightName, - styleName - ); - - combinedOptions.push( { - key: `${ styleValue }-${ weightValue }`, - name: optionName, - style: { - fontStyle: styleValue, - fontWeight: weightValue, - }, - } ); - } - ); - } ); - + if ( combinedStyleAndWeightOptions ) { + combinedOptions.push( ...combinedStyleAndWeightOptions ); + } return combinedOptions; }; // Generates select options for font styles only. const styleOptions = () => { const combinedOptions = [ defaultOption ]; - FONT_STYLES.forEach( ( { name, value } ) => { + fontStyles.forEach( ( { name, value } ) => { combinedOptions.push( { key: value, name, @@ -146,7 +81,7 @@ export default function FontAppearanceControl( props ) { // Generates select options for font weights only. const weightOptions = () => { const combinedOptions = [ defaultOption ]; - FONT_WEIGHTS.forEach( ( { name, value } ) => { + fontWeights.forEach( ( { name, value } ) => { combinedOptions.push( { key: value, name, @@ -158,12 +93,19 @@ export default function FontAppearanceControl( props ) { // Map font styles and weights to select options. const selectOptions = useMemo( () => { + // Display combined available font style and weight options. if ( hasFontStyles && hasFontWeights ) { return combineOptions(); } + // Display only font style options or font weight options. return hasFontStyles ? styleOptions() : weightOptions(); - }, [ props.options ] ); + }, [ + props.options, + fontStyles, + fontWeights, + combinedStyleAndWeightOptions, + ] ); // Find current selection by comparing font style & weight against options, // and fall back to the Default option if there is no matching option. diff --git a/packages/block-editor/src/components/global-styles/test/typography-utils.js b/packages/block-editor/src/components/global-styles/test/typography-utils.js index bea8a5c0032757..115de8cdf2563d 100644 --- a/packages/block-editor/src/components/global-styles/test/typography-utils.js +++ b/packages/block-editor/src/components/global-styles/test/typography-utils.js @@ -4,6 +4,8 @@ import { getTypographyFontSizeValue, getFluidTypographyOptionsFromSettings, + getMergedFontFamiliesAndFontFamilyFaces, + findNearestFontWeight, } from '../typography-utils'; describe( 'typography utils', () => { @@ -682,6 +684,273 @@ describe( 'typography utils', () => { } ); } ); + describe( 'getMergedFontFamiliesAndFontFamilyFaces', () => { + [ + { + message: + 'should return empty arrays when settings and fontFamily are empty', + settings: {}, + fontFamily: '', + expected: { + fontFamilies: [], + fontFamilyFaces: [], + }, + }, + + { + message: + 'should return empty arrays when only settings is `undefined`', + settings: undefined, + fontFamily: 'ABeeZee, sans-serif', + expected: { + fontFamilies: [], + fontFamilyFaces: [], + }, + }, + + { + message: + 'should return fontFamilies array and an empty fontFamilyFaces array when fontfamily is empty', + settings: { + typography: { + fontFamilies: { + custom: [ + { + name: 'ABeeZee', + slug: 'abeezee', + fontFamily: 'ABeeZee, sans-serif', + fontFace: [ + { + src: 'http://www.wordpress.org/wp-content/uploads/fonts/esDT31xSG-6AGleN2tCkkJUCGpG-GQ.woff2', + fontWeight: '400', + fontStyle: 'italic', + fontFamily: 'ABeeZee', + }, + ], + }, + ], + }, + }, + }, + fontFamily: '', + expected: { + fontFamilies: [ + { + fontFace: [ + { + fontFamily: 'ABeeZee', + fontStyle: 'italic', + fontWeight: '400', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/esDT31xSG-6AGleN2tCkkJUCGpG-GQ.woff2', + }, + ], + fontFamily: 'ABeeZee, sans-serif', + name: 'ABeeZee', + slug: 'abeezee', + }, + ], + fontFamilyFaces: [], + }, + }, + + { + message: + 'should return font families and font faces when both settings and fontFamily are defined', + settings: { + typography: { + fontFamilies: { + theme: [ + { + fontFace: [ + { + fontFamily: 'PT Sans', + fontStyle: 'normal', + fontWeight: '400', + src: [ + 'file:./assets/fonts/pt-sans_normal_400.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'normal', + fontWeight: '700', + src: [ + 'file:./assets/fonts/pt-sans_normal_700.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'italic', + fontWeight: '400', + src: [ + 'file:./assets/fonts/pt-sans_italic_400.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'italic', + fontWeight: '700', + src: [ + 'file:./assets/fonts/pt-sans_italic_700.ttf', + ], + }, + ], + fontFamily: 'PT Sans', + name: 'PT Sans', + slug: 'pt-sans', + }, + ], + custom: [ + { + name: 'ABeeZee', + slug: 'abeezee', + fontFamily: 'ABeeZee, sans-serif', + fontFace: [ + { + src: 'http://www.wordpress.org/wp-content/uploads/fonts/esDT31xSG-6AGleN2tCkkJUCGpG-GQ.woff2', + fontWeight: '400', + fontStyle: 'italic', + fontFamily: 'ABeeZee', + }, + ], + }, + ], + }, + }, + }, + fontFamily: 'ABeeZee, sans-serif', + expected: { + fontFamilyFaces: [ + { + fontFamily: 'ABeeZee', + fontStyle: 'italic', + fontWeight: '400', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/esDT31xSG-6AGleN2tCkkJUCGpG-GQ.woff2', + }, + ], + fontFamilies: [ + { + fontFace: [ + { + fontFamily: 'PT Sans', + fontStyle: 'normal', + fontWeight: '400', + src: [ + 'file:./assets/fonts/pt-sans_normal_400.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'normal', + fontWeight: '700', + src: [ + 'file:./assets/fonts/pt-sans_normal_700.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'italic', + fontWeight: '400', + src: [ + 'file:./assets/fonts/pt-sans_italic_400.ttf', + ], + }, + { + fontFamily: 'PT Sans', + fontStyle: 'italic', + fontWeight: '700', + src: [ + 'file:./assets/fonts/pt-sans_italic_700.ttf', + ], + }, + ], + fontFamily: 'PT Sans', + name: 'PT Sans', + slug: 'pt-sans', + }, + { + fontFace: [ + { + fontFamily: 'ABeeZee', + fontStyle: 'italic', + fontWeight: '400', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/esDT31xSG-6AGleN2tCkkJUCGpG-GQ.woff2', + }, + ], + fontFamily: 'ABeeZee, sans-serif', + name: 'ABeeZee', + slug: 'abeezee', + }, + ], + }, + }, + ].forEach( ( { message, settings, fontFamily, expected } ) => { + it( `${ message }`, () => { + expect( + getMergedFontFamiliesAndFontFamilyFaces( + settings, + fontFamily + ) + ).toEqual( expected ); + } ); + } ); + } ); + + describe( 'findNearestFontWeight', () => { + [ + { + message: + 'should return empty string when newFontWeightValue is `undefined`', + availableFontWeights: undefined, + newFontWeightValue: undefined, + expected: '', + }, + { + message: + 'should return newFontWeightValue value when availableFontWeights is empty', + availableFontWeights: [], + newFontWeightValue: '300', + expected: '300', + }, + { + message: 'should return correct nearest higher font weight', + availableFontWeights: [ + { name: 'Regular', value: '400' }, + { name: 'Bold', value: '700' }, + { name: 'Black', value: '900' }, + ], + newFontWeightValue: '300', + expected: '400', + }, + { + message: 'should return correct nearest lower font weight', + availableFontWeights: [ + { name: 'Thin', value: '100' }, + { name: 'Light', value: '300' }, + { name: 'Regular', value: '400' }, + ], + newFontWeightValue: '900', + expected: '400', + }, + ].forEach( + ( { + message, + availableFontWeights, + newFontWeightValue, + expected, + } ) => { + it( `${ message }`, () => { + expect( + findNearestFontWeight( + availableFontWeights, + newFontWeightValue + ) + ).toEqual( expected ); + } ); + } + ); + } ); + describe( 'typography utils', () => { [ { 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 8f79841dd05dae..12ceadeb758df2 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -8,7 +8,7 @@ import { __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useCallback, useMemo } from '@wordpress/element'; +import { useCallback, useMemo, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -23,6 +23,11 @@ import TextDecorationControl from '../text-decoration-control'; import WritingModeControl from '../writing-mode-control'; import { getValueFromVariable, useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; +import { + getMergedFontFamiliesAndFontFamilyFaces, + findNearestFontWeight, +} from './typography-utils'; +import { getFontStylesAndWeights } from '../../utils/get-font-styles-and-weights'; const MIN_TEXT_COLUMNS = 1; const MAX_TEXT_COLUMNS = 6; @@ -180,15 +185,13 @@ export default function TypographyPanel( { // Font Family const hasFontFamilyEnabled = useHasFontFamilyControl( settings ); - const fontFamilies = settings?.typography?.fontFamilies; - const mergedFontFamilies = useMemo( () => { - return [ 'default', 'theme', 'custom' ].flatMap( - ( key ) => fontFamilies?.[ key ] ?? [] - ); - }, [ fontFamilies ] ); const fontFamily = decodeValue( inheritedValue?.typography?.fontFamily ); + const { fontFamilies, fontFamilyFaces } = useMemo( () => { + return getMergedFontFamiliesAndFontFamilyFaces( settings, fontFamily ); + }, [ settings, fontFamily ] ); + const setFontFamily = ( newValue ) => { - const slug = mergedFontFamilies?.find( + const slug = fontFamilies?.find( ( { fontFamily: f } ) => f === newValue )?.slug; onChange( @@ -252,6 +255,39 @@ export default function TypographyPanel( { setFontAppearance( {} ); }; + // Check if previous font style and weight values are available in the new font family + useEffect( () => { + const { fontStyles, fontWeights, isSystemFont } = + getFontStylesAndWeights( fontFamilyFaces ); + const hasFontStyle = fontStyles?.some( + ( { value: fs } ) => fs === fontStyle + ); + const hasFontWeight = fontWeights?.some( + ( { value: fw } ) => fw === fontWeight + ); + + // Try to set nearest available font weight + if ( ! hasFontWeight && fontWeight ) { + setFontAppearance( { + fontStyle, + fontWeight: findNearestFontWeight( fontWeights, fontWeight ), + } ); + } + + // Set the same weight and style values if the font family is a system font or if both are the same + if ( isSystemFont || ( hasFontStyle && hasFontWeight ) ) { + setFontAppearance( { + fontStyle, + fontWeight, + } ); + } + + // Reset font appearance if the font family does not have the selected font style + if ( ! hasFontStyle ) { + resetFontAppearance(); + } + }, [ fontFamily ] ); + // Line Height const hasLineHeightEnabled = useHasLineHeightControl( settings ); const lineHeight = decodeValue( inheritedValue?.typography?.lineHeight ); @@ -387,7 +423,7 @@ export default function TypographyPanel( { panelId={ panelId } > diff --git a/packages/block-editor/src/components/global-styles/typography-utils.js b/packages/block-editor/src/components/global-styles/typography-utils.js index 80a86a1034caeb..0d4c1b29b8b662 100644 --- a/packages/block-editor/src/components/global-styles/typography-utils.js +++ b/packages/block-editor/src/components/global-styles/typography-utils.js @@ -122,3 +122,66 @@ export function getFluidTypographyOptionsFromSettings( settings ) { fluid: typographySettings?.fluid, }; } + +/** + * Returns an object of merged font families and the font faces from the selected font family + * based on the theme.json settings object and the currently selected font family. + * + * @param {Object} settings Theme.json settings + * @param {string} selectedFontFamily Decoded font family string + * @return {Object} Merged font families and font faces from the selected font family + */ +export function getMergedFontFamiliesAndFontFamilyFaces( + settings, + selectedFontFamily +) { + const fontFamiliesFromSettings = settings?.typography?.fontFamilies; + + const fontFamilies = [ 'default', 'theme', 'custom' ].flatMap( + ( key ) => fontFamiliesFromSettings?.[ key ] ?? [] + ); + + const fontFamilyFaces = + fontFamilies.find( + ( family ) => family.fontFamily === selectedFontFamily + )?.fontFace ?? []; + + return { fontFamilies, fontFamilyFaces }; +} + +/** + * Returns the nearest font weight value from the available font weight list based on the new font weight. + * The nearest font weight is the one with the smallest difference from the new font weight. + * + * @param {Array} availableFontWeights Array of available font weights + * @param {string} newFontWeightValue New font weight value + * @return {string} Nearest font weight + */ + +export function findNearestFontWeight( + availableFontWeights, + newFontWeightValue +) { + if ( ! newFontWeightValue || typeof newFontWeightValue !== 'string' ) { + return ''; + } + + if ( ! availableFontWeights || availableFontWeights.length === 0 ) { + return newFontWeightValue; + } + + const nearestFontWeight = availableFontWeights?.reduce( + ( nearest, { value: fw } ) => { + const currentDiff = Math.abs( + parseInt( fw ) - parseInt( newFontWeightValue ) + ); + const nearestDiff = Math.abs( + parseInt( nearest ) - parseInt( newFontWeightValue ) + ); + return currentDiff < nearestDiff ? fw : nearest; + }, + availableFontWeights[ 0 ]?.value + ); + + return nearestFontWeight; +} diff --git a/packages/block-editor/src/utils/format-font-style.js b/packages/block-editor/src/utils/format-font-style.js new file mode 100644 index 00000000000000..5db52f8a5808f5 --- /dev/null +++ b/packages/block-editor/src/utils/format-font-style.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { _x } from '@wordpress/i18n'; + +/** + * Formats font styles to human readable names. + * + * @param {string} fontStyle font style string + * @return {Object} new object with formatted font style + */ +export function formatFontStyle( fontStyle ) { + if ( ! fontStyle ) { + return {}; + } + + if ( typeof fontStyle === 'object' ) { + return fontStyle; + } + + let name; + + switch ( fontStyle ) { + case 'normal': + name = _x( 'Regular', 'font style' ); + break; + case 'italic': + name = _x( 'Italic', 'font style' ); + break; + case 'oblique': + name = _x( 'Oblique', 'font style' ); + break; + + default: + name = fontStyle; + break; + } + + return { name, value: fontStyle }; +} diff --git a/packages/block-editor/src/utils/format-font-weight.js b/packages/block-editor/src/utils/format-font-weight.js new file mode 100644 index 00000000000000..8fc1c9e479a23d --- /dev/null +++ b/packages/block-editor/src/utils/format-font-weight.js @@ -0,0 +1,63 @@ +/** + * WordPress dependencies + */ +import { _x } from '@wordpress/i18n'; + +/** + * Formats font weights to human readable names. + * + * @param {string} fontWeight font weight string + * @return {Object} new object with formatted font weight + */ +export function formatFontWeight( fontWeight ) { + if ( ! fontWeight ) { + return {}; + } + + if ( typeof fontWeight === 'object' ) { + return fontWeight; + } + + let name; + + switch ( fontWeight ) { + case 'normal': + case '400': + name = _x( 'Regular', 'font weight' ); + break; + case 'bold': + case '700': + name = _x( 'Bold', 'font weight' ); + break; + case '100': + name = _x( 'Thin', 'font weight' ); + break; + case '200': + name = _x( 'Extra Light', 'font weight' ); + break; + case '300': + name = _x( 'Light', 'font weight' ); + break; + case '500': + name = _x( 'Medium', 'font weight' ); + break; + case '600': + name = _x( 'Semi Bold', 'font weight' ); + break; + case '800': + name = _x( 'Extra Bold', 'font weight' ); + break; + case '900': + name = _x( 'Black', 'font weight' ); + break; + case '1000': + name = _x( 'Extra Black', 'font weight' ); + break; + + default: + name = fontWeight; + break; + } + + return { name, value: fontWeight }; +} diff --git a/packages/block-editor/src/utils/get-font-styles-and-weights.js b/packages/block-editor/src/utils/get-font-styles-and-weights.js new file mode 100644 index 00000000000000..88b40a5aacfeb5 --- /dev/null +++ b/packages/block-editor/src/utils/get-font-styles-and-weights.js @@ -0,0 +1,191 @@ +/** + * WordPress dependencies + */ +import { _x, __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { formatFontStyle } from './format-font-style'; +import { formatFontWeight } from './format-font-weight'; + +const FONT_STYLES = [ + { + name: _x( 'Regular', 'font style' ), + value: 'normal', + }, + { + name: _x( 'Italic', 'font style' ), + value: 'italic', + }, +]; + +const FONT_WEIGHTS = [ + { + name: _x( 'Thin', 'font weight' ), + value: '100', + }, + { + name: _x( 'Extra Light', 'font weight' ), + value: '200', + }, + { + name: _x( 'Light', 'font weight' ), + value: '300', + }, + { + name: _x( 'Regular', 'font weight' ), + value: '400', + }, + { + name: _x( 'Medium', 'font weight' ), + value: '500', + }, + { + name: _x( 'Semi Bold', 'font weight' ), + value: '600', + }, + { + name: _x( 'Bold', 'font weight' ), + value: '700', + }, + { + name: _x( 'Extra Bold', 'font weight' ), + value: '800', + }, + { + name: _x( 'Black', 'font weight' ), + value: '900', + }, + { + name: _x( 'Extra Black', 'font weight' ), + value: '1000', + }, +]; + +/** + * Builds a list of font style and weight options based on font family faces. + * Defaults to the standard font styles and weights if no font family faces are provided. + * + * @param {Array} fontFamilyFaces font family faces array + * @return {Object} new object with combined and separated font style and weight properties + */ +export function getFontStylesAndWeights( fontFamilyFaces ) { + let fontStyles = []; + let fontWeights = []; + const combinedStyleAndWeightOptions = []; + const isSystemFont = ! fontFamilyFaces || fontFamilyFaces?.length === 0; + let isVariableFont = false; + + fontFamilyFaces?.forEach( ( face ) => { + // Check for variable font by looking for a space in the font weight value. e.g. "100 900" + if ( /\s/.test( face.fontWeight.trim() ) ) { + isVariableFont = true; + + // Find font weight start and end values. + let [ startValue, endValue ] = face.fontWeight.split( ' ' ); + startValue = parseInt( startValue.slice( 0, 1 ) ); + if ( endValue === '1000' ) { + endValue = 10; + } else { + endValue = parseInt( endValue.slice( 0, 1 ) ); + } + + // Create font weight options for available variable weights. + for ( let i = startValue; i <= endValue; i++ ) { + const fontWeightValue = `${ i.toString() }00`; + if ( + ! fontWeights.some( + ( weight ) => weight.value === fontWeightValue + ) + ) { + fontWeights.push( formatFontWeight( fontWeightValue ) ); + } + } + } + + // Format font style and weight values. + const fontWeight = formatFontWeight( face.fontWeight ); + const fontStyle = formatFontStyle( face.fontStyle ); + + // Create font style and font weight lists without duplicates. + if ( fontStyle ) { + if ( + ! fontStyles.some( + ( style ) => style.value === fontStyle.value + ) + ) { + fontStyles.push( fontStyle ); + } + } + if ( fontWeight ) { + if ( + ! fontWeights.some( + ( weight ) => weight.value === fontWeight.value + ) + ) { + if ( ! isVariableFont ) { + fontWeights.push( fontWeight ); + } + } + } + } ); + + // If there is no font weight of 600 or above, then include faux bold as an option. + if ( ! fontWeights.some( ( weight ) => weight.value >= '600' ) ) { + fontWeights.push( { + name: _x( 'Bold', 'font weight' ), + value: '700', + } ); + } + + // If there is no italic font style, then include faux italic as an option. + if ( ! fontStyles.some( ( style ) => style.value === 'italic' ) ) { + fontStyles.push( { + name: _x( 'Italic', 'font style' ), + value: 'italic', + } ); + } + + // Use default font styles and weights for system fonts. + if ( isSystemFont ) { + fontStyles = FONT_STYLES; + fontWeights = FONT_WEIGHTS; + } + + // Use default styles and weights if there are no available styles or weights from the provided font faces. + fontStyles = fontStyles.length === 0 ? FONT_STYLES : fontStyles; + fontWeights = fontWeights.length === 0 ? FONT_WEIGHTS : fontWeights; + + // Generate combined font style and weight options for available fonts. + fontStyles.forEach( ( { name: styleName, value: styleValue } ) => { + fontWeights.forEach( ( { name: weightName, value: weightValue } ) => { + const optionName = + styleValue === 'normal' + ? weightName + : sprintf( + /* translators: 1: Font weight name. 2: Font style name. */ + __( '%1$s %2$s' ), + weightName, + styleName + ); + + combinedStyleAndWeightOptions.push( { + key: `${ styleValue }-${ weightValue }`, + name: optionName, + style: { + fontStyle: styleValue, + fontWeight: weightValue, + }, + } ); + } ); + } ); + + return { + fontStyles, + fontWeights, + combinedStyleAndWeightOptions, + isSystemFont, + isVariableFont, + }; +} diff --git a/packages/block-editor/src/utils/test/format-font-style.js b/packages/block-editor/src/utils/test/format-font-style.js new file mode 100644 index 00000000000000..f8557143cb4e24 --- /dev/null +++ b/packages/block-editor/src/utils/test/format-font-style.js @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +import { formatFontStyle } from '../format-font-style'; + +describe( 'formatFontStyle', () => { + it( 'should return empty object if style is not available', () => { + expect( formatFontStyle() ).toEqual( {} ); + } ); + + it( 'should return the same object if style is already an object', () => { + const fontStyle = { name: 'Italic', value: 'italic' }; + expect( formatFontStyle( fontStyle ) ).toEqual( fontStyle ); + } ); + + it( 'should return the formatted font style', () => { + expect( formatFontStyle( 'normal' ) ).toEqual( { + name: 'Regular', + value: 'normal', + } ); + expect( formatFontStyle( 'italic' ) ).toEqual( { + name: 'Italic', + value: 'italic', + } ); + expect( formatFontStyle( 'oblique' ) ).toEqual( { + name: 'Oblique', + value: 'oblique', + } ); + expect( formatFontStyle( 'oblique 40deg' ) ).toEqual( { + name: 'oblique 40deg', + value: 'oblique 40deg', + } ); + } ); +} ); diff --git a/packages/block-editor/src/utils/test/format-font-weight.js b/packages/block-editor/src/utils/test/format-font-weight.js new file mode 100644 index 00000000000000..8830e45532acfe --- /dev/null +++ b/packages/block-editor/src/utils/test/format-font-weight.js @@ -0,0 +1,66 @@ +/** + * Internal dependencies + */ +import { formatFontWeight } from '../format-font-weight'; + +describe( 'formatFontWeight', () => { + it( 'should return empty object if weight is not available', () => { + expect( formatFontWeight() ).toEqual( {} ); + } ); + + it( 'should return the same object if weight is already an object', () => { + const fontWeight = { name: 'Thin', value: '100' }; + expect( formatFontWeight( fontWeight ) ).toEqual( fontWeight ); + } ); + + it( 'should return the formatted font weight', () => { + expect( formatFontWeight( '100' ) ).toEqual( { + name: 'Thin', + value: '100', + } ); + expect( formatFontWeight( '200' ) ).toEqual( { + name: 'Extra Light', + value: '200', + } ); + expect( formatFontWeight( '300' ) ).toEqual( { + name: 'Light', + value: '300', + } ); + expect( formatFontWeight( '400' ) ).toEqual( { + name: 'Regular', + value: '400', + } ); + expect( formatFontWeight( '500' ) ).toEqual( { + name: 'Medium', + value: '500', + } ); + expect( formatFontWeight( '600' ) ).toEqual( { + name: 'Semi Bold', + value: '600', + } ); + expect( formatFontWeight( '700' ) ).toEqual( { + name: 'Bold', + value: '700', + } ); + expect( formatFontWeight( '800' ) ).toEqual( { + name: 'Extra Bold', + value: '800', + } ); + expect( formatFontWeight( '900' ) ).toEqual( { + name: 'Black', + value: '900', + } ); + expect( formatFontWeight( '1000' ) ).toEqual( { + name: 'Extra Black', + value: '1000', + } ); + expect( formatFontWeight( 'normal' ) ).toEqual( { + name: 'Regular', + value: 'normal', + } ); + expect( formatFontWeight( 'bold' ) ).toEqual( { + name: 'Bold', + value: 'bold', + } ); + } ); +} ); diff --git a/packages/block-editor/src/utils/test/get-font-styles-and-weights.js b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js new file mode 100644 index 00000000000000..ffc1a635aa1fb1 --- /dev/null +++ b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js @@ -0,0 +1,513 @@ +/** + * Internal dependencies + */ +import { getFontStylesAndWeights } from '../get-font-styles-and-weights'; + +describe( 'getFontStylesAndWeights', () => { + it( 'should return default styles and weights if fontFamilyFaces is not available', () => { + expect( getFontStylesAndWeights() ).toEqual( { + fontStyles: [ + { + name: 'Regular', + value: 'normal', + }, + { + name: 'Italic', + value: 'italic', + }, + ], + fontWeights: [ + { + name: 'Thin', + value: '100', + }, + { + name: 'Extra Light', + value: '200', + }, + { + name: 'Light', + value: '300', + }, + { + name: 'Regular', + value: '400', + }, + { + name: 'Medium', + value: '500', + }, + { + name: 'Semi Bold', + value: '600', + }, + { + name: 'Bold', + value: '700', + }, + { + name: 'Extra Bold', + value: '800', + }, + { + name: 'Black', + value: '900', + }, + { + name: 'Extra Black', + value: '1000', + }, + ], + combinedStyleAndWeightOptions: [ + { + key: 'normal-100', + name: 'Thin', + style: { + fontStyle: 'normal', + fontWeight: '100', + }, + }, + { + key: 'normal-200', + name: 'Extra Light', + style: { + fontStyle: 'normal', + fontWeight: '200', + }, + }, + { + key: 'normal-300', + name: 'Light', + style: { + fontStyle: 'normal', + fontWeight: '300', + }, + }, + { + key: 'normal-400', + name: 'Regular', + style: { + fontStyle: 'normal', + fontWeight: '400', + }, + }, + { + key: 'normal-500', + name: 'Medium', + style: { + fontStyle: 'normal', + fontWeight: '500', + }, + }, + { + key: 'normal-600', + name: 'Semi Bold', + style: { + fontStyle: 'normal', + fontWeight: '600', + }, + }, + { + key: 'normal-700', + name: 'Bold', + style: { + fontStyle: 'normal', + fontWeight: '700', + }, + }, + { + key: 'normal-800', + name: 'Extra Bold', + style: { + fontStyle: 'normal', + fontWeight: '800', + }, + }, + { + key: 'normal-900', + name: 'Black', + style: { + fontStyle: 'normal', + fontWeight: '900', + }, + }, + { + key: 'normal-1000', + name: 'Extra Black', + style: { + fontStyle: 'normal', + fontWeight: '1000', + }, + }, + { + key: 'italic-100', + name: 'Thin Italic', + style: { + fontStyle: 'italic', + fontWeight: '100', + }, + }, + { + key: 'italic-200', + name: 'Extra Light Italic', + style: { + fontStyle: 'italic', + fontWeight: '200', + }, + }, + { + key: 'italic-300', + name: 'Light Italic', + style: { + fontStyle: 'italic', + fontWeight: '300', + }, + }, + { + key: 'italic-400', + name: 'Regular Italic', + style: { + fontStyle: 'italic', + fontWeight: '400', + }, + }, + { + key: 'italic-500', + name: 'Medium Italic', + style: { + fontStyle: 'italic', + fontWeight: '500', + }, + }, + { + key: 'italic-600', + name: 'Semi Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '600', + }, + }, + { + key: 'italic-700', + name: 'Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '700', + }, + }, + { + key: 'italic-800', + name: 'Extra Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '800', + }, + }, + { + key: 'italic-900', + name: 'Black Italic', + style: { + fontStyle: 'italic', + fontWeight: '900', + }, + }, + { + key: 'italic-1000', + name: 'Extra Black Italic', + style: { + fontStyle: 'italic', + fontWeight: '1000', + }, + }, + ], + isSystemFont: true, + isVariableFont: false, + } ); + } ); + + it( 'should return available styles and weights for a variable font', () => { + const fontFamilyFaces = [ + { + fontFamily: 'AR One Sans', + fontStyle: 'normal', + fontWeight: '400 700', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/AROneSans-VariableFont_ARRRwght.ttf', + }, + ]; + + expect( getFontStylesAndWeights( fontFamilyFaces ) ).toEqual( { + fontStyles: [ + { + name: 'Regular', + value: 'normal', + }, + { + name: 'Italic', + value: 'italic', + }, + ], + fontWeights: [ + { + name: 'Regular', + value: '400', + }, + { + name: 'Medium', + value: '500', + }, + { + name: 'Semi Bold', + value: '600', + }, + { + name: 'Bold', + value: '700', + }, + ], + combinedStyleAndWeightOptions: [ + { + key: 'normal-400', + name: 'Regular', + style: { + fontStyle: 'normal', + fontWeight: '400', + }, + }, + { + key: 'normal-500', + name: 'Medium', + style: { + fontStyle: 'normal', + fontWeight: '500', + }, + }, + { + key: 'normal-600', + name: 'Semi Bold', + style: { + fontStyle: 'normal', + fontWeight: '600', + }, + }, + { + key: 'normal-700', + name: 'Bold', + style: { + fontStyle: 'normal', + fontWeight: '700', + }, + }, + { + key: 'italic-400', + name: 'Regular Italic', + style: { + fontStyle: 'italic', + fontWeight: '400', + }, + }, + { + key: 'italic-500', + name: 'Medium Italic', + style: { + fontStyle: 'italic', + fontWeight: '500', + }, + }, + { + key: 'italic-600', + name: 'Semi Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '600', + }, + }, + { + key: 'italic-700', + name: 'Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '700', + }, + }, + ], + isSystemFont: false, + isVariableFont: true, + } ); + } ); + + it( 'should return available styles and weights for a regular font', () => { + const fontFamilyFaces = [ + { + fontFamily: 'Piazzolla', + fontStyle: 'normal', + fontWeight: '400', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7LYxnL31AHfAAy5.woff2', + }, + { + fontFamily: 'Piazzolla', + fontStyle: 'normal', + fontWeight: '900', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7JxwXL31AHfAAy5.woff2', + }, + { + fontFamily: 'Piazzolla', + fontStyle: 'italic', + fontWeight: '300', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhcSx3gD9BRy5m5M.woff2', + }, + { + fontFamily: 'Piazzolla', + fontStyle: 'italic', + fontWeight: '900', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhTO23gD9BRy5m5M.woff2', + }, + ]; + expect( getFontStylesAndWeights( fontFamilyFaces ) ).toEqual( { + fontStyles: [ + { + name: 'Regular', + value: 'normal', + }, + { + name: 'Italic', + value: 'italic', + }, + ], + fontWeights: [ + { + name: 'Regular', + value: '400', + }, + { + name: 'Black', + value: '900', + }, + { + name: 'Light', + value: '300', + }, + ], + combinedStyleAndWeightOptions: [ + { + key: 'normal-400', + name: 'Regular', + style: { + fontStyle: 'normal', + fontWeight: '400', + }, + }, + { + key: 'normal-900', + name: 'Black', + style: { + fontStyle: 'normal', + fontWeight: '900', + }, + }, + { + key: 'normal-300', + name: 'Light', + style: { + fontStyle: 'normal', + fontWeight: '300', + }, + }, + { + key: 'italic-400', + name: 'Regular Italic', + style: { + fontStyle: 'italic', + fontWeight: '400', + }, + }, + { + key: 'italic-900', + name: 'Black Italic', + style: { + fontStyle: 'italic', + fontWeight: '900', + }, + }, + { + key: 'italic-300', + name: 'Light Italic', + style: { + fontStyle: 'italic', + fontWeight: '300', + }, + }, + ], + isSystemFont: false, + isVariableFont: false, + } ); + } ); + + it( 'should return available styles and weights for a regular font with faux bold added', () => { + const fontFamilyFaces = [ + { + fontFamily: 'Piazzolla', + fontStyle: 'normal', + fontWeight: '400', + src: 'http://www.wordpress.org/wp-content/uploads/fonts/N0b52SlTPu5rIkWIZjVKKtYtfxYqZ4RJBFzFfYUjkSDdlqZgy7LYxnL31AHfAAy5.woff2', + }, + ]; + expect( getFontStylesAndWeights( fontFamilyFaces ) ).toEqual( { + fontStyles: [ + { + name: 'Regular', + value: 'normal', + }, + { + name: 'Italic', + value: 'italic', + }, + ], + fontWeights: [ + { + name: 'Regular', + value: '400', + }, + { + name: 'Bold', + value: '700', + }, + ], + combinedStyleAndWeightOptions: [ + { + key: 'normal-400', + name: 'Regular', + style: { + fontStyle: 'normal', + fontWeight: '400', + }, + }, + { + key: 'normal-700', + name: 'Bold', + style: { + fontStyle: 'normal', + fontWeight: '700', + }, + }, + { + key: 'italic-400', + name: 'Regular Italic', + style: { + fontStyle: 'italic', + fontWeight: '400', + }, + }, + { + key: 'italic-700', + name: 'Bold Italic', + style: { + fontStyle: 'italic', + fontWeight: '700', + }, + }, + ], + isSystemFont: false, + isVariableFont: false, + } ); + } ); +} );