From 114a1a2d9b06911b451e0d79ea946ebf92438e55 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 20:47:37 +0000 Subject: [PATCH 01/13] Initial version --- .vscode/settings.json | 3 + lib/block-supports/typography.php | 18 - packages/block-editor/package.json | 3 - packages/block-editor/src/hooks/fit-text.js | 329 ------------------ packages/block-editor/src/hooks/index.js | 4 - packages/block-editor/src/hooks/typography.js | 16 +- packages/block-library/package.json | 1 + .../block-library/src/fit-text/block.json | 74 ++++ packages/block-library/src/fit-text/edit.js | 151 ++++++++ packages/block-library/src/fit-text/index.js | 29 ++ packages/block-library/src/fit-text/index.php | 51 +++ packages/block-library/src/fit-text/init.js | 6 + packages/block-library/src/fit-text/save.js | 24 ++ .../src/fit-text/utils.js} | 0 .../src/fit-text/view.js} | 2 +- packages/block-library/src/heading/block.json | 1 - packages/block-library/src/index.js | 2 + .../block-library/src/paragraph/block.json | 1 - test/e2e/specs/editor/blocks/fit-text.spec.js | 315 ++++++----------- 19 files changed, 449 insertions(+), 581 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 packages/block-editor/src/hooks/fit-text.js create mode 100644 packages/block-library/src/fit-text/block.json create mode 100644 packages/block-library/src/fit-text/edit.js create mode 100644 packages/block-library/src/fit-text/index.js create mode 100644 packages/block-library/src/fit-text/index.php create mode 100644 packages/block-library/src/fit-text/init.js create mode 100644 packages/block-library/src/fit-text/save.js rename packages/{block-editor/src/utils/fit-text-utils.js => block-library/src/fit-text/utils.js} (100%) rename packages/{block-editor/src/utils/fit-text-frontend.js => block-library/src/fit-text/view.js} (96%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000000..6296ef93791426 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "window.title": "${activeEditorShort}${separator}${rootName}${separator}${profileName}${separator}[Branch: fit-text-block]" +} \ No newline at end of file diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 8fdf11793511b7..a4719b7bdd4099 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -244,24 +244,6 @@ function gutenberg_typography_get_preset_inline_style_value( $style_value, $css_ * @return string Filtered block content. */ function gutenberg_render_typography_support( $block_content, $block ) { - if ( ! empty( $block['attrs']['fitText'] ) && ! is_admin() ) { - wp_enqueue_script_module( '@wordpress/block-editor/utils/fit-text-frontend' ); - - // Add Interactivity API directives for fit text to work with client-side navigation. - if ( ! empty( $block_content ) ) { - $processor = new WP_HTML_Tag_Processor( $block_content ); - if ( $processor->next_tag() ) { - if ( ! $processor->get_attribute( 'data-wp-interactive' ) ) { - $processor->set_attribute( 'data-wp-interactive', true ); - } - $processor->set_attribute( 'data-wp-context---core-fit-text', 'core/fit-text::{"fontSize":""}' ); - $processor->set_attribute( 'data-wp-init---core-fit-text', 'core/fit-text::callbacks.init' ); - $processor->set_attribute( 'data-wp-style--font-size', 'core/fit-text::context.fontSize' ); - $block_content = $processor->get_updated_html(); - } - } - } - if ( ! isset( $block['attrs']['style']['typography']['fontSize'] ) ) { return $block_content; } diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index a1c399315f60d1..52b816e9ba3d7d 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -35,9 +35,6 @@ }, "react-native": "src/index", "wpScript": true, - "wpScriptModuleExports": { - "./utils/fit-text-frontend": "./build-module/utils/fit-text-frontend.js" - }, "sideEffects": [ "build-style/**", "src/**/*.scss", diff --git a/packages/block-editor/src/hooks/fit-text.js b/packages/block-editor/src/hooks/fit-text.js deleted file mode 100644 index 356452035064ae..00000000000000 --- a/packages/block-editor/src/hooks/fit-text.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * WordPress dependencies - */ -import { addFilter } from '@wordpress/hooks'; -import { hasBlockSupport } from '@wordpress/blocks'; -import { useEffect, useCallback } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import { - ToggleControl, - __experimentalToolsPanelItem as ToolsPanelItem, -} from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { optimizeFitText } from '../utils/fit-text-utils'; -import { store as blockEditorStore } from '../store'; -import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs'; -import InspectorControls from '../components/inspector-controls'; - -export const FIT_TEXT_SUPPORT_KEY = 'typography.fitText'; - -/** - * Filters registered block settings, extending attributes to include - * the `fitText` attribute. - * - * @param {Object} settings Original block settings. - * @return {Object} Filtered block settings. - */ -function addAttributes( settings ) { - if ( ! hasBlockSupport( settings, FIT_TEXT_SUPPORT_KEY ) ) { - return settings; - } - - // Allow blocks to specify their own attribute definition. - if ( settings.attributes?.fitText ) { - return settings; - } - - // Add fitText attribute. - return { - ...settings, - attributes: { - ...settings.attributes, - fitText: { - type: 'boolean', - }, - }, - }; -} - -/** - * Custom hook to handle fit text functionality in the editor. - * - * @param {Object} props Component props. - * @param {?boolean} props.fitText Fit text attribute. - * @param {string} props.name Block name. - * @param {string} props.clientId Block client ID. - */ -function useFitText( { fitText, name, clientId } ) { - const hasFitTextSupport = hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY ); - const blockElement = useBlockElement( clientId ); - - // Monitor block attribute changes - // Any attribute may change the available space. - const blockAttributes = useSelect( - ( select ) => { - if ( ! clientId || ! hasFitTextSupport || ! fitText ) { - return; - } - return select( blockEditorStore ).getBlockAttributes( clientId ); - }, - [ clientId, hasFitTextSupport, fitText ] - ); - - const applyFitText = useCallback( () => { - if ( ! blockElement || ! hasFitTextSupport || ! fitText ) { - return; - } - - // Get or create style element with unique ID - const styleId = `fit-text-${ clientId }`; - let styleElement = blockElement.ownerDocument.getElementById( styleId ); - if ( ! styleElement ) { - styleElement = blockElement.ownerDocument.createElement( 'style' ); - styleElement.id = styleId; - blockElement.ownerDocument.head.appendChild( styleElement ); - } - - const blockSelector = `#block-${ clientId }`; - - const applyFontSize = ( fontSize ) => { - if ( fontSize === 0 ) { - styleElement.textContent = ''; - } else { - styleElement.textContent = `${ blockSelector } { font-size: ${ fontSize }px !important; }`; - } - }; - - optimizeFitText( blockElement, applyFontSize ); - }, [ blockElement, clientId, hasFitTextSupport, fitText ] ); - - useEffect( () => { - if ( - ! fitText || - ! blockElement || - ! clientId || - ! hasFitTextSupport - ) { - return; - } - - // Store current element value for cleanup - const currentElement = blockElement; - const previousVisibility = currentElement.style.visibility; - - // Store IDs for cleanup - let hideFrameId = null; - let calculateFrameId = null; - let showTimeoutId = null; - - // We are hiding the element doing the calculation of fit text - // and then showing it again to avoid the user noticing a flash of potentially - // big fitText while the binary search is happening. - hideFrameId = window.requestAnimationFrame( () => { - currentElement.style.visibility = 'hidden'; - // Wait for browser to render the hidden state - calculateFrameId = window.requestAnimationFrame( () => { - applyFitText(); - - // Using a timeout instead of requestAnimationFrame, because - // with requestAnimationFrame a flash of very high size - // can still occur although rare. - showTimeoutId = setTimeout( () => { - currentElement.style.visibility = previousVisibility; - }, 10 ); - } ); - } ); - - // Watch for size changes - let resizeObserver; - if ( window.ResizeObserver && currentElement.parentElement ) { - resizeObserver = new window.ResizeObserver( applyFitText ); - resizeObserver.observe( currentElement.parentElement ); - } - - // Cleanup function - return () => { - // Cancel pending async operations - if ( hideFrameId !== null ) { - window.cancelAnimationFrame( hideFrameId ); - } - if ( calculateFrameId !== null ) { - window.cancelAnimationFrame( calculateFrameId ); - } - if ( showTimeoutId !== null ) { - clearTimeout( showTimeoutId ); - } - - if ( resizeObserver ) { - resizeObserver.disconnect(); - } - - const styleId = `fit-text-${ clientId }`; - const styleElement = - currentElement.ownerDocument.getElementById( styleId ); - if ( styleElement ) { - styleElement.remove(); - } - }; - }, [ fitText, clientId, applyFitText, blockElement, hasFitTextSupport ] ); - - // Trigger fit text recalculation when content changes - useEffect( () => { - if ( fitText && blockElement && hasFitTextSupport ) { - // Wait for next frame to ensure DOM has updated after content changes - const frameId = window.requestAnimationFrame( () => { - if ( blockElement ) { - applyFitText(); - } - } ); - - return () => window.cancelAnimationFrame( frameId ); - } - }, [ - blockAttributes, - fitText, - applyFitText, - blockElement, - hasFitTextSupport, - ] ); -} - -/** - * Fit text control component for the typography panel. - * - * @param {Object} props Component props. - * @param {string} props.clientId Block client ID. - * @param {Function} props.setAttributes Function to set block attributes. - * @param {string} props.name Block name. - * @param {boolean} props.fitText Whether fit text is enabled. - * @param {string} props.fontSize Font size slug. - * @param {Object} props.style Block style object. - */ -export function FitTextControl( { - clientId, - fitText = false, - setAttributes, - name, - fontSize, - style, -} ) { - if ( ! hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY ) ) { - return null; - } - return ( - - fitText } - label={ __( 'Fit text' ) } - onDeselect={ () => setAttributes( { fitText: undefined } ) } - resetAllFilter={ () => ( { fitText: undefined } ) } - panelId={ clientId } - > - { - const newFitText = ! fitText || undefined; - const updates = { fitText: newFitText }; - - // When enabling fit text, clear font size if it has a value - if ( newFitText ) { - if ( fontSize ) { - updates.fontSize = undefined; - } - if ( style?.typography?.fontSize ) { - updates.style = { - ...style, - typography: { - ...style?.typography, - fontSize: undefined, - }, - }; - } - } - - setAttributes( updates ); - } } - help={ - fitText - ? __( 'Text will resize to fit its container.' ) - : __( - 'The text will resize to fit its container, resetting other font size settings.' - ) - } - /> - - - ); -} - -/** - * Override props applied to the block element on save. - * - * @param {Object} props Additional props applied to the block element. - * @param {Object} blockType Block type. - * @param {Object} attributes Block attributes. - * @return {Object} Filtered props applied to the block element. - */ -function addSaveProps( props, blockType, attributes ) { - if ( ! hasBlockSupport( blockType, FIT_TEXT_SUPPORT_KEY ) ) { - return props; - } - - const { fitText } = attributes; - - if ( ! fitText ) { - return props; - } - - // Add CSS class for frontend detection and styling - const className = props.className - ? `${ props.className } has-fit-text` - : 'has-fit-text'; - - return { - ...props, - className, - }; -} -/** - * Override props applied to the block element in the editor. - * - * @param {Object} props Component props including block attributes. - * @param {string} props.name Block name. - * @param {boolean} props.fitText Whether fit text is enabled. - * @param {string} props.clientId Block client ID. - * @return {Object} Filtered props applied to the block element. - */ -function useBlockProps( { name, fitText, clientId } ) { - useFitText( { fitText, name, clientId } ); - if ( ! fitText || ! hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY ) ) { - return {}; - } - return { - className: 'has-fit-text', - }; -} - -addFilter( - 'blocks.registerBlockType', - 'core/fit-text/addAttribute', - addAttributes -); - -const hasFitTextSupport = ( blockNameOrType ) => { - return hasBlockSupport( blockNameOrType, FIT_TEXT_SUPPORT_KEY ); -}; - -export default { - useBlockProps, - addSaveProps, - attributeKeys: [ 'fitText', 'fontSize', 'style' ], - hasSupport: hasFitTextSupport, - edit: FitTextControl, -}; diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index cc8339455fdd84..95c18e6a3f55a7 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -23,7 +23,6 @@ import duotone from './duotone'; import fontFamily from './font-family'; import fontSize from './font-size'; import textAlign from './text-align'; -import fitText from './fit-text'; import border from './border'; import position from './position'; import blockStyleVariation from './block-style-variation'; @@ -44,7 +43,6 @@ createBlockEditFilter( customClassName, style, duotone, - fitText, position, layout, contentLockUI, @@ -64,7 +62,6 @@ createBlockListBlockFilter( [ duotone, fontFamily, fontSize, - fitText, border, position, blockStyleVariation, @@ -77,7 +74,6 @@ createBlockSaveFilter( [ ariaLabel, customClassName, border, - fitText, color, style, fontFamily, diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index 5c551a4bb35761..fd5f01137b178b 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -18,7 +18,6 @@ import { LINE_HEIGHT_SUPPORT_KEY } from './line-height'; import { FONT_FAMILY_SUPPORT_KEY } from './font-family'; import { FONT_SIZE_SUPPORT_KEY } from './font-size'; import { TEXT_ALIGN_SUPPORT_KEY } from './text-align'; -import { FIT_TEXT_SUPPORT_KEY } from './fit-text'; import { cleanEmptyObject } from './utils'; import { store as blockEditorStore } from '../store'; @@ -48,7 +47,6 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [ WRITING_MODE_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, LETTER_SPACING_SUPPORT_KEY, - FIT_TEXT_SUPPORT_KEY, ]; function styleToAttributes( style ) { @@ -116,11 +114,11 @@ function TypographyInspectorControl( { children, resetAllFilter } ) { export function TypographyPanel( { clientId, name, setAttributes, settings } ) { function selector( select ) { - const { style, fontFamily, fontSize, fitText } = + const { style, fontFamily, fontSize } = select( blockEditorStore ).getBlockAttributes( clientId ) || {}; - return { style, fontFamily, fontSize, fitText }; + return { style, fontFamily, fontSize }; } - const { style, fontFamily, fontSize, fitText } = useSelect( selector, [ + const { style, fontFamily, fontSize } = useSelect( selector, [ clientId, ] ); const isEnabled = useHasTypographyPanel( settings ); @@ -131,14 +129,6 @@ export function TypographyPanel( { clientId, name, setAttributes, settings } ) { const onChange = ( newStyle ) => { const newAttributes = styleToAttributes( newStyle ); - - // If setting a font size and fitText is currently enabled, disable it - const hasFontSize = - newAttributes.fontSize || newAttributes.style?.typography?.fontSize; - if ( hasFontSize && fitText ) { - newAttributes.fitText = undefined; - } - setAttributes( newAttributes ); }; diff --git a/packages/block-library/package.json b/packages/block-library/package.json index f329bbd91da6ab..4a714df91f7c26 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -39,6 +39,7 @@ "wpScriptModuleExports": { "./accordion/view": "./build-module/accordion/view.js", "./file/view": "./build-module/file/view.js", + "./fit-text/view": "./build-module/fit-text/view.js", "./form/view": "./build-module/form/view.js", "./image/view": "./build-module/image/view.js", "./navigation/view": "./build-module/navigation/view.js", diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json new file mode 100644 index 00000000000000..58dd0ea30a2ef7 --- /dev/null +++ b/packages/block-library/src/fit-text/block.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "core/fit-text", + "title": "Fit Text", + "category": "text", + "description": "Add text that automatically scales to fit its container.", + "keywords": [ "text", "heading", "resize", "scale" ], + "textdomain": "default", + "attributes": { + "content": { + "type": "rich-text", + "source": "rich-text", + "selector": "h1,h2,h3,h4,h5,h6,p", + "role": "content" + }, + "level": { + "type": "number", + "default": 2 + }, + "levelOptions": { + "type": "array", + "default": [ 0, 1, 2, 3, 4, 5, 6 ] + }, + "textAlign": { + "type": "string" + } + }, + "supports": { + "align": [ "wide", "full" ], + "anchor": true, + "className": true, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": {} + }, + "interactivity": { + "clientNavigation": true + }, + "__unstablePasteTextInline": true, + "__experimentalSlashInserter": true + }, + "editorStyle": "wp-block-fit-text-editor", + "style": "wp-block-fit-text" +} diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js new file mode 100644 index 00000000000000..4c44e4f437de20 --- /dev/null +++ b/packages/block-library/src/fit-text/edit.js @@ -0,0 +1,151 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + useEffect, + useLayoutEffect, + useCallback, + useRef, +} from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { + RichText, + AlignmentControl, + BlockControls, + useBlockProps, + HeadingLevelDropdown, + useBlockEditingMode, +} from '@wordpress/block-editor'; +import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { optimizeFitText } from './utils'; + +export default function FitTextEdit( { + attributes, + setAttributes, + insertBlocksAfter, + clientId, +} ) { + const { level, levelOptions, textAlign, content } = attributes; + const blockEditingMode = useBlockEditingMode(); + const blockRef = useRef(); + + const applyFitText = useCallback( () => { + if ( ! blockRef.current ) { + return; + } + + const blockElement = blockRef.current; + + // Get or create style element with unique ID + const styleId = `fit-text-${ clientId }`; + let styleElement = blockElement.ownerDocument.getElementById( styleId ); + if ( ! styleElement ) { + styleElement = blockElement.ownerDocument.createElement( 'style' ); + styleElement.id = styleId; + blockElement.ownerDocument.head.appendChild( styleElement ); + } + + const blockSelector = `#block-${ clientId }`; + + const applyFontSize = ( fontSize ) => { + if ( fontSize === 0 ) { + styleElement.textContent = ''; + } else { + styleElement.textContent = `${ blockSelector } { font-size: ${ fontSize }px !important; }`; + } + }; + + optimizeFitText( blockElement, applyFontSize ); + }, [ clientId ] ); + + useEffect( () => { + if ( ! blockRef.current || ! clientId ) { + return; + } + + const currentElement = blockRef.current; + + applyFitText(); + + // Watch for size changes + let resizeObserver; + if ( window.ResizeObserver && currentElement.parentElement ) { + resizeObserver = new window.ResizeObserver( applyFitText ); + resizeObserver.observe( currentElement.parentElement ); + } + + // Cleanup function + return () => { + if ( resizeObserver ) { + resizeObserver.disconnect(); + } + + const styleId = `fit-text-${ clientId }`; + const styleElement = + currentElement.ownerDocument.getElementById( styleId ); + if ( styleElement ) { + styleElement.remove(); + } + }; + }, [ clientId, applyFitText ] ); + + // Trigger fit text recalculation when content changes + useLayoutEffect( () => { + if ( blockRef.current ) { + applyFitText(); + } + }, [ attributes, applyFitText ] ); + + const tagName = level === 0 ? 'p' : `h${ level }`; + const blockProps = useBlockProps( { + ref: blockRef, + className: clsx( 'has-fit-text', { + [ `has-text-align-${ textAlign }` ]: textAlign, + } ), + } ); + + return ( + <> + { blockEditingMode === 'default' && ( + + + setAttributes( { level: newLevel } ) + } + /> + { + setAttributes( { textAlign: nextAlign } ); + } } + /> + + ) } + setAttributes( { content: value } ) } + allowedFormats={ [] } + disableLineBreaks + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( createBlock( getDefaultBlockName() ) ) + } + /> + + ); +} diff --git a/packages/block-library/src/fit-text/index.js b/packages/block-library/src/fit-text/index.js new file mode 100644 index 00000000000000..efefd7d4d5aab0 --- /dev/null +++ b/packages/block-library/src/fit-text/index.js @@ -0,0 +1,29 @@ +/** + * WordPress dependencies + */ +import { paragraph as icon } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import initBlock from '../utils/init-block'; +import metadata from './block.json'; +import edit from './edit'; +import save from './save'; + +const { name } = metadata; +export { metadata, name }; + +export const settings = { + icon, + example: { + attributes: { + content: 'Fit Text', + level: 2, + }, + }, + edit, + save, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/block-library/src/fit-text/index.php b/packages/block-library/src/fit-text/index.php new file mode 100644 index 00000000000000..feabf719bac9e5 --- /dev/null +++ b/packages/block-library/src/fit-text/index.php @@ -0,0 +1,51 @@ +next_tag() ) { + if ( ! $processor->get_attribute( 'data-wp-interactive' ) ) { + $processor->set_attribute( 'data-wp-interactive', true ); + } + $processor->set_attribute( 'data-wp-context---core-fit-text', 'core/fit-text::{"fontSize":""}' ); + $processor->set_attribute( 'data-wp-init---core-fit-text', 'core/fit-text::callbacks.init' ); + $processor->set_attribute( 'data-wp-style--font-size', 'core/fit-text::context.fontSize' ); + $content = $processor->get_updated_html(); + } + + return $content; +} + +/** + * Registers the `core/fit-text` block on the server. + */ +function register_block_core_fit_text() { + register_block_type_from_metadata( + __DIR__ . '/fit-text', + array( + 'render_callback' => 'render_block_core_fit_text', + ) + ); +} +add_action( 'init', 'register_block_core_fit_text' ); diff --git a/packages/block-library/src/fit-text/init.js b/packages/block-library/src/fit-text/init.js new file mode 100644 index 00000000000000..79f0492c2cb2f8 --- /dev/null +++ b/packages/block-library/src/fit-text/init.js @@ -0,0 +1,6 @@ +/** + * Internal dependencies + */ +import { init } from './'; + +export default init(); diff --git a/packages/block-library/src/fit-text/save.js b/packages/block-library/src/fit-text/save.js new file mode 100644 index 00000000000000..8c959a7e94304f --- /dev/null +++ b/packages/block-library/src/fit-text/save.js @@ -0,0 +1,24 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { RichText, useBlockProps } from '@wordpress/block-editor'; + +export default function save( { attributes } ) { + const { textAlign, content, level } = attributes; + const TagName = level === 0 ? 'p' : `h${ level }`; + + const className = clsx( 'has-fit-text', { + [ `has-text-align-${ textAlign }` ]: textAlign, + } ); + + return ( + + + + ); +} diff --git a/packages/block-editor/src/utils/fit-text-utils.js b/packages/block-library/src/fit-text/utils.js similarity index 100% rename from packages/block-editor/src/utils/fit-text-utils.js rename to packages/block-library/src/fit-text/utils.js diff --git a/packages/block-editor/src/utils/fit-text-frontend.js b/packages/block-library/src/fit-text/view.js similarity index 96% rename from packages/block-editor/src/utils/fit-text-frontend.js rename to packages/block-library/src/fit-text/view.js index f6729e3c2585c4..ab085e7594883a 100644 --- a/packages/block-editor/src/utils/fit-text-frontend.js +++ b/packages/block-library/src/fit-text/view.js @@ -12,7 +12,7 @@ import { store, getElement, getContext } from '@wordpress/interactivity'; /** * Internal dependencies */ -import { optimizeFitText } from './fit-text-utils'; +import { optimizeFitText } from './utils'; // Initialize via Interactivity API for client-side navigation store( 'core/fit-text', { diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index d9488a3156528d..2869ee85c55206 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -65,7 +65,6 @@ "__experimentalTextTransform": true, "__experimentalTextDecoration": true, "__experimentalWritingMode": true, - "fitText": true, "__experimentalDefaultControls": { "fontSize": true } diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index e71d6d1b651ac7..151e86333c40b5 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -59,6 +59,7 @@ import * as cover from './cover'; import * as details from './details'; import * as embed from './embed'; import * as file from './file'; +import * as fitText from './fit-text'; import * as form from './form'; import * as formInput from './form-input'; import * as formSubmitButton from './form-submit-button'; @@ -178,6 +179,7 @@ const getAllBlocks = () => { details, embed, file, + fitText, group, html, math, diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 9617627ef5d0da..7e004019cbf282 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -65,7 +65,6 @@ "__experimentalLetterSpacing": true, "__experimentalTextTransform": true, "__experimentalWritingMode": true, - "fitText": true, "__experimentalDefaultControls": { "fontSize": true } diff --git a/test/e2e/specs/editor/blocks/fit-text.spec.js b/test/e2e/specs/editor/blocks/fit-text.spec.js index c10e065155c4c7..262b9de29bffc8 100644 --- a/test/e2e/specs/editor/blocks/fit-text.spec.js +++ b/test/e2e/specs/editor/blocks/fit-text.spec.js @@ -9,123 +9,105 @@ test.describe( 'Fit Text', () => { } ); test.describe( 'Editor functionality', () => { - test( 'should enable fit text on a heading block', async ( { + test( 'should insert a fit text block', async ( { editor, page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { - content: 'Test Heading', + content: 'Test Fit Text', level: 2, }, } ); - await editor.openDocumentSettingsSidebar(); - - // Enable Fit text control via Typography options menu - await page - .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { name: 'Typography options' } ) - .click(); - await page - .getByRole( 'menu', { name: 'Typography options' } ) - .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } ) - .click(); - - const fitTextToggle = page.getByRole( 'checkbox', { - name: 'Fit text', - } ); - - await fitTextToggle.click(); - await expect.poll( editor.getBlocks ).toMatchObject( [ { - name: 'core/heading', + name: 'core/fit-text', attributes: { - content: 'Test Heading', + content: 'Test Fit Text', level: 2, - fitText: true, }, }, ] ); - const headingBlock = editor.canvas.locator( - '[data-type="core/heading"]' + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' ); - await expect( headingBlock ).toHaveClass( /has-fit-text/ ); + await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); } ); - test( 'should disable fit text when toggled off', async ( { + test( 'should allow changing heading level', async ( { editor, page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { - content: 'Test Heading', + content: 'Heading Level Test', level: 2, - fitText: true, }, } ); - await editor.openDocumentSettingsSidebar(); + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' + ); + await fitTextBlock.click(); - const fitTextToggle = page.getByRole( 'checkbox', { - name: 'Fit text', - } ); + // Open heading level dropdown + await page + .getByRole( 'button', { name: 'Change level' } ) + .click(); - await fitTextToggle.click(); + // Select H4 + await page.getByRole( 'menuitemradio', { name: 'Heading 4' } ).click(); - const blocks = await editor.getBlocks(); - expect( blocks[ 0 ].attributes.fitText ).toBeUndefined(); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/fit-text', + attributes: { + content: 'Heading Level Test', + level: 4, + }, + }, + ] ); } ); - test( 'should enable fit text on a paragraph block', async ( { + test( 'should allow changing to paragraph (level 0)', async ( { editor, page, } ) => { await editor.insertBlock( { - name: 'core/paragraph', + name: 'core/fit-text', attributes: { - content: 'Test paragraph with fit text enabled', + content: 'Paragraph Test', + level: 2, }, } ); - await editor.openDocumentSettingsSidebar(); + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' + ); + await fitTextBlock.click(); - // Enable Fit text control via Typography options menu + // Open heading level dropdown await page - .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { name: 'Typography options' } ) + .getByRole( 'button', { name: 'Change level' } ) .click(); - await page - .getByRole( 'menu', { name: 'Typography options' } ) - .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } ) - .click(); - - const fitTextToggle = page.getByRole( 'checkbox', { - name: 'Fit text', - } ); - await fitTextToggle.click(); + // Select Paragraph + await page.getByRole( 'menuitemradio', { name: 'Paragraph' } ).click(); await expect.poll( editor.getBlocks ).toMatchObject( [ { - name: 'core/paragraph', + name: 'core/fit-text', attributes: { - content: 'Test paragraph with fit text enabled', - fitText: true, + content: 'Paragraph Test', + level: 0, }, }, ] ); - - const paragraphBlock = editor.canvas.locator( - '[data-type="core/paragraph"]' - ); - - await expect( paragraphBlock ).toHaveClass( /has-fit-text/ ); } ); test( 'should apply font size dynamically based on container width in editor', async ( { @@ -133,37 +115,36 @@ test.describe( 'Fit Text', () => { page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { content: 'Resizable Text', level: 2, - fitText: true, }, } ); - const headingBlock = editor.canvas.locator( - '[data-type="core/heading"]' + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' ); // Wait for fit text to apply - await headingBlock.waitFor( { state: 'attached' } ); - await expect( headingBlock ).toHaveClass( /has-fit-text/ ); + await fitTextBlock.waitFor( { state: 'attached' } ); + await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); - const initialFontSize = await headingBlock.evaluate( ( el ) => { + const initialFontSize = await fitTextBlock.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); // Add more text to force smaller font size - await headingBlock.click(); + await fitTextBlock.click(); await page.keyboard.press( 'End' ); await page.keyboard.type( ' that is much longer and should have smaller font' ); // Wait for DOM to update and fit text to recalculate - await headingBlock.waitFor( { state: 'attached' } ); + await fitTextBlock.waitFor( { state: 'attached' } ); - const newFontSize = await headingBlock.evaluate( ( el ) => { + const newFontSize = await fitTextBlock.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); @@ -174,46 +155,44 @@ test.describe( 'Fit Text', () => { expect( newSize ).toBeLessThan( initialSize ); } ); - test( 'should apply much larger font size with fit text compared to without fit text for a short text', async ( { + test( 'should apply much larger font size with fit text compared to a normal heading for short text', async ( { editor, } ) => { - // Insert two paragraphs with same content for comparison + // Insert a regular heading and a fit text block with same content await editor.insertBlock( { - name: 'core/paragraph', + name: 'core/heading', attributes: { content: 'Hello', + level: 2, }, } ); await editor.insertBlock( { - name: 'core/paragraph', + name: 'core/fit-text', attributes: { content: 'Hello', - fitText: true, + level: 2, }, } ); - const paragraphBlocks = editor.canvas.locator( - '[data-type="core/paragraph"]' + const headingBlock = editor.canvas.locator( + '[data-type="core/heading"]' + ); + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' ); // Wait for fit text to apply - await paragraphBlocks.nth( 1 ).waitFor( { state: 'attached' } ); - await expect( paragraphBlocks.nth( 1 ) ).toHaveClass( - /has-fit-text/ - ); + await fitTextBlock.waitFor( { state: 'attached' } ); + await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); - const normalFontSize = await paragraphBlocks - .nth( 0 ) - .evaluate( ( el ) => { - return window.getComputedStyle( el ).fontSize; - } ); + const normalFontSize = await headingBlock.evaluate( ( el ) => { + return window.getComputedStyle( el ).fontSize; + } ); - const fitTextFontSize = await paragraphBlocks - .nth( 1 ) - .evaluate( ( el ) => { - return window.getComputedStyle( el ).fontSize; - } ); + const fitTextFontSize = await fitTextBlock.evaluate( ( el ) => { + return window.getComputedStyle( el ).fontSize; + } ); const normalSize = parseFloat( normalFontSize ); const fitTextSize = parseFloat( fitTextFontSize ); @@ -222,100 +201,16 @@ test.describe( 'Fit Text', () => { expect( fitTextSize ).toBeGreaterThan( normalSize * 2 ); } ); - test( 'should disable fit text when a font size is selected', async ( { - editor, - page, - } ) => { - await editor.insertBlock( { - name: 'core/heading', - attributes: { - content: 'Test Heading', - level: 2, - fitText: true, - }, - } ); - - await editor.openDocumentSettingsSidebar(); - - // Set a custom font size - await page.click( - 'role=region[name="Editor settings"i] >> role=button[name="Set custom size"i]' - ); - await page.click( 'role=spinbutton[name="Font size"i]' ); - await page.keyboard.type( '24' ); - - // fitText should be cleared - await expect.poll( editor.getBlocks ).toMatchObject( [ - { - name: 'core/heading', - attributes: expect.objectContaining( { - content: 'Test Heading', - level: 2, - style: { - typography: { - fontSize: '24px', - }, - }, - } ), - }, - ] ); - } ); - - test( 'should clear font size when fit text is enabled', async ( { - editor, - page, - } ) => { - await editor.insertBlock( { - name: 'core/heading', - attributes: { - content: 'Test Heading', - level: 2, - fontSize: 'large', - }, - } ); - - await editor.openDocumentSettingsSidebar(); - - // Enable Fit text control via Typography options menu - await page - .getByRole( 'region', { name: 'Editor settings' } ) - .getByRole( 'button', { name: 'Typography options' } ) - .click(); - await page - .getByRole( 'menu', { name: 'Typography options' } ) - .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } ) - .click(); - - const fitTextToggle = page.getByRole( 'checkbox', { - name: 'Fit text', - } ); - - await fitTextToggle.click(); - - // fontSize should be cleared - await expect.poll( editor.getBlocks ).toMatchObject( [ - { - name: 'core/heading', - attributes: expect.objectContaining( { - content: 'Test Heading', - level: 2, - fitText: true, - } ), - }, - ] ); - } ); - test( 'should not load frontend script when editing a saved post with fit text', async ( { admin, editor, page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { content: 'Test Heading', level: 2, - fitText: true, }, } ); @@ -323,18 +218,17 @@ test.describe( 'Fit Text', () => { await admin.editPost( postId ); - const headingBlock = editor.canvas.locator( - '[data-type="core/heading"]' + const fitTextBlock = editor.canvas.locator( + '[data-type="core/fit-text"]' ); - await expect( headingBlock ).toBeVisible(); + await expect( fitTextBlock ).toBeVisible(); await expect.poll( editor.getBlocks ).toMatchObject( [ { - name: 'core/heading', + name: 'core/fit-text', attributes: { content: 'Test Heading', level: 2, - fitText: true, }, }, ] ); @@ -345,7 +239,7 @@ test.describe( 'Fit Text', () => { document.querySelectorAll( 'script[type="module"]' ) ); return scripts.some( ( script ) => - script.src.includes( 'fit-text-frontend' ) + script.src.includes( 'fit-text' ) && script.src.includes( 'view' ) ); } ); expect( frontendScriptLoaded ).toBe( false ); @@ -358,11 +252,10 @@ test.describe( 'Fit Text', () => { page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { content: 'Frontend Test', level: 2, - fitText: true, }, } ); @@ -374,16 +267,16 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const heading = page.locator( 'h2.has-fit-text' ); + const fitText = page.locator( 'h2.has-fit-text' ); - await expect( heading ).toBeVisible(); - await expect( heading ).toHaveClass( /has-fit-text/ ); + await expect( fitText ).toBeVisible(); + await expect( fitText ).toHaveClass( /has-fit-text/ ); - const inlineStyle = await heading.getAttribute( 'style' ); + const inlineStyle = await fitText.getAttribute( 'style' ); expect( inlineStyle ).toContain( 'font-size' ); expect( inlineStyle ).toMatch( /font-size:\s*\d+px/ ); - const computedFontSize = await heading.evaluate( ( el ) => { + const computedFontSize = await fitText.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); @@ -398,11 +291,10 @@ test.describe( 'Fit Text', () => { page, } ) => { await editor.insertBlock( { - name: 'core/heading', + name: 'core/fit-text', attributes: { content: 'Resize Me', level: 2, - fitText: true, }, } ); @@ -414,11 +306,11 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const heading = page.locator( 'h2.has-fit-text' ); + const fitText = page.locator( 'h2.has-fit-text' ); // Wait for fit text to initialize - await heading.waitFor( { state: 'visible' } ); - await expect( heading ).toHaveClass( /has-fit-text/ ); + await fitText.waitFor( { state: 'visible' } ); + await expect( fitText ).toHaveClass( /has-fit-text/ ); // Wait for inline style to be applied await page.waitForFunction( @@ -429,11 +321,11 @@ test.describe( 'Fit Text', () => { { timeout: 5000 } ); - const initialFontSize = await heading.evaluate( ( el ) => { + const initialFontSize = await fitText.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); - const initialInlineStyle = await heading.getAttribute( 'style' ); + const initialInlineStyle = await fitText.getAttribute( 'style' ); await page.setViewportSize( { width: 440, height: 720 } ); @@ -451,7 +343,7 @@ test.describe( 'Fit Text', () => { { timeout: 5000 } ); - const newFontSize = await heading.evaluate( ( el ) => { + const newFontSize = await fitText.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); @@ -466,19 +358,20 @@ test.describe( 'Fit Text', () => { editor, page, } ) => { - // Insert two paragraphs with same content for comparison + // Insert two headings with same content for comparison await editor.insertBlock( { - name: 'core/paragraph', + name: 'core/heading', attributes: { content: 'Hello', + level: 2, }, } ); await editor.insertBlock( { - name: 'core/paragraph', + name: 'core/fit-text', attributes: { content: 'Hello', - fitText: true, + level: 2, }, } ); @@ -490,30 +383,30 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitTextParagraph = page.locator( 'p.has-fit-text' ); + const fitText = page.locator( 'h2.has-fit-text' ); // Wait for fit text to initialize - await fitTextParagraph.waitFor( { state: 'visible' } ); - await expect( fitTextParagraph ).toHaveClass( /has-fit-text/ ); + await fitText.waitFor( { state: 'visible' } ); + await expect( fitText ).toHaveClass( /has-fit-text/ ); // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( 'p.has-fit-text' ); + const el = document.querySelector( 'h2.has-fit-text' ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } ); - const paragraphs = page.locator( 'p' ); + const headings = page.locator( 'h2' ); - const normalFontSize = await paragraphs + const normalFontSize = await headings .first() .evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); - const fitTextFontSize = await fitTextParagraph.evaluate( ( el ) => { + const fitTextFontSize = await fitText.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; } ); From 330e73764869aa86509a98c7102784b1feea6fa8 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 21:01:39 +0000 Subject: [PATCH 02/13] working with timers --- packages/block-library/src/fit-text/edit.js | 43 +++++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js index 4c44e4f437de20..2dbac6a39f9131 100644 --- a/packages/block-library/src/fit-text/edit.js +++ b/packages/block-library/src/fit-text/edit.js @@ -44,15 +44,15 @@ export default function FitTextEdit( { return; } - const blockElement = blockRef.current; - // Get or create style element with unique ID const styleId = `fit-text-${ clientId }`; - let styleElement = blockElement.ownerDocument.getElementById( styleId ); + let styleElement = + blockRef.current.ownerDocument.getElementById( styleId ); if ( ! styleElement ) { - styleElement = blockElement.ownerDocument.createElement( 'style' ); + styleElement = + blockRef.current.ownerDocument.createElement( 'style' ); styleElement.id = styleId; - blockElement.ownerDocument.head.appendChild( styleElement ); + blockRef.current.ownerDocument.head.appendChild( styleElement ); } const blockSelector = `#block-${ clientId }`; @@ -65,7 +65,7 @@ export default function FitTextEdit( { } }; - optimizeFitText( blockElement, applyFontSize ); + optimizeFitText( blockRef.current, applyFontSize ); }, [ clientId ] ); useEffect( () => { @@ -73,26 +73,38 @@ export default function FitTextEdit( { return; } - const currentElement = blockRef.current; + // Store IDs for cleanup + let calculateTimeoutId = null; + + // Hide the element during calculation to avoid flash + //blockRef.current.style.visibility = 'hidden'; - applyFitText(); + // Wait 100ms for DOM to fully render and layout to settle + calculateTimeoutId = setTimeout( () => { + applyFitText(); + }, 100 ); // Watch for size changes let resizeObserver; - if ( window.ResizeObserver && currentElement.parentElement ) { + if ( window.ResizeObserver && blockRef.current.parentElement ) { resizeObserver = new window.ResizeObserver( applyFitText ); - resizeObserver.observe( currentElement.parentElement ); + resizeObserver.observe( blockRef.current.parentElement ); } // Cleanup function return () => { + // Cancel pending async operations + if ( calculateTimeoutId !== null ) { + clearTimeout( calculateTimeoutId ); + } + if ( resizeObserver ) { resizeObserver.disconnect(); } const styleId = `fit-text-${ clientId }`; const styleElement = - currentElement.ownerDocument.getElementById( styleId ); + blockRef.current.ownerDocument.getElementById( styleId ); if ( styleElement ) { styleElement.remove(); } @@ -102,7 +114,14 @@ export default function FitTextEdit( { // Trigger fit text recalculation when content changes useLayoutEffect( () => { if ( blockRef.current ) { - applyFitText(); + // Wait 100ms for DOM layout to settle (especially for alignment changes) + const timeoutId = setTimeout( () => { + if ( blockRef.current ) { + applyFitText(); + } + }, 100 ); + + return () => clearTimeout( timeoutId ); } }, [ attributes, applyFitText ] ); From 18700b65d006a101af0a36ffc913fb32cd80ecf3 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 21:29:45 +0000 Subject: [PATCH 03/13] improvements --- packages/block-library/src/common.scss | 5 -- .../block-library/src/fit-text/block.json | 3 - packages/block-library/src/fit-text/edit.js | 63 ++++++++++--------- packages/block-library/src/fit-text/save.js | 13 +--- .../block-library/src/fit-text/style.scss | 3 + packages/block-library/src/fit-text/view.js | 2 +- packages/block-library/src/style.scss | 1 + test/e2e/specs/editor/blocks/fit-text.spec.js | 19 +++--- 8 files changed, 46 insertions(+), 63 deletions(-) create mode 100644 packages/block-library/src/fit-text/style.scss diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss index 8bb0d00460f4a5..63d2337928c69c 100644 --- a/packages/block-library/src/common.scss +++ b/packages/block-library/src/common.scss @@ -49,11 +49,6 @@ text-align: right; } -// Fit Text -.has-fit-text { - white-space: nowrap !important; -} - // This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor. #end-resizable-editor-section { display: none; diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json index 58dd0ea30a2ef7..f2133b1bd66328 100644 --- a/packages/block-library/src/fit-text/block.json +++ b/packages/block-library/src/fit-text/block.json @@ -21,9 +21,6 @@ "levelOptions": { "type": "array", "default": [ 0, 1, 2, 3, 4, 5, 6 ] - }, - "textAlign": { - "type": "string" } }, "supports": { diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js index 2dbac6a39f9131..19b40c770a6fa1 100644 --- a/packages/block-library/src/fit-text/edit.js +++ b/packages/block-library/src/fit-text/edit.js @@ -13,7 +13,6 @@ import { useCallback, useRef, } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; import { RichText, AlignmentControl, @@ -35,7 +34,7 @@ export default function FitTextEdit( { insertBlocksAfter, clientId, } ) { - const { level, levelOptions, textAlign, content } = attributes; + const { level, levelOptions, content } = attributes; const blockEditingMode = useBlockEditingMode(); const blockRef = useRef(); @@ -73,16 +72,12 @@ export default function FitTextEdit( { return; } - // Store IDs for cleanup - let calculateTimeoutId = null; + let calculateFrameId = null; - // Hide the element during calculation to avoid flash - //blockRef.current.style.visibility = 'hidden'; - - // Wait 100ms for DOM to fully render and layout to settle - calculateTimeoutId = setTimeout( () => { + // Wait for next animation frame for DOM to fully render and layout to settle + calculateFrameId = window.requestAnimationFrame( () => { applyFitText(); - }, 100 ); + } ); // Watch for size changes let resizeObserver; @@ -91,11 +86,12 @@ export default function FitTextEdit( { resizeObserver.observe( blockRef.current.parentElement ); } + const blockRefToCleanup = blockRef.current; + // Cleanup function return () => { - // Cancel pending async operations - if ( calculateTimeoutId !== null ) { - clearTimeout( calculateTimeoutId ); + if ( calculateFrameId !== null ) { + window.cancelAnimationFrame( calculateFrameId ); } if ( resizeObserver ) { @@ -104,33 +100,44 @@ export default function FitTextEdit( { const styleId = `fit-text-${ clientId }`; const styleElement = - blockRef.current.ownerDocument.getElementById( styleId ); + blockRefToCleanup.ownerDocument.getElementById( styleId ); if ( styleElement ) { styleElement.remove(); } }; }, [ clientId, applyFitText ] ); - // Trigger fit text recalculation when content changes + // Trigger fit text recalculation when attributes change useLayoutEffect( () => { if ( blockRef.current ) { - // Wait 100ms for DOM layout to settle (especially for alignment changes) - const timeoutId = setTimeout( () => { - if ( blockRef.current ) { - applyFitText(); + // Wait for two animation frames for DOM layout to settle. + // If we do it in a single frame, because of some reason when changing + // alignment from full to wide or non things don't recompute correctly. + let firstFrameId = null; + let secondFrameId = null; + + firstFrameId = window.requestAnimationFrame( () => { + secondFrameId = window.requestAnimationFrame( () => { + if ( blockRef.current ) { + applyFitText(); + } + } ); + } ); + + return () => { + if ( firstFrameId !== null ) { + window.cancelAnimationFrame( firstFrameId ); } - }, 100 ); - - return () => clearTimeout( timeoutId ); + if ( secondFrameId !== null ) { + window.cancelAnimationFrame( secondFrameId ); + } + }; } }, [ attributes, applyFitText ] ); const tagName = level === 0 ? 'p' : `h${ level }`; const blockProps = useBlockProps( { ref: blockRef, - className: clsx( 'has-fit-text', { - [ `has-text-align-${ textAlign }` ]: textAlign, - } ), } ); return ( @@ -144,12 +151,6 @@ export default function FitTextEdit( { setAttributes( { level: newLevel } ) } /> - { - setAttributes( { textAlign: nextAlign } ); - } } - /> ) } + ); diff --git a/packages/block-library/src/fit-text/style.scss b/packages/block-library/src/fit-text/style.scss new file mode 100644 index 00000000000000..45ae356081f4a3 --- /dev/null +++ b/packages/block-library/src/fit-text/style.scss @@ -0,0 +1,3 @@ +.wp-block-fit-text { + white-space: nowrap !important; +} diff --git a/packages/block-library/src/fit-text/view.js b/packages/block-library/src/fit-text/view.js index ab085e7594883a..27f7a674384939 100644 --- a/packages/block-library/src/fit-text/view.js +++ b/packages/block-library/src/fit-text/view.js @@ -1,6 +1,6 @@ /** * Frontend fit text functionality. - * Automatically detects and initializes fit text on blocks with the has-fit-text class. + * Automatically detects and initializes fit text on core/fit-text blocks. * Supports both initial page load and Interactivity API client-side navigation. */ diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index 907c7105467259..13cc1fa427f213 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -23,6 +23,7 @@ @use "./details/style.scss" as *; @use "./embed/style.scss" as *; @use "./file/style.scss" as *; +@use "./fit-text/style.scss" as *; @use "./form-input/style.scss" as *; @use "./gallery/style.scss" as *; @use "./group/style.scss" as *; diff --git a/test/e2e/specs/editor/blocks/fit-text.spec.js b/test/e2e/specs/editor/blocks/fit-text.spec.js index 262b9de29bffc8..c75988877ace0c 100644 --- a/test/e2e/specs/editor/blocks/fit-text.spec.js +++ b/test/e2e/specs/editor/blocks/fit-text.spec.js @@ -35,7 +35,7 @@ test.describe( 'Fit Text', () => { '[data-type="core/fit-text"]' ); - await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); + await expect( fitTextBlock ).toBeVisible(); } ); test( 'should allow changing heading level', async ( { @@ -128,7 +128,6 @@ test.describe( 'Fit Text', () => { // Wait for fit text to apply await fitTextBlock.waitFor( { state: 'attached' } ); - await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); const initialFontSize = await fitTextBlock.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; @@ -184,7 +183,6 @@ test.describe( 'Fit Text', () => { // Wait for fit text to apply await fitTextBlock.waitFor( { state: 'attached' } ); - await expect( fitTextBlock ).toHaveClass( /has-fit-text/ ); const normalFontSize = await headingBlock.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; @@ -267,10 +265,9 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( 'h2.has-fit-text' ); + const fitText = page.locator( '.wp-block-fit-text h2' ); await expect( fitText ).toBeVisible(); - await expect( fitText ).toHaveClass( /has-fit-text/ ); const inlineStyle = await fitText.getAttribute( 'style' ); expect( inlineStyle ).toContain( 'font-size' ); @@ -306,16 +303,15 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( 'h2.has-fit-text' ); + const fitText = page.locator( '.wp-block-fit-text h2' ); // Wait for fit text to initialize await fitText.waitFor( { state: 'visible' } ); - await expect( fitText ).toHaveClass( /has-fit-text/ ); // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( 'h2.has-fit-text' ); + const el = document.querySelector( '.wp-block-fit-text h2' ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } @@ -332,7 +328,7 @@ test.describe( 'Fit Text', () => { // Wait for inline font-size style to change after resize await page.waitForFunction( ( previousStyle ) => { - const el = document.querySelector( 'h2.has-fit-text' ); + const el = document.querySelector( '.wp-block-fit-text h2' ); return ( el && el.style.fontSize && @@ -383,16 +379,15 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( 'h2.has-fit-text' ); + const fitText = page.locator( '.wp-block-fit-text h2' ); // Wait for fit text to initialize await fitText.waitFor( { state: 'visible' } ); - await expect( fitText ).toHaveClass( /has-fit-text/ ); // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( 'h2.has-fit-text' ); + const el = document.querySelector( '.wp-block-fit-text h2' ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } From 42f08ad431ddbe62f6f03514030c607214ec244f Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 21:40:04 +0000 Subject: [PATCH 04/13] remove wrongly commited file --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6296ef93791426..00000000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "window.title": "${activeEditorShort}${separator}${rootName}${separator}${profileName}${separator}[Branch: fit-text-block]" -} \ No newline at end of file From 79d468d5f8eec2d3793b6dba331f2f5501013e5f Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 22:00:44 +0000 Subject: [PATCH 05/13] lint fixes --- packages/block-library/src/fit-text/edit.js | 6 ------ packages/block-library/src/fit-text/index.php | 11 +++++++---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js index 19b40c770a6fa1..ba32b05df67aef 100644 --- a/packages/block-library/src/fit-text/edit.js +++ b/packages/block-library/src/fit-text/edit.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import clsx from 'clsx'; - /** * WordPress dependencies */ @@ -15,7 +10,6 @@ import { } from '@wordpress/element'; import { RichText, - AlignmentControl, BlockControls, useBlockProps, HeadingLevelDropdown, diff --git a/packages/block-library/src/fit-text/index.php b/packages/block-library/src/fit-text/index.php index feabf719bac9e5..2e6d1c27dcb521 100644 --- a/packages/block-library/src/fit-text/index.php +++ b/packages/block-library/src/fit-text/index.php @@ -8,13 +8,14 @@ /** * Renders the `core/fit-text` block on the server. * - * @param array $attributes Block attributes. - * @param string $content Block default content. - * @param WP_Block $block Block instance. + * @since 22.0.0 + * + * @param array $attributes Block attributes. + * @param string $content Block default content. * * @return string Returns the block content. */ -function render_block_core_fit_text( $attributes, $content, $block ) { +function render_block_core_fit_text( $attributes, $content ) { if ( ! $content || is_admin() ) { return $content; } @@ -39,6 +40,8 @@ function render_block_core_fit_text( $attributes, $content, $block ) { /** * Registers the `core/fit-text` block on the server. + * + * @since 22.0.0 */ function register_block_core_fit_text() { register_block_type_from_metadata( From 9a2f8b82bd394ba38e10c9851b52c27e69cc5072 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 22:03:35 +0000 Subject: [PATCH 06/13] add fit text fixture --- test/integration/fixtures/blocks/core__fit-text.html | 3 +++ test/integration/fixtures/blocks/core__fit-text.json | 12 ++++++++++++ .../fixtures/blocks/core__fit-text.parsed.json | 9 +++++++++ .../fixtures/blocks/core__fit-text.serialized.html | 3 +++ 4 files changed, 27 insertions(+) create mode 100644 test/integration/fixtures/blocks/core__fit-text.html create mode 100644 test/integration/fixtures/blocks/core__fit-text.json create mode 100644 test/integration/fixtures/blocks/core__fit-text.parsed.json create mode 100644 test/integration/fixtures/blocks/core__fit-text.serialized.html diff --git a/test/integration/fixtures/blocks/core__fit-text.html b/test/integration/fixtures/blocks/core__fit-text.html new file mode 100644 index 00000000000000..3a0f15f407e427 --- /dev/null +++ b/test/integration/fixtures/blocks/core__fit-text.html @@ -0,0 +1,3 @@ + +

Fit Text

+ diff --git a/test/integration/fixtures/blocks/core__fit-text.json b/test/integration/fixtures/blocks/core__fit-text.json new file mode 100644 index 00000000000000..ef96470abb2608 --- /dev/null +++ b/test/integration/fixtures/blocks/core__fit-text.json @@ -0,0 +1,12 @@ +[ + { + "name": "core/fit-text", + "isValid": true, + "attributes": { + "content": "Fit Text", + "level": 2, + "levelOptions": [ 0, 1, 2, 3, 4, 5, 6 ] + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__fit-text.parsed.json b/test/integration/fixtures/blocks/core__fit-text.parsed.json new file mode 100644 index 00000000000000..39c525d8e4bfb9 --- /dev/null +++ b/test/integration/fixtures/blocks/core__fit-text.parsed.json @@ -0,0 +1,9 @@ +[ + { + "blockName": "core/fit-text", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n

Fit Text

\n", + "innerContent": [ "\n

Fit Text

\n" ] + } +] diff --git a/test/integration/fixtures/blocks/core__fit-text.serialized.html b/test/integration/fixtures/blocks/core__fit-text.serialized.html new file mode 100644 index 00000000000000..3a0f15f407e427 --- /dev/null +++ b/test/integration/fixtures/blocks/core__fit-text.serialized.html @@ -0,0 +1,3 @@ + +

Fit Text

+ From 808ef338343210ccac52b78477c9fc00c6d4325f Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 22:12:07 +0000 Subject: [PATCH 07/13] lint fixes --- packages/block-editor/src/hooks/typography.js | 4 +- test/e2e/specs/editor/blocks/fit-text.spec.js | 53 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index fd5f01137b178b..1f381a3d0bec14 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -118,9 +118,7 @@ export function TypographyPanel( { clientId, name, setAttributes, settings } ) { select( blockEditorStore ).getBlockAttributes( clientId ) || {}; return { style, fontFamily, fontSize }; } - const { style, fontFamily, fontSize } = useSelect( selector, [ - clientId, - ] ); + const { style, fontFamily, fontSize } = useSelect( selector, [ clientId ] ); const isEnabled = useHasTypographyPanel( settings ); const value = useMemo( () => attributesToStyle( { style, fontFamily, fontSize } ), diff --git a/test/e2e/specs/editor/blocks/fit-text.spec.js b/test/e2e/specs/editor/blocks/fit-text.spec.js index c75988877ace0c..1f8231fde237ec 100644 --- a/test/e2e/specs/editor/blocks/fit-text.spec.js +++ b/test/e2e/specs/editor/blocks/fit-text.spec.js @@ -9,10 +9,7 @@ test.describe( 'Fit Text', () => { } ); test.describe( 'Editor functionality', () => { - test( 'should insert a fit text block', async ( { - editor, - page, - } ) => { + test( 'should insert a fit text block', async ( { editor, page } ) => { await editor.insertBlock( { name: 'core/fit-text', attributes: { @@ -56,12 +53,12 @@ test.describe( 'Fit Text', () => { await fitTextBlock.click(); // Open heading level dropdown - await page - .getByRole( 'button', { name: 'Change level' } ) - .click(); + await page.getByRole( 'button', { name: 'Change level' } ).click(); // Select H4 - await page.getByRole( 'menuitemradio', { name: 'Heading 4' } ).click(); + await page + .getByRole( 'menuitemradio', { name: 'Heading 4' } ) + .click(); await expect.poll( editor.getBlocks ).toMatchObject( [ { @@ -92,12 +89,12 @@ test.describe( 'Fit Text', () => { await fitTextBlock.click(); // Open heading level dropdown - await page - .getByRole( 'button', { name: 'Change level' } ) - .click(); + await page.getByRole( 'button', { name: 'Change level' } ).click(); // Select Paragraph - await page.getByRole( 'menuitemradio', { name: 'Paragraph' } ).click(); + await page + .getByRole( 'menuitemradio', { name: 'Paragraph' } ) + .click(); await expect.poll( editor.getBlocks ).toMatchObject( [ { @@ -236,8 +233,10 @@ test.describe( 'Fit Text', () => { const scripts = Array.from( document.querySelectorAll( 'script[type="module"]' ) ); - return scripts.some( ( script ) => - script.src.includes( 'fit-text' ) && script.src.includes( 'view' ) + return scripts.some( + ( script ) => + script.src.includes( 'fit-text' ) && + script.src.includes( 'view' ) ); } ); expect( frontendScriptLoaded ).toBe( false ); @@ -265,7 +264,7 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( '.wp-block-fit-text h2' ); + const fitText = page.locator( '.wp-block-fit-text' ); await expect( fitText ).toBeVisible(); @@ -303,7 +302,7 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( '.wp-block-fit-text h2' ); + const fitText = page.locator( '.wp-block-fit-text' ); // Wait for fit text to initialize await fitText.waitFor( { state: 'visible' } ); @@ -311,7 +310,9 @@ test.describe( 'Fit Text', () => { // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( '.wp-block-fit-text h2' ); + const el = document.querySelector( + '.wp-block-fit-text' + ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } @@ -328,7 +329,9 @@ test.describe( 'Fit Text', () => { // Wait for inline font-size style to change after resize await page.waitForFunction( ( previousStyle ) => { - const el = document.querySelector( '.wp-block-fit-text h2' ); + const el = document.querySelector( + '.wp-block-fit-text' + ); return ( el && el.style.fontSize && @@ -379,7 +382,7 @@ test.describe( 'Fit Text', () => { await page.goto( postUrl ); - const fitText = page.locator( '.wp-block-fit-text h2' ); + const fitText = page.locator( '.wp-block-fit-text' ); // Wait for fit text to initialize await fitText.waitFor( { state: 'visible' } ); @@ -387,7 +390,9 @@ test.describe( 'Fit Text', () => { // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( '.wp-block-fit-text h2' ); + const el = document.querySelector( + '.wp-block-fit-text' + ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } @@ -395,11 +400,9 @@ test.describe( 'Fit Text', () => { const headings = page.locator( 'h2' ); - const normalFontSize = await headings - .first() - .evaluate( ( el ) => { - return window.getComputedStyle( el ).fontSize; - } ); + const normalFontSize = await headings.first().evaluate( ( el ) => { + return window.getComputedStyle( el ).fontSize; + } ); const fitTextFontSize = await fitText.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; From a776df4d22feae9cd5375e2c34dc75d567782420 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 22:15:47 +0000 Subject: [PATCH 08/13] end to end test fixes --- test/e2e/specs/editor/blocks/fit-text.spec.js | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/test/e2e/specs/editor/blocks/fit-text.spec.js b/test/e2e/specs/editor/blocks/fit-text.spec.js index 1f8231fde237ec..9dc26ac07c52a4 100644 --- a/test/e2e/specs/editor/blocks/fit-text.spec.js +++ b/test/e2e/specs/editor/blocks/fit-text.spec.js @@ -9,7 +9,7 @@ test.describe( 'Fit Text', () => { } ); test.describe( 'Editor functionality', () => { - test( 'should insert a fit text block', async ( { editor, page } ) => { + test( 'should insert a fit text block', async ( { editor } ) => { await editor.insertBlock( { name: 'core/fit-text', attributes: { @@ -137,8 +137,22 @@ test.describe( 'Fit Text', () => { ' that is much longer and should have smaller font' ); - // Wait for DOM to update and fit text to recalculate - await fitTextBlock.waitFor( { state: 'attached' } ); + // Wait for font size to decrease after adding more text + await fitTextBlock.evaluate( ( el, prevSize ) => { + return new Promise( ( resolve ) => { + const checkSize = () => { + const currentSize = parseFloat( + window.getComputedStyle( el ).fontSize + ); + if ( currentSize < prevSize ) { + resolve(); + } else { + window.requestAnimationFrame( checkSize ); + } + }; + checkSize(); + } ); + }, parseFloat( initialFontSize ) ); const newFontSize = await fitTextBlock.evaluate( ( el ) => { return window.getComputedStyle( el ).fontSize; @@ -310,9 +324,7 @@ test.describe( 'Fit Text', () => { // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( - '.wp-block-fit-text' - ); + const el = document.querySelector( '.wp-block-fit-text' ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } @@ -329,9 +341,7 @@ test.describe( 'Fit Text', () => { // Wait for inline font-size style to change after resize await page.waitForFunction( ( previousStyle ) => { - const el = document.querySelector( - '.wp-block-fit-text' - ); + const el = document.querySelector( '.wp-block-fit-text' ); return ( el && el.style.fontSize && @@ -390,9 +400,7 @@ test.describe( 'Fit Text', () => { // Wait for inline style to be applied await page.waitForFunction( () => { - const el = document.querySelector( - '.wp-block-fit-text' - ); + const el = document.querySelector( '.wp-block-fit-text' ); return el && el.style.fontSize && el.style.fontSize !== ''; }, { timeout: 5000 } From d2ca73f19f171d1aa733af17b05b2cefb2901945 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 22:44:06 +0000 Subject: [PATCH 09/13] docs update --- docs/reference-guides/core-blocks.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 1bac37d554d1e7..b2b4bb12bdc185 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -326,6 +326,15 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb - **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity, spacing (margin, padding) - **Attributes:** blob, displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget +## Fit Text + +Add text that automatically scales to fit its container. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/fit-text)) + +- **Name:** core/fit-text +- **Category:** text +- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (lineHeight) +- **Attributes:** content, level, levelOptions + ## Footnotes Display footnotes added to the page. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/footnotes)) @@ -410,7 +419,7 @@ Introduce new sections and organize content to help visitors (and search engines - **Name:** core/heading - **Category:** text -- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight) +- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fontSize, lineHeight) - **Attributes:** content, level, levelOptions, placeholder, textAlign ## Home Link @@ -590,7 +599,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text -- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight), ~~className~~ +- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fontSize, lineHeight), ~~className~~ - **Attributes:** align, content, direction, dropCap, placeholder ## Pattern Placeholder From ba1a774e8bfb780809e872f01bd2f28f08322c70 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 23:09:30 +0000 Subject: [PATCH 10/13] update keywords --- packages/block-library/src/fit-text/block.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json index f2133b1bd66328..534e073fc9f249 100644 --- a/packages/block-library/src/fit-text/block.json +++ b/packages/block-library/src/fit-text/block.json @@ -5,7 +5,7 @@ "title": "Fit Text", "category": "text", "description": "Add text that automatically scales to fit its container.", - "keywords": [ "text", "heading", "resize", "scale" ], + "keywords": [ "text", "resize", "scale" ], "textdomain": "default", "attributes": { "content": { From 106bbd46a6404ddd5c71bb7c814ec3d3dec7cac5 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 23:20:15 +0000 Subject: [PATCH 11/13] update supports --- packages/block-library/src/fit-text/block.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json index 534e073fc9f249..6b52a0313f580c 100644 --- a/packages/block-library/src/fit-text/block.json +++ b/packages/block-library/src/fit-text/block.json @@ -35,7 +35,6 @@ }, "color": { "gradients": true, - "link": true, "__experimentalDefaultControls": { "background": true, "text": true @@ -57,7 +56,6 @@ "__experimentalLetterSpacing": true, "__experimentalTextTransform": true, "__experimentalTextDecoration": true, - "__experimentalWritingMode": true, "__experimentalDefaultControls": {} }, "interactivity": { From 6f028e7d203778fa94dedb9279aae23ac1022811 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Nov 2025 23:45:53 +0000 Subject: [PATCH 12/13] default to paragraph, and --- packages/block-library/src/fit-text/block.json | 2 +- packages/block-library/src/fit-text/edit.js | 1 - test/integration/fixtures/blocks/core__fit-text.html | 2 +- test/integration/fixtures/blocks/core__fit-text.json | 2 +- test/integration/fixtures/blocks/core__fit-text.parsed.json | 4 ++-- .../fixtures/blocks/core__fit-text.serialized.html | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json index 6b52a0313f580c..ca45e6c4252cdf 100644 --- a/packages/block-library/src/fit-text/block.json +++ b/packages/block-library/src/fit-text/block.json @@ -16,7 +16,7 @@ }, "level": { "type": "number", - "default": 2 + "default": 0 }, "levelOptions": { "type": "array", diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js index ba32b05df67aef..dca9a12406e550 100644 --- a/packages/block-library/src/fit-text/edit.js +++ b/packages/block-library/src/fit-text/edit.js @@ -154,7 +154,6 @@ export default function FitTextEdit( { placeholder={ __( 'Add text…' ) } value={ content } onChange={ ( value ) => setAttributes( { content: value } ) } - allowedFormats={ [] } disableLineBreaks __unstableOnSplitAtEnd={ () => insertBlocksAfter( createBlock( getDefaultBlockName() ) ) diff --git a/test/integration/fixtures/blocks/core__fit-text.html b/test/integration/fixtures/blocks/core__fit-text.html index 3a0f15f407e427..0bad0186b0b246 100644 --- a/test/integration/fixtures/blocks/core__fit-text.html +++ b/test/integration/fixtures/blocks/core__fit-text.html @@ -1,3 +1,3 @@ -

Fit Text

+

Fit Text

diff --git a/test/integration/fixtures/blocks/core__fit-text.json b/test/integration/fixtures/blocks/core__fit-text.json index ef96470abb2608..24035c017e63ed 100644 --- a/test/integration/fixtures/blocks/core__fit-text.json +++ b/test/integration/fixtures/blocks/core__fit-text.json @@ -4,7 +4,7 @@ "isValid": true, "attributes": { "content": "Fit Text", - "level": 2, + "level": 0, "levelOptions": [ 0, 1, 2, 3, 4, 5, 6 ] }, "innerBlocks": [] diff --git a/test/integration/fixtures/blocks/core__fit-text.parsed.json b/test/integration/fixtures/blocks/core__fit-text.parsed.json index 39c525d8e4bfb9..d988f7233d620e 100644 --- a/test/integration/fixtures/blocks/core__fit-text.parsed.json +++ b/test/integration/fixtures/blocks/core__fit-text.parsed.json @@ -3,7 +3,7 @@ "blockName": "core/fit-text", "attrs": {}, "innerBlocks": [], - "innerHTML": "\n

Fit Text

\n", - "innerContent": [ "\n

Fit Text

\n" ] + "innerHTML": "\n

Fit Text

\n", + "innerContent": [ "\n

Fit Text

\n" ] } ] diff --git a/test/integration/fixtures/blocks/core__fit-text.serialized.html b/test/integration/fixtures/blocks/core__fit-text.serialized.html index 3a0f15f407e427..0bad0186b0b246 100644 --- a/test/integration/fixtures/blocks/core__fit-text.serialized.html +++ b/test/integration/fixtures/blocks/core__fit-text.serialized.html @@ -1,3 +1,3 @@ -

Fit Text

+

Fit Text

From 3a38dfbaa1a42bb5ea1bff6e7193eb819f9af701 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 4 Nov 2025 00:18:15 +0000 Subject: [PATCH 13/13] update docs --- docs/reference-guides/core-blocks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index b2b4bb12bdc185..6ad2bd1f22570c 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -332,7 +332,7 @@ Add text that automatically scales to fit its container. ([Source](https://githu - **Name:** core/fit-text - **Category:** text -- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (lineHeight) +- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, text), interactivity (clientNavigation), spacing (margin, padding), typography (lineHeight) - **Attributes:** content, level, levelOptions ## Footnotes