diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index abd247003f9b6f..0afd4dc411ccb8 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -698,7 +698,7 @@ Help visitors find your content. ([Source](https://github.com/WordPress/gutenber - **Name:** core/search - **Category:** widgets -- **Supports:** align (center, left, right), color (background, gradients, text), ~~html~~ +- **Supports:** align (center, left, right), color (background, gradients, text), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** buttonPosition, buttonText, buttonUseIcon, label, placeholder, query, showLabel, width, widthUnit ## Separator diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index c2b8ebc4efd714..ccdb5b9e375951 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -474,6 +474,19 @@ _Returns_ - `string`: returns the cssUnit value in a simple px format. +### getTypographyClassesAndStyles + +Provides the CSS class names and inline styles for a block's typography support +attributes. + +_Parameters_ + +- _attributes_ `Object`: Block attributes. + +_Returns_ + +- `Object`: Typography block support derived CSS classes & styles. + ### InnerBlocks _Related_ diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 5a07d864beb620..3ed81af7cda60c 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -21,5 +21,6 @@ export { useCustomSides } from './dimensions'; export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; export { getColorClassesAndStyles, useColorProps } from './use-color-props'; export { getSpacingClassesAndStyles } from './use-spacing-props'; +export { getTypographyClassesAndStyles } from './use-typography-props'; export { getGapCSSValue } from './gap'; export { useCachedTruthy } from './use-cached-truthy'; diff --git a/packages/block-editor/src/hooks/test/use-typography-props.js b/packages/block-editor/src/hooks/test/use-typography-props.js new file mode 100644 index 00000000000000..93e4de29bc7348 --- /dev/null +++ b/packages/block-editor/src/hooks/test/use-typography-props.js @@ -0,0 +1,28 @@ +/** + * Internal dependencies + */ +import { getTypographyClassesAndStyles } from '../use-typography-props'; + +describe( 'getTypographyClassesAndStyles', () => { + it( 'should return styles and classes', () => { + const attributes = { + fontFamily: 'tofu', + fontSize: 'large', + style: { + typography: { + letterSpacing: '22px', + fontSize: '2rem', + textTransform: 'uppercase', + }, + }, + }; + expect( getTypographyClassesAndStyles( attributes ) ).toEqual( { + className: 'has-tofu-font-family has-large-font-size', + style: { + letterSpacing: '22px', + fontSize: '2rem', + textTransform: 'uppercase', + }, + } ); + } ); +} ); diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js new file mode 100644 index 00000000000000..d08105d8d90c1e --- /dev/null +++ b/packages/block-editor/src/hooks/use-typography-props.js @@ -0,0 +1,41 @@ +/** + * External dependencies + */ +import { kebabCase } from 'lodash'; +import classnames from 'classnames'; + +/** + * Internal dependencies + */ +import { getInlineStyles } from './style'; +import { getFontSizeClass } from '../components/font-sizes'; + +// This utility is intended to assist where the serialization of the typography +// block support is being skipped for a block but the typography related CSS +// styles still need to be generated so they can be applied to inner elements. + +/** + * Provides the CSS class names and inline styles for a block's typography support + * attributes. + * + * @param {Object} attributes Block attributes. + * + * @return {Object} Typography block support derived CSS classes & styles. + */ +export function getTypographyClassesAndStyles( attributes ) { + const typographyStyles = attributes?.style?.typography || {}; + const style = getInlineStyles( { typography: typographyStyles } ); + const fontFamilyClassName = !! attributes?.fontFamily + ? `has-${ kebabCase( attributes.fontFamily ) }-font-family` + : ''; + + const className = classnames( + fontFamilyClassName, + getFontSizeClass( attributes?.fontSize ) + ); + + return { + className, + style, + }; +} diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js index c3d55ce8962c7a..ec0f20a8f9c892 100644 --- a/packages/block-editor/src/index.js +++ b/packages/block-editor/src/index.js @@ -6,6 +6,7 @@ export { getBorderClassesAndStyles as __experimentalGetBorderClassesAndStyles, useBorderProps as __experimentalUseBorderProps, getColorClassesAndStyles as __experimentalGetColorClassesAndStyles, + getTypographyClassesAndStyles, useColorProps as __experimentalUseColorProps, useCustomSides as __experimentalUseCustomSides, getSpacingClassesAndStyles as __experimentalGetSpacingClassesAndStyles, diff --git a/packages/block-library/src/search/block.json b/packages/block-library/src/search/block.json index cb9ae01a648bcc..fbd0fa874c4087 100644 --- a/packages/block-library/src/search/block.json +++ b/packages/block-library/src/search/block.json @@ -54,6 +54,21 @@ "text": true } }, + "typography": { + "__experimentalSkipSerialization": true, + "__experimentalSelector": ".wp-block-search__label, .wp-block-search__input, .wp-block-search__button", + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, "__experimentalBorder": { "color": true, "radius": true, diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index 0206986961cef6..807dda04e743e4 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -13,6 +13,7 @@ import { RichText, __experimentalUseBorderProps as useBorderProps, __experimentalUseColorProps as useColorProps, + getTypographyClassesAndStyles as useTypographyProps, store as blockEditorStore, __experimentalGetElementClassName, } from '@wordpress/block-editor'; @@ -112,6 +113,7 @@ export default function SearchEdit( { } const colorProps = useColorProps( attributes ); + const typographyProps = useTypographyProps( attributes ); const unitControlInstanceId = useInstanceId( UnitControl ); const unitControlInputId = `wp-block-search__width-${ unitControlInstanceId }`; const isButtonPositionInside = 'button-inside' === buttonPosition; @@ -208,11 +210,16 @@ export default function SearchEdit( { // If the input is inside the wrapper, the wrapper gets the border color styles/classes, not the input control. const textFieldClasses = classnames( 'wp-block-search__input', - isButtonPositionInside ? undefined : borderProps.className + isButtonPositionInside ? undefined : borderProps.className, + typographyProps.className ); - const textFieldStyles = isButtonPositionInside - ? { borderRadius } - : borderProps.style; + const textFieldStyles = { + ...( isButtonPositionInside + ? { borderRadius } + : borderProps.style ), + ...typographyProps.style, + textDecoration: undefined, + }; return ( { controls } { showLabel && ( setAttributes( { label: html } ) } + style={ typographyProps.style } /> ) } diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index d056da16d958a5..8e2a158969acb4 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -37,6 +37,7 @@ function render_block_core_search( $attributes ) { $query_params_markup = ''; $inline_styles = styles_for_block_core_search( $attributes ); $color_classes = get_color_classes_for_block_core_search( $attributes ); + $typography_classes = get_typography_classes_for_block_core_search( $attributes ); $is_button_inside = ! empty( $attributes['buttonPosition'] ) && 'button-inside' === $attributes['buttonPosition']; // Border color classes need to be applied to the elements that have a border color. @@ -50,19 +51,31 @@ function render_block_core_search( $attributes ) { $label_inner_html ); if ( $show_label && ! empty( $attributes['label'] ) ) { + $label_classes = array( 'wp-block-search__label' ); + if ( ! empty( $typography_classes ) ) { + $label_classes[] = $typography_classes; + } $label_markup = sprintf( - '', - $input_id, + '', + esc_attr( $input_id ), + esc_attr( implode( ' ', $label_classes ) ), + $inline_styles['label'], $label_inner_html ); } if ( $show_input ) { - $input_classes = ! $is_button_inside ? $border_color_classes : ''; - $input_markup = sprintf( + $input_classes = array( 'wp-block-search__input' ); + if ( $is_button_inside ) { + $input_classes[] = $border_color_classes; + } + if ( ! empty( $typography_classes ) ) { + $input_classes[] = $typography_classes; + } + $input_markup = sprintf( '', $input_id, - esc_attr( $input_classes ), + esc_attr( implode( ' ', $input_classes ) ), get_search_query(), esc_attr( $attributes['placeholder'] ), $inline_styles['input'] @@ -80,11 +93,14 @@ function render_block_core_search( $attributes ) { } if ( $show_button ) { - $button_classes = array(); + $button_classes = array( 'wp-block-search__button' ); $button_internal_markup = ''; if ( ! empty( $color_classes ) ) { $button_classes[] = $color_classes; } + if ( ! empty( $typography_classes ) ) { + $button_classes[] = $typography_classes; + } $aria_label = ''; if ( ! $is_button_inside && ! empty( $border_color_classes ) ) { @@ -107,7 +123,7 @@ function render_block_core_search( $attributes ) { // Include the button element class. $button_classes[] = WP_Theme_JSON_Gutenberg::get_element_class_name( 'button' ); $button_markup = sprintf( - '', + '', esc_attr( implode( ' ', $button_classes ) ), $inline_styles['button'], $aria_label, @@ -122,7 +138,9 @@ function render_block_core_search( $attributes ) { $inline_styles['wrapper'], $input_markup . $query_params_markup . $button_markup ); - $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classnames ) ); + $wrapper_attributes = get_block_wrapper_attributes( + array( 'class' => $classnames ) + ); return sprintf( '
%s
', @@ -270,8 +288,10 @@ function styles_for_block_core_search( $attributes ) { $wrapper_styles = array(); $button_styles = array(); $input_styles = array(); + $label_styles = array(); $is_button_inside = ! empty( $attributes['buttonPosition'] ) && 'button-inside' === $attributes['buttonPosition']; + $show_label = ( isset( $attributes['showLabel'] ) ) && false !== $attributes['showLabel']; // Add width styles. $has_width = ! empty( $attributes['width'] ) && ! empty( $attributes['widthUnit'] ); @@ -360,13 +380,98 @@ function styles_for_block_core_search( $attributes ) { $button_styles[] = sprintf( 'background: %s;', $attributes['style']['color']['gradient'] ); } + // Get typography styles to be shared across inner elements. + $typography_styles = get_typography_styles_for_block_core_search( $attributes ); + if ( ! empty( $typography_styles ) ) { + $label_styles [] = $typography_styles; + $button_styles[] = $typography_styles; + $input_styles [] = $typography_styles; + } + + // Typography text-decoration is only applied to the label and button. + if ( ! empty( $attributes['style']['typography']['textDecoration'] ) ) { + $text_decoration_value = sprintf( 'text-decoration: %s;', esc_attr( $attributes['style']['typography']['textDecoration'] ) ); + $button_styles[] = $text_decoration_value; + // Input opts out of text decoration. + if ( $show_label ) { + $label_styles[] = $text_decoration_value; + } + } + return array( 'input' => ! empty( $input_styles ) ? sprintf( ' style="%s"', safecss_filter_attr( implode( ' ', $input_styles ) ) ) : '', 'button' => ! empty( $button_styles ) ? sprintf( ' style="%s"', safecss_filter_attr( implode( ' ', $button_styles ) ) ) : '', 'wrapper' => ! empty( $wrapper_styles ) ? sprintf( ' style="%s"', safecss_filter_attr( implode( ' ', $wrapper_styles ) ) ) : '', + 'label' => ! empty( $label_styles ) ? sprintf( ' style="%s"', esc_attr( safecss_filter_attr( implode( ' ', $label_styles ) ) ) ) : '', ); } +/** + * Returns typography classnames depending on whether there are named font sizes/families . + * + * @param array $attributes The block attributes. + * + * @return string The typography color classnames to be applied to the block elements. + */ +function get_typography_classes_for_block_core_search( $attributes ) { + $typography_classes = array(); + $has_named_font_family = ! empty( $attributes['fontFamily'] ); + $has_named_font_size = ! empty( $attributes['fontSize'] ); + + if ( $has_named_font_size ) { + $typography_classes[] = sprintf( 'has-%s-font-size', esc_attr( $attributes['fontSize'] ) ); + } + + if ( $has_named_font_family ) { + $typography_classes[] = sprintf( 'has-%s-font-family', esc_attr( $attributes['fontFamily'] ) ); + } + + return implode( ' ', $typography_classes ); +} + +/** + * Returns typography styles to be included in an HTML style tag. + * This excludes text-decoration, which is applied only to the label and button elements of the search block. + * + * @param array $attributes The block attributes. + * + * @return string A string of typography CSS declarations. + */ +function get_typography_styles_for_block_core_search( $attributes ) { + $typography_styles = array(); + + // Add typography styles. + if ( ! empty( $attributes['style']['typography']['fontSize'] ) ) { + $typography_styles[] = sprintf( 'font-size: %s;', esc_attr( $attributes['style']['typography']['fontSize'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['fontFamily'] ) ) { + $typography_styles[] = sprintf( 'font-family: %s;', esc_attr( $attributes['style']['typography']['fontFamily'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['letterSpacing'] ) ) { + $typography_styles[] = sprintf( 'letter-spacing: %s;', esc_attr( $attributes['style']['typography']['letterSpacing'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['fontWeight'] ) ) { + $typography_styles[] = sprintf( 'font-weight: %s;', esc_attr( $attributes['style']['typography']['fontWeight'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['fontStyle'] ) ) { + $typography_styles[] = sprintf( 'font-style: %s;', esc_attr( $attributes['style']['typography']['fontStyle'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['lineHeight'] ) ) { + $typography_styles[] = sprintf( 'line-height: %s;', esc_attr( $attributes['style']['typography']['lineHeight'] ) ); + } + + if ( ! empty( $attributes['style']['typography']['textTransform'] ) ) { + $typography_styles[] = sprintf( 'text-transform: %s;', esc_attr( $attributes['style']['typography']['textTransform'] ) ); + } + + return implode( '', $typography_styles ); +} + /** * Returns border color classnames depending on whether there are named or custom border colors. * diff --git a/packages/block-library/src/search/style.scss b/packages/block-library/src/search/style.scss index f6eb7ed287c5ef..9a713804603ba3 100644 --- a/packages/block-library/src/search/style.scss +++ b/packages/block-library/src/search/style.scss @@ -10,6 +10,7 @@ min-width: 1.5em; min-height: 1.5em; fill: currentColor; + vertical-align: text-bottom; } } @@ -38,9 +39,9 @@ margin-right: 0; min-width: 3em; border: 1px solid #949494; - font-size: inherit; - font-family: inherit; - line-height: inherit; + // !important used to forcibly prevent undesired application of + // text-decoration styles on the input field. + text-decoration: unset !important; } .wp-block-search.wp-block-search__button-only {