fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

View File

@@ -0,0 +1,645 @@
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
import UnitControl from '..';
import { CSS_UNITS, parseQuantityAndUnitFromRawValue } from '../utils';
const getInput = ( {
isInputTypeText = false,
}: {
isInputTypeText?: boolean;
} = {} ) =>
screen.getByRole(
isInputTypeText ? 'textbox' : 'spinbutton'
) as HTMLInputElement;
const getSelect = () => screen.getByRole( 'combobox' ) as HTMLSelectElement;
const getSelectOptions = () =>
screen.getAllByRole( 'option' ) as HTMLOptionElement[];
const ControlledSyncUnits = () => {
const [ state, setState ] = useState( {
valueA: '',
valueB: '',
} );
// Keep the unit sync'd between the two `UnitControl` instances.
const onUnitControlChange = (
fieldName: 'valueA' | 'valueB',
newValue?: string | number
) => {
const parsedQuantityAndUnit =
parseQuantityAndUnitFromRawValue( newValue );
const quantity = parsedQuantityAndUnit[ 0 ];
if ( ! Number.isFinite( quantity ) ) {
return;
}
const newUnit = parsedQuantityAndUnit[ 1 ];
const nextState = {
...state,
[ fieldName ]: newValue,
};
Object.entries( state ).forEach( ( [ stateProp, stateValue ] ) => {
const [ stateQuantity, stateUnit ] =
parseQuantityAndUnitFromRawValue( stateValue );
if ( stateProp !== fieldName && stateUnit !== newUnit ) {
nextState[
stateProp as 'valueA' | 'valueB'
] = `${ stateQuantity }${ newUnit }`;
}
} );
setState( nextState );
};
return (
<>
<UnitControl
label="Field A"
value={ state.valueA }
onChange={ ( v ) => onUnitControlChange( 'valueA', v ) }
/>
<UnitControl
label="Field B"
value={ state.valueB }
onChange={ ( v ) => onUnitControlChange( 'valueB', v ) }
/>
</>
);
};
describe( 'UnitControl', () => {
describe( 'Basic rendering', () => {
it( 'should render', () => {
render( <UnitControl /> );
const input = getInput();
const select = getSelect();
expect( input ).toBeInTheDocument();
expect( select ).toBeInTheDocument();
} );
it( 'should render custom className', () => {
const { container: withoutClassName } = render( <UnitControl /> );
const { container: withClassName } = render(
<UnitControl className="hello" />
);
expect(
// eslint-disable-next-line testing-library/no-node-access
withoutClassName.querySelector( '.components-unit-control' )
).not.toHaveClass( 'hello' );
expect(
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
withClassName.querySelector( '.components-unit-control' )
).toHaveClass( 'hello' );
} );
it( 'should not render select, if units are disabled', () => {
render( <UnitControl value="3em" units={ [] } /> );
const input = getInput();
// Using `queryByRole` instead of `getSelect` because we need to test
// for this element NOT to be in the document.
const select = screen.queryByRole( 'combobox' );
expect( input ).toBeInTheDocument();
expect( select ).not.toBeInTheDocument();
} );
it( 'should render label if single units', () => {
render( <UnitControl units={ [ { value: '%', label: '%' } ] } /> );
const select = screen.queryByRole( 'combobox' );
const label = screen.getByText( '%' );
expect( select ).not.toBeInTheDocument();
expect( label ).toBeInTheDocument();
} );
} );
describe( 'Value', () => {
it( 'should update value on change', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render( <UnitControl value={ '50px' } onChange={ onChangeSpy } /> );
const input = getInput();
await user.clear( input );
await user.type( input, '62' );
// 3 times:
// - 1: clear
// - 2: type '6'
// - 3: type '62'
expect( onChangeSpy ).toHaveBeenCalledTimes( 3 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'62px',
expect.anything()
);
} );
it( 'should increment value on UP press', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render( <UnitControl value={ '50px' } onChange={ onChangeSpy } /> );
const input = getInput();
await user.type( input, '{ArrowUp}' );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'51px',
expect.anything()
);
} );
it( 'should increment value on UP + SHIFT press, with step', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render( <UnitControl value={ '50px' } onChange={ onChangeSpy } /> );
const input = getInput();
await user.type( input, '{Shift>}{ArrowUp}{/Shift}' );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'60px',
expect.anything()
);
} );
it( 'should decrement value on DOWN press', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render( <UnitControl value={ 50 } onChange={ onChangeSpy } /> );
const input = getInput();
await user.type( input, '{ArrowDown}' );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'49px',
expect.anything()
);
} );
it( 'should decrement value on DOWN + SHIFT press, with step', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render( <UnitControl value={ 50 } onChange={ onChangeSpy } /> );
const input = getInput();
await user.type( input, '{Shift>}{ArrowDown}{/Shift}' );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'40px',
expect.anything()
);
} );
it( 'should cancel change when ESCAPE key is pressed', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render(
<UnitControl
value={ 50 }
onChange={ onChangeSpy }
isPressEnterToChange
/>
);
// Input type is `text` when the `isPressEnterToChange` prop is passed
const input = getInput( { isInputTypeText: true } );
await user.clear( input );
await user.type( input, '300' );
expect( input.value ).toBe( '300' );
expect( onChangeSpy ).not.toHaveBeenCalled();
await user.keyboard( '{Escape}' );
expect( input.value ).toBe( '50' );
expect( onChangeSpy ).not.toHaveBeenCalled();
} );
it( 'should run onBlur callback when quantity input is blurred', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
const onBlurSpy = jest.fn();
render(
<UnitControl
value={ '33%' }
onChange={ onChangeSpy }
onBlur={ onBlurSpy }
/>
);
const input = getInput();
await user.clear( input );
await user.type( input, '41' );
expect( onChangeSpy ).toHaveBeenCalledTimes( 3 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'41%',
expect.anything()
);
// Clicking document.body to trigger a blur event on the input.
await user.click( document.body );
expect( onBlurSpy ).toHaveBeenCalledTimes( 1 );
} );
it( 'should invoke onChange when isPressEnterToChange is true and the input is blurred with an uncommitted value', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render(
<UnitControl
value={ '15px' }
onChange={ onChangeSpy }
isPressEnterToChange
/>
);
// Input type is `text` when the `isPressEnterToChange` prop is passed
const input = getInput( { isInputTypeText: true } );
await user.clear( input );
// Typing the first letter of a unit blurs the input.
await user.type( input, '41v' );
// Called only once because `isPressEnterToChange` is `true`.
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
// The correct expected behavior would be for the `onChangeSpy` callback
// to be called twice, first with `41px` and immediately after with `41vh`,
// but the test environment doesn't seem to change values on `select`
// elements when using the keyboard.
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'41px',
expect.anything()
);
} );
it( 'should update value correctly when typed and blurred when a single unit is passed', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render(
<>
<button>Click me</button>
<UnitControl
units={ [ { value: '%', label: '%' } ] }
onChange={ onChangeSpy }
/>
</>
);
const input = getInput();
await user.type( input, '62' );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'62%',
expect.anything()
);
// Start counting again calls to `onChangeSpy`.
onChangeSpy.mockClear();
// Clicking on the button should cause the `onBlur` callback to fire.
const button = screen.getByRole( 'button' );
await user.click( button );
expect( onChangeSpy ).not.toHaveBeenCalled();
} );
} );
describe( 'Unit', () => {
it( 'should update unit value on change', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
const onUnitChangeSpy = jest.fn();
render(
<UnitControl
value={ '14rem' }
onChange={ onChangeSpy }
onUnitChange={ onUnitChangeSpy }
/>
);
const select = getSelect();
await user.selectOptions( select, [ 'px' ] );
expect( onUnitChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onUnitChangeSpy ).toHaveBeenLastCalledWith(
'px',
expect.anything()
);
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'14px',
expect.anything()
);
} );
it( 'should render customized units, if defined', () => {
const units = [
{ value: 'pt', label: 'pt', default: 0 },
{ value: 'vmax', label: 'vmax', default: 10 },
// Proves that units with regex control characters don't error.
{ value: '+', label: '+', default: 10 },
];
render( <UnitControl units={ units } /> );
const options = getSelectOptions();
expect( options.length ).toBe( 3 );
const [ pt, vmax, plus ] = options;
expect( pt.value ).toBe( 'pt' );
expect( vmax.value ).toBe( 'vmax' );
expect( plus.value ).toBe( '+' );
} );
it( 'should reset value on unit change, if unit has default value', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
const units = [
{ value: 'pt', label: 'pt', default: 25 },
{ value: 'vmax', label: 'vmax', default: 75 },
];
render(
<UnitControl
isResetValueOnUnitChange
units={ units }
onChange={ onChangeSpy }
value={ 50 }
/>
);
const select = getSelect();
await user.selectOptions( select, [ 'vmax' ] );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'75vmax',
expect.anything()
);
await user.selectOptions( select, [ 'pt' ] );
expect( onChangeSpy ).toHaveBeenCalledTimes( 2 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'25pt',
expect.anything()
);
} );
it( 'should not reset value on unit change, if disabled', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
const units = [
{ value: 'pt', label: 'pt', default: 25 },
{ value: 'vmax', label: 'vmax', default: 75 },
];
render(
<UnitControl
isResetValueOnUnitChange={ false }
value={ 50 }
units={ units }
onChange={ onChangeSpy }
/>
);
const select = getSelect();
await user.selectOptions( select, [ 'vmax' ] );
expect( onChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'50vmax',
expect.anything()
);
await user.selectOptions( select, [ 'pt' ] );
expect( onChangeSpy ).toHaveBeenCalledTimes( 2 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'50pt',
expect.anything()
);
} );
it( 'should set correct unit if single units', async () => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
render(
<UnitControl
value={ '50%' }
units={ [ { value: '%', label: '%' } ] }
onChange={ onChangeSpy }
/>
);
const input = getInput();
await user.clear( input );
await user.type( input, '62' );
// 3 times:
// - 1: clear
// - 2: type '6'
// - 3: type '62'
expect( onChangeSpy ).toHaveBeenCalledTimes( 3 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'62%',
expect.anything()
);
} );
it( 'should update unit value when a new raw value is passed', async () => {
const user = userEvent.setup();
render( <ControlledSyncUnits /> );
const [ inputA, inputB ] = screen.getAllByRole( 'spinbutton' );
const [ selectA, selectB ] = screen.getAllByRole( 'combobox' );
const [ remOptionA ] = screen.getAllByRole( 'option', {
name: 'rem',
} );
const [ , vwOptionB ] = screen.getAllByRole( 'option', {
name: 'vw',
} );
await user.type( inputA, '55' );
await user.type( inputB, '14' );
await user.selectOptions( selectA, remOptionA );
expect( selectB ).toHaveValue( 'rem' );
expect( selectA ).toHaveValue( 'rem' );
await user.selectOptions( selectB, vwOptionB );
expect( selectA ).toHaveValue( 'vw' );
expect( selectB ).toHaveValue( 'vw' );
} );
it( 'should maintain the chosen non-default unit when value is cleared', async () => {
const user = userEvent.setup();
const units = [
{ value: 'pt', label: 'pt' },
{ value: 'vmax', label: 'vmax' },
];
render( <UnitControl units={ units } value="5" /> );
const select = getSelect();
await user.selectOptions( select, [ 'vmax' ] );
const input = getInput();
await user.clear( input );
expect( select ).toHaveValue( 'vmax' );
} );
it( 'should run onBlur callback when the unit select is blurred', async () => {
const user = userEvent.setup();
const onUnitChangeSpy = jest.fn();
const onBlurSpy = jest.fn();
render(
<UnitControl
value="15px"
onUnitChange={ onUnitChangeSpy }
onBlur={ onBlurSpy }
/>
);
const select = getSelect();
await user.selectOptions( select, [ 'em' ] );
expect( onUnitChangeSpy ).toHaveBeenCalledTimes( 1 );
expect( onUnitChangeSpy ).toHaveBeenLastCalledWith(
'em',
expect.anything()
);
// Clicking document.body to trigger a blur event on the input.
await user.click( document.body );
expect( onBlurSpy ).toHaveBeenCalledTimes( 1 );
} );
} );
describe( 'Unit Parser', () => {
it( 'should update unit after initial render and with new unit prop', async () => {
const { rerender } = render( <UnitControl value={ '10%' } /> );
const select = getSelect();
expect( select.value ).toBe( '%' );
rerender( <UnitControl value={ '20vh' } /> );
expect( select.value ).toBe( 'vh' );
} );
it( 'should fallback to default unit if parsed unit is invalid', () => {
render( <UnitControl value={ '10null' } /> );
expect( getSelect().value ).toBe( 'px' );
} );
it( 'should display valid CSS unit when not explicitly included in units list', () => {
render(
<UnitControl
value={ '10%' }
units={ [
{ value: 'px', label: 'px' },
{ value: 'em', label: 'em' },
] }
/>
);
const select = getSelect();
const options = getSelectOptions();
expect( select.value ).toBe( '%' );
expect( options.length ).toBe( 3 );
} );
} );
describe( 'Unit switching convenience', () => {
it.each( CSS_UNITS.map( ( { value } ) => value ) )(
'should move focus from the input to the unit select when typing the first character of %p',
async ( testUnit ) => {
const user = userEvent.setup();
const onChangeSpy = jest.fn();
const onUnitChangeSpy = jest.fn();
render(
<UnitControl
value={ '10%' }
onChange={ onChangeSpy }
onUnitChange={ onUnitChangeSpy }
/>
);
const input = getInput();
await user.clear( input );
await user.type( input, `55${ testUnit }` );
expect( getSelect() ).toHaveFocus();
// The unit character was not entered in the input.
expect( input ).toHaveValue( 55 );
// The correct expected behavior would be for onChangeSpy to be
// called 4 times, and for the last value it was called with to be
// `55${testUnit}`, but the test environment doesn't seem to change
// values on `select` elements when using the keyboard.
expect( onChangeSpy ).toHaveBeenCalledTimes( 3 );
expect( onChangeSpy ).toHaveBeenLastCalledWith(
'55%',
expect.anything()
);
}
);
} );
} );

View File

@@ -0,0 +1,288 @@
/**
* Internal dependencies
*/
import {
filterUnitsWithSettings,
useCustomUnits,
getValidParsedQuantityAndUnit,
getUnitsWithCurrentUnit,
parseQuantityAndUnitFromRawValue,
} from '../utils';
import type { WPUnitControlUnit } from '../types';
describe( 'UnitControl utils', () => {
describe( 'useCustomUnits', () => {
it( 'should return filtered css units', () => {
const cssUnits = [
{ value: 'px', label: 'pixel' },
{ value: '%', label: 'percent' },
];
const units = useCustomUnits( {
availableUnits: [ 'em', 'px' ],
units: cssUnits,
} );
expect( units ).toEqual( [ { value: 'px', label: 'pixel' } ] );
} );
it( 'should add default values to available units', () => {
const cssUnits = [
{ value: 'px', label: 'pixel' },
{ value: '%', label: 'percent' },
];
const units = useCustomUnits( {
availableUnits: [ '%', 'px' ],
defaultValues: { '%': 10, px: 10 },
units: cssUnits,
} );
expect( units ).toEqual( [
{ value: 'px', label: 'pixel', default: 10 },
{ value: '%', label: 'percent', default: 10 },
] );
} );
it( 'should add default values to available units even if the default values are strings', () => {
// Although the public APIs of the component expect a `number` as the type of the
// default values, it's still good to test for strings (as it can happen in un-typed
// environments)
const cssUnits = [
{ value: 'px', label: 'pixel' },
{ value: '%', label: 'percent' },
];
const units = useCustomUnits( {
availableUnits: [ '%', 'px' ],
defaultValues: {
// @ts-expect-error (passing a string instead of a number is the point of the test)
'%': '14',
// @ts-expect-error (passing a string instead of a number is the point of the test)
px: 'not a valid numeric quantity',
},
units: cssUnits,
} );
expect( units ).toEqual( [
{ value: 'px', label: 'pixel', default: undefined },
{ value: '%', label: 'percent', default: 14 },
] );
} );
it( 'should return an empty array where availableUnits match no preferred css units', () => {
const cssUnits = [
{ value: 'em', label: 'em' },
{ value: 'vh', label: 'vh' },
];
const units = useCustomUnits( {
availableUnits: [ '%', 'px' ],
defaultValues: { '%': 10, px: 10 },
units: cssUnits,
} );
expect( units ).toHaveLength( 0 );
} );
} );
describe( 'filterUnitsWithSettings', () => {
it( 'should return filtered units array', () => {
const preferredUnits = [ '%', 'px' ];
const availableUnits = [
{ value: 'px', label: 'pixel' },
{ value: 'em', label: 'em' },
];
expect(
filterUnitsWithSettings( preferredUnits, availableUnits )
).toEqual( [ { value: 'px', label: 'pixel' } ] );
} );
it( 'should return empty array where preferred units match no available css unit', () => {
const preferredUnits = [ '%', 'px' ];
const availableUnits = [ { value: 'em', label: 'em' } ];
expect(
filterUnitsWithSettings( preferredUnits, availableUnits )
).toEqual( [] );
} );
// Although the component's APIs and types don't allow for `false` as a value
// unit lists, it's good to keep this test around for backwards compat.
it( 'should return empty array where available units is set to false', () => {
const preferredUnits = [ '%', 'px' ];
const availableUnits = false;
expect(
// @ts-expect-error (passing `false` instead of a valid array of units is the point of the test)
filterUnitsWithSettings( preferredUnits, availableUnits )
).toEqual( [] );
} );
it( 'should return empty array where available units is set to an empty array', () => {
const preferredUnits = [ '%', 'px' ];
const availableUnits: WPUnitControlUnit[] = [];
expect(
filterUnitsWithSettings( preferredUnits, availableUnits )
).toEqual( [] );
} );
} );
describe( 'getValidParsedQuantityAndUnit', () => {
it( 'should parse valid number and unit', () => {
const nextValue = '42px';
expect( getValidParsedQuantityAndUnit( nextValue ) ).toEqual( [
42,
'px',
] );
} );
it( 'should return next value only where no known unit parsed', () => {
const nextValue = '365zz';
expect( getValidParsedQuantityAndUnit( nextValue ) ).toEqual( [
365,
undefined,
] );
} );
it( 'should return fallback value', () => {
const nextValue = 'thirteen';
const preferredUnits = [ { value: 'em', label: 'em' } ];
const fallbackValue = 13;
expect(
getValidParsedQuantityAndUnit(
nextValue,
preferredUnits,
fallbackValue
)
).toEqual( [ 13, 'em' ] );
} );
it( 'should return fallback unit', () => {
const nextValue = '911';
const fallbackUnit = '%';
expect(
getValidParsedQuantityAndUnit(
nextValue,
undefined,
undefined,
fallbackUnit
)
).toEqual( [ 911, '%' ] );
} );
it( 'should return first unit in preferred units collection as second fallback unit', () => {
const nextValue = 101;
const preferredUnits = [ { value: 'px', label: 'pixel' } ];
expect(
getValidParsedQuantityAndUnit( nextValue, preferredUnits )
).toEqual( [ 101, 'px' ] );
} );
} );
describe( 'getUnitsWithCurrentUnit', () => {
const limitedUnits = [
{
value: 'px',
label: 'px',
},
{
value: 'em',
label: 'em',
},
];
it( 'should return units list with valid current unit prepended', () => {
const result = getUnitsWithCurrentUnit(
'20%',
undefined,
limitedUnits
);
expect( result ).toHaveLength( 3 );
const currentUnit = result.shift();
expect( currentUnit?.value ).toBe( '%' );
expect( currentUnit?.label ).toBe( '%' );
expect( result ).toEqual( limitedUnits );
} );
it( 'should return units list with valid current unit prepended using legacy values', () => {
const result = getUnitsWithCurrentUnit( 20, '%', limitedUnits );
expect( result ).toHaveLength( 3 );
const currentUnit = result.shift();
expect( currentUnit?.value ).toBe( '%' );
expect( currentUnit?.label ).toBe( '%' );
expect( result ).toEqual( limitedUnits );
} );
it( 'should return units list without invalid current unit prepended', () => {
const result = getUnitsWithCurrentUnit(
'20null',
undefined,
limitedUnits
);
expect( result ).toHaveLength( 2 );
expect( result ).toEqual( limitedUnits );
} );
it( 'should return units list without an existing current unit prepended', () => {
const result = getUnitsWithCurrentUnit(
'20em',
undefined,
limitedUnits
);
expect( result ).toHaveLength( 2 );
expect( result ).toEqual( limitedUnits );
} );
} );
describe( 'parseQuantityAndUnitFromRawValue', () => {
const cases: [
number | string | undefined,
number | undefined,
string | undefined,
][] = [
// Test undefined.
[ undefined, undefined, undefined ],
// Test integers and non-integers.
[ 1, 1, undefined ],
[ 1.25, 1.25, undefined ],
[ '123', 123, undefined ],
[ '1.5', 1.5, undefined ],
[ '0.75', 0.75, undefined ],
// Valid simple CSS values.
[ '20px', 20, 'px' ],
[ '0.8em', 0.8, 'em' ],
[ '2rem', 2, 'rem' ],
[ '1.4vw', 1.4, 'vw' ],
[ '0.4vh', 0.4, 'vh' ],
[ '-5px', -5, 'px' ],
// Complex CSS values that shouldn't parse.
[ 'abs(-15px)', undefined, undefined ],
[ 'calc(10px + 1)', undefined, undefined ],
[ 'clamp(2.5rem, 4vw, 3rem)', undefined, undefined ],
[ 'max(4.5em, 3vh)', undefined, undefined ],
[ 'min(10px, 1rem)', undefined, undefined ],
[ 'minmax(30px, auto)', undefined, undefined ],
[ 'var(--wp--font-size)', undefined, undefined ],
];
test.each( cases )(
'given %p as argument, returns value = %p and unit = %p',
( rawValue, expectedQuantity, expectedUnit ) => {
const [ quantity, unit ] =
parseQuantityAndUnitFromRawValue( rawValue );
expect( quantity ).toBe( expectedQuantity );
expect( unit ).toBe( expectedUnit );
}
);
} );
} );