Skip to content
1 change: 1 addition & 0 deletions packages/block-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
- Convert the keyboard shortcut components, `ObserveTyping`, `Typewriter`, `BlockSelectionClearer`, `SkipToSelectedBlock` and `MultiSelectScrollIntoView` to TypeScript ([#84902](https://github.com/WordPress/gutenberg/pull/84902)).
- Convert `useOnBlockDrop`, `usePasteStyles`, `useMovingAnimation`, `useFlashEditableBlocks`, `useBlockCommands`, the convert to group buttons, the note icon slots, `useListViewPanelState` and `useResizeCanvas` to TypeScript ([#84906](https://github.com/WordPress/gutenberg/pull/84906)).
- Convert `SpacingSizesControl`, `FitTextSizeWarning`, the line height hook and the spacing visualizer to TypeScript ([#84907](https://github.com/WordPress/gutenberg/pull/84907)).
- Convert the block color contrast warning hook, the block bindings panel, the block style variation hook and the position hook to TypeScript ([#84908](https://github.com/WordPress/gutenberg/pull/84908)).

## 18.1.0 (2026-10-07)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,25 @@ import { unlock } from '../lock-unlock';
import InspectorControls from '../components/inspector-controls';
import BlockContext from '../components/block-context';
import { store as blockEditorStore } from '../store';
import type { BlockBindingsPanelProps } from './types';

const useToolsPanelDropdownMenuProps = () => {
const isMobile = useViewportMatch( 'medium', '<' );
return ! isMobile
? {
popoverProps: {
placement: 'left-start',
placement: 'left-start' as const,
// For non-mobile, inner sidebar width (248px) - button width (24px) - border (1px) + padding (16px) + spacing (20px)
offset: 259,
},
}
: {};
};

export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
export const BlockBindingsPanel = ( {
name: blockName,
metadata,
}: BlockBindingsPanelProps ) => {
const blockContext = useContext( BlockContext );
const { removeAllBlockBindings } = useBlockBindingsUtils();
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
Expand All @@ -45,10 +49,11 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
} = unlock( select( blocksStore ) );

return {
bindableAttributes:
__experimentalBlockBindingsSupportedAttributes?.[
blockName
],
// The setting maps block names to their bindable attributes.
bindableAttributes: (
__experimentalBlockBindingsSupportedAttributes as
Record< string, string[] > | undefined
)?.[ blockName ],
hasCompatibleFields: Object.values(
getAllBlockBindingsSources()
).some(
Expand Down Expand Up @@ -111,7 +116,7 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
export default {
edit: BlockBindingsPanel,
attributeKeys: [ 'metadata' ],
hasSupport( name ) {
hasSupport( name: string ) {
return ! [
'core/post-date',
'core/navigation-link',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ import { usePrivateStyleOverride } from './utils';
import { store as blockEditorStore } from '../store';
import { globalStylesDataKey } from '../store/private-keys';
import { unlock } from '../lock-unlock';
import type {
BlockListBlockFeatureProps,
BlockStyleVariationOverridesWithConfigProps,
OverrideStylesProps,
StyleOverrideOptions,
} from './types';

const { getVariationStyle } = unlock( globalStylesEnginePrivateApis );

const VARIATION_PREFIX = 'is-style-';

function getVariationMatches( className ) {
function getVariationMatches( className?: string ): string[] {
if ( ! className ) {
return [];
}
return className.split( /\s+/ ).reduce( ( matches, name ) => {
return className.split( /\s+/ ).reduce< string[] >( ( matches, name ) => {
if ( name.startsWith( VARIATION_PREFIX ) ) {
const match = name.slice( VARIATION_PREFIX.length );
if ( match !== 'default' ) {
Expand All @@ -33,12 +39,15 @@ function getVariationMatches( className ) {
/**
* Get the first block style variation that has been registered from the class string.
*
* @param {string} className CSS class string for a block.
* @param {Array} registeredStyles Currently registered block styles.
* @param className CSS class string for a block.
* @param registeredStyles Currently registered block styles.
*
* @return {string|null} The name of the first registered variation.
* @return The name of the first registered variation.
*/
export function getVariationNameFromClass( className, registeredStyles = [] ) {
export function getVariationNameFromClass(
className?: string,
registeredStyles: Array< { name: string } > = []
): string | null {
// The global flag affects how capturing groups work in JS. So the regex
// below will only return full CSS classes not just the variation name.
const matches = getVariationMatches( className );
Expand All @@ -56,7 +65,7 @@ export function getVariationNameFromClass( className, registeredStyles = [] ) {
}

// A helper component to apply a style override using the useStyleOverride hook.
function OverrideStyles( { override } ) {
function OverrideStyles( { override }: OverrideStylesProps ): undefined {
usePrivateStyleOverride( override );
}

Expand All @@ -67,11 +76,13 @@ function OverrideStyles( { override } ) {
* useStyleOverride to apply the new styles to the editor. Its use is
* subject to change.
*
* @param {Object} props Props.
* @param {Object} props.config A global styles object, containing settings and styles.
* @return {React.JSX.Element} An array of new block variation overrides.
* @param props Props.
* @param props.config A global styles object, containing settings and styles.
* @return An array of new block variation overrides.
*/
export function BlockStyleVariationOverridesWithConfig( { config } ) {
export function BlockStyleVariationOverridesWithConfig( {
config,
}: BlockStyleVariationOverridesWithConfigProps ) {
const { overrides } = useSelect(
( select ) => ( {
overrides: unlock( select( blockEditorStore ) ).getStyleOverrides(),
Expand All @@ -84,8 +95,8 @@ export function BlockStyleVariationOverridesWithConfig( { config } ) {
if ( ! overrides?.length ) {
return;
}
const newOverrides = [];
const overriddenClientIds = [];
const newOverrides: StyleOverrideOptions[] = [];
const overriddenClientIds: string[] = [];
for ( const [ , override ] of overrides ) {
if (
override?.variation &&
Expand All @@ -96,7 +107,8 @@ export function BlockStyleVariationOverridesWithConfig( { config } ) {
*/
! overriddenClientIds.includes( override.clientId )
) {
const blockName = getBlockName( override.clientId );
// Overrides belong to blocks in the editor.
const blockName = getBlockName( override.clientId )!;
const configStyles =
config?.styles?.blocks?.[ blockName ]?.variations?.[
override.variation
Expand Down Expand Up @@ -173,7 +185,11 @@ export function BlockStyleVariationOverridesWithConfig( { config } ) {
);
}

function useBlockStyleVariation( name, variation, clientId ) {
function useBlockStyleVariation(
name: string,
variation: string | null,
clientId: string
) {
const { globalSettings, globalStyles } = useSelect( ( select ) => {
const settings = select( blockEditorStore ).getSettings();
return {
Expand Down Expand Up @@ -213,7 +229,11 @@ function useBlockStyleVariation( name, variation, clientId ) {
// Rather than leveraging `useInstanceId` here, the `clientId` is used.
// This is so that the variation style override's ID is predictable
// when the order of applied style variations changes.
function useBlockProps( { name, className, clientId } ) {
function useBlockProps( {
name,
className,
clientId,
}: BlockListBlockFeatureProps & { className?: string } ) {
const { getBlockStyles } = useSelect( blocksStore );

const registeredStyles = getBlockStyles( name );
Expand Down Expand Up @@ -274,6 +294,7 @@ function useBlockProps( { name, className, clientId } ) {
export default {
hasSupport: () => true,
attributeKeys: [ 'className' ],
isMatch: ( { className } ) => getVariationMatches( className ).length > 0,
isMatch: ( { className }: { className?: string } ) =>
getVariationMatches( className ).length > 0,
useBlockProps,
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { useEffect, useLayoutEffect, useReducer } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { store as blocksStore } from '@wordpress/blocks';
import type { BlockType } from '@wordpress/blocks';
import { getBlockSelector } from '@wordpress/global-styles-engine';
import { speak } from '@wordpress/a11y';
import { getContrastWarning } from '../components/contrast-checker';
import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';

function getComputedValue( node, property ) {
return node.ownerDocument.defaultView
.getComputedStyle( node )
import type {
BlockElementColors,
UseBlockColorContrastWarningOptions,
} from './types';

function getComputedValue( node: Element, property: string ) {
// A rendered block element has a window.
return node.ownerDocument
.defaultView!.getComputedStyle( node )
.getPropertyValue( property );
}

function getBlockElementColors( blockEl, blockType ) {
function getBlockElementColors(
blockEl: Element | null,
blockType: BlockType | null | undefined
): BlockElementColors {
if ( ! blockEl || ! blockType ) {
return {};
}
Expand All @@ -28,9 +37,10 @@ function getBlockElementColors( blockEl, blockType ) {
);

// Find target elements - querySelector handles all the complexity
const textElement = blockEl.querySelector( textSelector ) || blockEl;
// The `fallback` option always returns a selector.
const textElement = blockEl.querySelector( textSelector! ) || blockEl;
const backgroundElement =
blockEl.querySelector( backgroundSelector ) || blockEl;
blockEl.querySelector( backgroundSelector! ) || blockEl;
const linkElement = blockEl.querySelector( 'a' );

// Get computed colors from the appropriate elements
Expand All @@ -51,7 +61,8 @@ function getBlockElementColors( blockEl, blockType ) {
backgroundColorNode.parentNode.nodeType ===
backgroundColorNode.parentNode.ELEMENT_NODE
) {
backgroundColorNode = backgroundColorNode.parentNode;
// The loop only climbs to element parents.
backgroundColorNode = backgroundColorNode.parentNode as Element;
backgroundColor = getComputedValue(
backgroundColorNode,
'background-color'
Expand All @@ -65,9 +76,14 @@ function getBlockElementColors( blockEl, blockType ) {
};
}

function reducer( prevColors, newColors ) {
function reducer(
prevColors: BlockElementColors,
newColors: BlockElementColors
): BlockElementColors {
const hasChanged = Object.keys( newColors ).some(
( key ) => prevColors[ key ] !== newColors[ key ]
( key ) =>
prevColors[ key as keyof BlockElementColors ] !==
newColors[ key as keyof BlockElementColors ]
);

// Do not re-render if the colors have not changed.
Expand All @@ -81,15 +97,15 @@ function reducer( prevColors, newColors ) {
* Colors are read from the rendered block element so that inherited values
* (e.g. from Global Styles or ancestor blocks) are taken into account.
*
* @param {Object} props
* @param {string} props.clientId Block client ID.
* @param {string} props.name Block name.
* @param {boolean} [props.enabled] Whether contrast checking is active.
* @param {boolean} [props.checkTextColor] Whether to evaluate the text/background pair.
* @param {boolean} [props.checkLinkColor] Whether to evaluate the link/background pair.
* @param {string} [props.messageOverride] Caller-provided copy used in place of the generic guidance.
* @param props Hook options.
* @param props.clientId Block client ID.
* @param props.name Block name.
* @param props.enabled Whether contrast checking is active.
* @param props.checkTextColor Whether to evaluate the text/background pair.
* @param props.checkLinkColor Whether to evaluate the link/background pair.
* @param props.messageOverride Caller-provided copy used in place of the generic guidance.
*
* @return {?string} The warning message, if any.
* @return The warning message, if any.
*/
export default function useBlockColorContrastWarning( {
clientId,
Expand All @@ -98,7 +114,7 @@ export default function useBlockColorContrastWarning( {
checkTextColor = true,
checkLinkColor = true,
messageOverride,
} ) {
}: UseBlockColorContrastWarningOptions ) {
const blockEl = useBlockElement( clientId );
const [ colors, setColors ] = useReducer( reducer, {} );

Expand Down
Loading
Loading