Skip to content

Commit

Permalink
Components: Move kebabCase() function from block-editor package and m…
Browse files Browse the repository at this point in the history
…ark it as private API (#56758)

* Components: Move kebabCase() function from block-editor package and mark it as private API

* Update changelog

* Fix native app test

* Try to fix native app test

* Try to fix mobile app test

* Try importing kebabCase directly from components

* Fix another import

* Move changelog entry to Internal section

* Change the argument type of kebabCase function to unknown

* Fix lint error

* Merge duplicate Internal sections

* Remove type info in the JSDoc comment

---------

Co-authored-by: Marin Atanasov <tyxla@abv.bg>
  • Loading branch information
t-hamano and tyxla authored Dec 14, 2023
1 parent 59e8958 commit e103afb
Show file tree
Hide file tree
Showing 21 changed files with 214 additions and 161 deletions.
9 changes: 8 additions & 1 deletion packages/block-editor/src/components/colors/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import { colord, extend } from 'colord';
import namesPlugin from 'colord/plugins/names';
import a11yPlugin from 'colord/plugins/a11y';

/**
* WordPress dependencies
*/
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
*/
import { kebabCase } from '../../utils/object';
import { unlock } from '../../lock-unlock';

extend( [ namesPlugin, a11yPlugin ] );

Expand Down Expand Up @@ -70,6 +75,8 @@ export function getColorClassName( colorContextName, colorSlug ) {
return undefined;
}

const { kebabCase } = unlock( componentsPrivateApis );

This comment has been minimized.

Copy link
@ellatrix

ellatrix Dec 14, 2023

Member

We are we not calling this once outside the function? Pretty sure all of this stuff is causing a spike in performance metrics...

This comment has been minimized.

Copy link
@ellatrix

ellatrix Dec 14, 2023

Member

I'll try it in #57042 and investigate the errors


return `has-${ kebabCase( colorSlug ) }-${ colorContextName }`;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/block-editor/src/components/colors/with-colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { useMemo, Component } from '@wordpress/element';
import { compose, createHigherOrderComponent } from '@wordpress/compose';
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
Expand All @@ -14,7 +15,7 @@ import {
getMostReadableColor,
} from './utils';
import { useSettings } from '../use-settings';
import { kebabCase } from '../../utils/object';
import { unlock } from '../../lock-unlock';

/**
* Capitalizes the first letter in a string.
Expand Down Expand Up @@ -79,6 +80,7 @@ const withEditorColorPalette = () =>
* @return {Component} The component that can be used as a HOC.
*/
function createColorHOC( colorTypes, withColorPalette ) {
const { kebabCase } = unlock( componentsPrivateApis );
const colorMap = colorTypes.reduce( ( colorObject, colorType ) => {
return {
...colorObject,
Expand Down
8 changes: 7 additions & 1 deletion packages/block-editor/src/components/font-sizes/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/**
* WordPress dependencies
*/
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
*/
import { kebabCase } from '../../utils/object';
import { unlock } from '../../lock-unlock';

/**
* Returns the font size object based on an array of named font sizes and the namedFontSize and customFontSize values.
Expand Down Expand Up @@ -64,5 +69,6 @@ export function getFontSizeClass( fontSizeSlug ) {
return;
}

const { kebabCase } = unlock( componentsPrivateApis );
return `has-${ kebabCase( fontSizeSlug ) }-font-size`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { useSelect } from '@wordpress/data';
import { useContext, useMemo } from '@wordpress/element';
import { getCSSRules } from '@wordpress/style-engine';
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
Expand All @@ -32,12 +33,9 @@ import { getDuotoneFilter } from '../duotone/utils';
import { getGapCSSValue } from '../../hooks/gap';
import { store as blockEditorStore } from '../../store';
import { LAYOUT_DEFINITIONS } from '../../layouts/definitions';
import {
getValueFromObjectPath,
kebabCase,
setImmutably,
} from '../../utils/object';
import { getValueFromObjectPath, setImmutably } from '../../utils/object';
import BlockContext from '../block-context';
import { unlock } from '../../lock-unlock';

// List of block support features that can have their related styles
// generated under their own feature level selector rather than the block's.
Expand Down Expand Up @@ -72,6 +70,8 @@ function compileStyleValue( uncompiledValue ) {
* @return {Array<Object>} An array of style declarations.
*/
function getPresetsDeclarations( blockPresets = {}, mergedSettings ) {
const { kebabCase } = unlock( componentsPrivateApis );

return PRESET_METADATA.reduce(
( declarations, { path, valueKey, valueFunc, cssVarInfix } ) => {
const presetByOrigin = getValueFromObjectPath(
Expand Down Expand Up @@ -116,6 +116,8 @@ function getPresetsDeclarations( blockPresets = {}, mergedSettings ) {
* @return {string} CSS declarations for the preset classes.
*/
function getPresetsClasses( blockSelector = '*', blockPresets = {} ) {
const { kebabCase } = unlock( componentsPrivateApis );

return PRESET_METADATA.reduce(
( declarations, { path, cssVarInfix, classes } ) => {
if ( ! classes ) {
Expand Down Expand Up @@ -180,6 +182,7 @@ function getPresetsSvgFilters( blockPresets = {} ) {
}

function flattenTree( input = {}, prefix, token ) {
const { kebabCase } = unlock( componentsPrivateApis );
let result = [];
Object.keys( input ).forEach( ( key ) => {
const newKey = prefix + kebabCase( key.replace( '/', '-' ) );
Expand Down Expand Up @@ -321,6 +324,7 @@ export function getStylesDeclarations(
tree = {},
isTemplate = true
) {
const { kebabCase } = unlock( componentsPrivateApis );
const isRoot = ROOT_BLOCK_SELECTOR === selector;
const output = Object.entries( STYLE_PROPERTY ).reduce(
(
Expand Down
4 changes: 3 additions & 1 deletion packages/block-editor/src/hooks/font-family.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import { addFilter } from '@wordpress/hooks';
import { hasBlockSupport } from '@wordpress/blocks';
import TokenList from '@wordpress/token-list';
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
*/
import { shouldSkipSerialization } from './utils';
import { TYPOGRAPHY_SUPPORT_KEY } from './typography';
import { kebabCase } from '../utils/object';
import { unlock } from '../lock-unlock';

export const FONT_FAMILY_SUPPORT_KEY = 'typography.__experimentalFontFamily';

Expand Down Expand Up @@ -67,6 +68,7 @@ function addSaveProps( props, blockType, attributes ) {

// Use TokenList to dedupe classes.
const classes = new TokenList( props.className );
const { kebabCase } = unlock( componentsPrivateApis );
classes.add( `has-${ kebabCase( attributes?.fontFamily ) }-font-family` );
const newClassName = classes.value;
props.className = newClassName ? newClassName : undefined;
Expand Down
5 changes: 4 additions & 1 deletion packages/block-editor/src/hooks/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ButtonGroup,
ToggleControl,
PanelBody,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

Expand All @@ -27,8 +28,8 @@ import { useSettings } from '../components/use-settings';
import { getLayoutType, getLayoutTypes } from '../layouts';
import { useBlockEditingMode } from '../components/block-editing-mode';
import { LAYOUT_DEFINITIONS } from '../layouts/definitions';
import { kebabCase } from '../utils/object';
import { useBlockSettings, useStyleOverride } from './utils';
import { unlock } from '../lock-unlock';

const layoutBlockSupportKey = 'layout';

Expand All @@ -48,6 +49,7 @@ function hasLayoutBlockSupport( blockName ) {
* @return { Array } Array of CSS classname strings.
*/
export function useLayoutClasses( blockAttributes = {}, blockName = '' ) {
const { kebabCase } = unlock( componentsPrivateApis );
const rootPaddingAlignment = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
return getSettings().__experimentalFeatures
Expand Down Expand Up @@ -348,6 +350,7 @@ function BlockWithLayoutStyles( { block: BlockListBlock, props } ) {
: layout || defaultBlockLayout || {};
const layoutClasses = useLayoutClasses( attributes, name );

const { kebabCase } = unlock( componentsPrivateApis );
const selectorPrefix = `wp-container-${ kebabCase( name ) }-layout-`;
// Higher specificity to override defaults from theme.json.
const selector = `.${ selectorPrefix }${ id }.${ selectorPrefix }${ id }`;
Expand Down
8 changes: 7 additions & 1 deletion packages/block-editor/src/hooks/use-typography-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { privateApis as componentsPrivateApis } from '@wordpress/components';

/**
* Internal dependencies
*/
Expand All @@ -12,7 +17,7 @@ import {
getTypographyFontSizeValue,
getFluidTypographyOptionsFromSettings,
} from '../components/global-styles/typography-utils';
import { kebabCase } from '../utils/object';
import { unlock } from '../lock-unlock';

/*
* This utility is intended to assist where the serialization of the typography
Expand All @@ -29,6 +34,7 @@ import { kebabCase } from '../utils/object';
* @return {Object} Typography block support derived CSS classes & styles.
*/
export function getTypographyClassesAndStyles( attributes, settings ) {
const { kebabCase } = unlock( componentsPrivateApis );
let typographyStyles = attributes?.style?.typography || {};
const fluidTypographySettings =
getFluidTypographyOptionsFromSettings( settings );
Expand Down
2 changes: 0 additions & 2 deletions packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import * as globalStyles from './components/global-styles';
import { ExperimentalBlockEditorProvider } from './components/provider';
import { lock } from './lock-unlock';
import { getRichTextValues } from './components/rich-text/get-rich-text-values';
import { kebabCase } from './utils/object';
import ResizableBoxPopover from './components/resizable-box-popover';
import { ComposedPrivateInserter as PrivateInserter } from './components/inserter';
import { PrivateListView } from './components/list-view';
Expand Down Expand Up @@ -36,7 +35,6 @@ lock( privateApis, {
ExperimentalBlockEditorProvider,
getDuotoneFilter,
getRichTextValues,
kebabCase,
PrivateInserter,
PrivateListView,
ResizableBoxPopover,
Expand Down
2 changes: 0 additions & 2 deletions packages/block-editor/src/private-apis.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/
import * as globalStyles from './components/global-styles';
import { ExperimentalBlockEditorProvider } from './components/provider';
import { kebabCase } from './utils/object';
import { lock } from './lock-unlock';

/**
Expand All @@ -12,6 +11,5 @@ import { lock } from './lock-unlock';
export const privateApis = {};
lock( privateApis, {
...globalStyles,
kebabCase,
ExperimentalBlockEditorProvider,
} );
35 changes: 0 additions & 35 deletions packages/block-editor/src/utils/object.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
/**
* External dependencies
*/
import { paramCase } from 'change-case';

/**
* Converts any string to kebab case.
* Backwards compatible with Lodash's `_.kebabCase()`.
* Backwards compatible with `_wp_to_kebab_case()`.
*
* @see https://lodash.com/docs/4.17.15#kebabCase
* @see https://developer.wordpress.org/reference/functions/_wp_to_kebab_case/
*
* @param {string} str String to convert.
* @return {string} Kebab-cased string
*/
export function kebabCase( str ) {
let input = str;
if ( typeof str !== 'string' ) {
input = str?.toString?.() ?? '';
}

// See https://github.com/lodash/lodash/blob/b185fcee26b2133bd071f4aaca14b455c2ed1008/lodash.js#L4970
input = input.replace( /['\u2019]/, '' );

return paramCase( input, {
splitRegexp: [
/(?!(?:1ST|2ND|3RD|[4-9]TH)(?![a-z]))([a-z0-9])([A-Z])/g, // fooBar => foo-bar, 3Bar => 3-bar
/(?!(?:1st|2nd|3rd|[4-9]th)(?![a-z]))([0-9])([a-z])/g, // 3bar => 3-bar
/([A-Za-z])([0-9])/g, // Foo3 => foo-3, foo3 => foo-3
/([A-Z])([A-Z][a-z])/g, // FOOBar => foo-bar
],
} );
}

/**
* Immutably sets a value inside an object. Like `lodash#set`, but returning a
* new object. Treats nullish initial values as empty objects. Clones any
Expand Down
97 changes: 1 addition & 96 deletions packages/block-editor/src/utils/test/object.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,7 @@
/**
* Internal dependencies
*/
import { kebabCase, setImmutably } from '../object';

describe( 'kebabCase', () => {
it( 'separates lowercase letters, followed by uppercase letters', () => {
expect( kebabCase( 'fooBar' ) ).toEqual( 'foo-bar' );
} );

it( 'separates numbers, followed by uppercase letters', () => {
expect( kebabCase( '123FOO' ) ).toEqual( '123-foo' );
} );

it( 'separates numbers, followed by lowercase characters', () => {
expect( kebabCase( '123bar' ) ).toEqual( '123-bar' );
} );

it( 'separates uppercase letters, followed by numbers', () => {
expect( kebabCase( 'FOO123' ) ).toEqual( 'foo-123' );
} );

it( 'separates lowercase letters, followed by numbers', () => {
expect( kebabCase( 'foo123' ) ).toEqual( 'foo-123' );
} );

it( 'separates uppercase groups from capitalized groups', () => {
expect( kebabCase( 'FOOBar' ) ).toEqual( 'foo-bar' );
} );

it( 'removes any non-dash special characters', () => {
expect(
kebabCase( 'foo±§!@#$%^&*()-_=+/?.>,<\\|{}[]`~\'";:bar' )
).toEqual( 'foo-bar' );
} );

it( 'removes any spacing characters', () => {
expect( kebabCase( ' foo \t \n \r \f \v bar ' ) ).toEqual( 'foo-bar' );
} );

it( 'groups multiple dashes into a single one', () => {
expect( kebabCase( 'foo---bar' ) ).toEqual( 'foo-bar' );
} );

it( 'returns an empty string unchanged', () => {
expect( kebabCase( '' ) ).toEqual( '' );
} );

it( 'returns an existing kebab case string unchanged', () => {
expect( kebabCase( 'foo-123-bar' ) ).toEqual( 'foo-123-bar' );
} );

it( 'returns an empty string if any nullish type is passed', () => {
expect( kebabCase( undefined ) ).toEqual( '' );
expect( kebabCase( null ) ).toEqual( '' );
} );

it( 'converts any unexpected non-nullish type to a string', () => {
expect( kebabCase( 12345 ) ).toEqual( '12345' );
expect( kebabCase( [] ) ).toEqual( '' );
expect( kebabCase( {} ) ).toEqual( 'object-object' );
} );

/**
* Should cover all test cases of `_wp_to_kebab_case()`.
*
* @see https://developer.wordpress.org/reference/functions/_wp_to_kebab_case/
* @see https://github.com/WordPress/wordpress-develop/blob/76376fdbc3dc0b3261de377dffc350677345e7ba/tests/phpunit/tests/functions/wpToKebabCase.php#L35-L62
*/
it.each( [
[ 'white', 'white' ],
[ 'white+black', 'white-black' ],
[ 'white:black', 'white-black' ],
[ 'white*black', 'white-black' ],
[ 'white.black', 'white-black' ],
[ 'white black', 'white-black' ],
[ 'white black', 'white-black' ],
[ 'white-to-black', 'white-to-black' ],
[ 'white2white', 'white-2-white' ],
[ 'white2nd', 'white-2nd' ],
[ 'white2ndcolor', 'white-2-ndcolor' ],
[ 'white2ndColor', 'white-2nd-color' ],
[ 'white2nd_color', 'white-2nd-color' ],
[ 'white23color', 'white-23-color' ],
[ 'white23', 'white-23' ],
[ '23color', '23-color' ],
[ 'white4th', 'white-4th' ],
[ 'font2xl', 'font-2-xl' ],
[ 'whiteToWhite', 'white-to-white' ],
[ 'whiteTOwhite', 'white-t-owhite' ],
[ 'WHITEtoWHITE', 'whit-eto-white' ],
[ 42, '42' ],
[ "i've done", 'ive-done' ],
[ '#ffffff', 'ffffff' ],
[ '$ffffff', 'ffffff' ],
] )( 'converts %s properly to %s', ( input, expected ) => {
expect( kebabCase( input ) ).toEqual( expected );
} );
} );
import { setImmutably } from '../object';

describe( 'setImmutably', () => {
describe( 'handling falsy values properly', () => {
Expand Down
Loading

1 comment on commit e103afb

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in e103afb.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7203282968
📝 Reported issues:

Please sign in to comment.