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,44 @@
# Scrollable
<div class="callout callout-alert">
This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
</div>
`Scrollable` is a layout component that content in a scrollable container.
## Usage
```jsx
import { __experimentalScrollable as Scrollable } from '@wordpress/components';
function Example() {
return (
<Scrollable style={ { maxHeight: 200 } }>
<div style={ { height: 500 } }>...</div>
</Scrollable>
);
}
```
## Props
### `children`: `ReactNode`
The children elements.
- Required: Yes
### `scrollDirection`: `string`
Renders a scrollbar for a specific axis when content overflows.
- Required: No
- Default: `y`
- Allowed values: `x`, `y`, `auto`
### `smoothScroll`: `boolean`
Enables (CSS) smooth scrolling.
- Required: No
- Default: `false`

View File

@@ -0,0 +1,41 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';
/**
* Internal dependencies
*/
import type { WordPressComponentProps } from '../context';
import { contextConnect } from '../context';
import { View } from '../view';
import { useScrollable } from './hook';
import type { ScrollableProps } from './types';
function UnconnectedScrollable(
props: WordPressComponentProps< ScrollableProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const scrollableProps = useScrollable( props );
return <View { ...scrollableProps } ref={ forwardedRef } />;
}
/**
* `Scrollable` is a layout component that content in a scrollable container.
*
* ```jsx
* import { __experimentalScrollable as Scrollable } from `@wordpress/components`;
*
* function Example() {
* return (
* <Scrollable style={ { maxHeight: 200 } }>
* <div style={ { height: 500 } }>...</div>
* </Scrollable>
* );
* }
* ```
*/
export const Scrollable = contextConnect( UnconnectedScrollable, 'Scrollable' );
export default Scrollable;

View File

@@ -0,0 +1,42 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { WordPressComponentProps } from '../context';
import { useContextSystem } from '../context';
import * as styles from './styles';
import { useCx } from '../utils/hooks/use-cx';
import type { ScrollableProps } from './types';
export function useScrollable(
props: WordPressComponentProps< ScrollableProps, 'div' >
) {
const {
className,
scrollDirection = 'y',
smoothScroll = false,
...otherProps
} = useContextSystem( props, 'Scrollable' );
const cx = useCx();
const classes = useMemo(
() =>
cx(
styles.Scrollable,
styles.scrollableScrollbar,
smoothScroll && styles.smoothScroll,
scrollDirection === 'x' && styles.scrollX,
scrollDirection === 'y' && styles.scrollY,
scrollDirection === 'auto' && styles.scrollAuto,
className
),
[ className, cx, scrollDirection, smoothScroll ]
);
return { ...otherProps, className: classes };
}

View File

@@ -0,0 +1,3 @@
export { default as Scrollable } from './component';
export * from './hook';

View File

@@ -0,0 +1,83 @@
/**
* External dependencies
*/
import type { Meta, StoryFn } from '@storybook/react';
/**
* WordPress dependencies
*/
import { useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import { View } from '../../view';
import { Scrollable } from '..';
const meta: Meta< typeof Scrollable > = {
component: Scrollable,
title: 'Components (Experimental)/Scrollable',
argTypes: {
as: {
control: { type: 'text' },
},
children: {
control: { type: null },
},
},
parameters: {
controls: {
expanded: true,
},
docs: { canvas: { sourceState: 'shown' } },
},
};
export default meta;
const Template: StoryFn< typeof Scrollable > = ( { ...args } ) => {
const targetRef = useRef< HTMLInputElement >( null );
const onButtonClick = () => {
targetRef.current?.focus();
};
const containerWidth = 300;
const containerHeight = 400;
return (
<Scrollable
style={ { height: containerHeight, width: containerWidth } }
{ ...args }
>
<View
style={ {
backgroundColor: '#eee',
height:
args.scrollDirection === 'x' ? containerHeight : 1000,
width: args.scrollDirection === 'y' ? containerWidth : 1000,
position: 'relative',
} }
>
<button onClick={ onButtonClick }>
Move focus to an element out of view
</button>
<input
ref={ targetRef }
style={ {
position: 'absolute',
bottom: args.scrollDirection === 'x' ? 'initial' : 0,
right: 0,
} }
type="text"
value="Focus me"
/>
</View>
</Scrollable>
);
};
export const Default: StoryFn< typeof Scrollable > = Template.bind( {} );
Default.args = {
smoothScroll: false,
scrollDirection: 'y',
};

View File

@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import { css } from '@emotion/react';
/**
* Internal dependencies
*/
import { CONFIG } from '../utils';
export const scrollableScrollbar = css`
@media only screen and ( min-device-width: 40em ) {
&::-webkit-scrollbar {
height: 12px;
width: 12px;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
&::-webkit-scrollbar-track {
background: ${ CONFIG.colorScrollbarTrack };
border-radius: 8px;
}
&::-webkit-scrollbar-thumb {
background-clip: padding-box;
background-color: ${ CONFIG.colorScrollbarThumb };
border: 2px solid rgba( 0, 0, 0, 0 );
border-radius: 7px;
}
&:hover::-webkit-scrollbar-thumb {
background-color: ${ CONFIG.colorScrollbarThumbHover };
}
}
`;
export const Scrollable = css`
height: 100%;
`;
export const Content = css`
position: relative;
`;
export const smoothScroll = css`
scroll-behavior: smooth;
`;
export const scrollX = css`
overflow-x: auto;
overflow-y: hidden;
`;
export const scrollY = css`
overflow-x: hidden;
overflow-y: auto;
`;
export const scrollAuto = css`
overflow-y: auto;
`;

View File

@@ -0,0 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`props should render correctly 1`] = `
.emotion-0 {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
@media only screen and ( min-device-width: 40em ) {
.emotion-0::-webkit-scrollbar {
height: 12px;
width: 12px;
}
.emotion-0::-webkit-scrollbar-track {
background-color: transparent;
}
.emotion-0::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.04);
border-radius: 8px;
}
.emotion-0::-webkit-scrollbar-thumb {
-webkit-background-clip: padding-box;
background-clip: padding-box;
background-color: rgba(0, 0, 0, 0.2);
border: 2px solid rgba( 0, 0, 0, 0 );
border-radius: 7px;
}
.emotion-0:hover::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.5);
}
}
<div
class="components-scrollable emotion-0 emotion-1"
data-testid="scrollable"
data-wp-c16t="true"
data-wp-component="Scrollable"
>
WordPress.org - Code is Poetry
</div>
`;
exports[`props should render smoothScroll 1`] = `
Snapshot Diff:
- Received styles
+ Base styles
Array [
Object {
"height": "100%",
"overflow-x": "hidden",
"overflow-y": "auto",
- "scroll-behavior": "smooth",
},
]
`;

View File

@@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';
/**
* Internal dependencies
*/
import { Scrollable } from '../index';
describe( 'props', () => {
test( 'should render correctly', () => {
render(
<Scrollable data-testid="scrollable">
WordPress.org - Code is Poetry
</Scrollable>
);
expect( screen.getByTestId( 'scrollable' ) ).toMatchSnapshot();
} );
test( 'should render smoothScroll', () => {
render(
<Scrollable data-testid="scrollable">
WordPress.org - Code is Poetry
</Scrollable>
);
render(
<Scrollable smoothScroll data-testid="smooth-scrollable">
WordPress.org - Code is Poetry
</Scrollable>
);
expect(
screen.getByTestId( 'smooth-scrollable' )
).toMatchStyleDiffSnapshot( screen.getByTestId( 'scrollable' ) );
} );
} );

View File

@@ -0,0 +1,25 @@
/**
* External dependencies
*/
import type { ReactNode } from 'react';
export type ScrollableDirection = 'x' | 'y' | 'auto';
export type ScrollableProps = {
/**
* The children elements.
*/
children: ReactNode;
/**
* Renders a scrollbar for a specific axis when content overflows.
*
* @default 'y'
*/
scrollDirection?: ScrollableDirection;
/**
* Enables (CSS) smooth scrolling.
*
* @default false
*/
smoothScroll?: boolean;
};