Skip to content

Commit b1ca145

Browse files
authored
feat: improve React Compiler compatibility (#8185)
1 parent 7634a40 commit b1ca145

77 files changed

Lines changed: 663 additions & 537 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# Created by .ignore support plugin (hsz.mobi)
21
node_modules
32
*.iml
43
yarn-error.log

.storybook/components/DocsHeader.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ export const InfoTable = ({
9595
<Button
9696
design={ButtonDesign.Transparent}
9797
className={clsx('ui5-content-density-compact', classes.copyBtn)}
98-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
99-
onClick={handleCopy}
98+
onClick={void handleCopy}
10099
icon={copyIcon}
101100
tooltip="copy"
102101
/>
@@ -153,8 +152,7 @@ export const InfoTable = ({
153152
<Button
154153
design={ButtonDesign.Transparent}
155154
className={clsx('ui5-content-density-compact', classes.copyBtn)}
156-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
157-
onClick={handleCopy}
155+
onClick={void handleCopy}
158156
icon={copyIcon}
159157
tooltip="copy"
160158
/>

.storybook/components/Footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js';
22
import PopoverPlacement from '@ui5/webcomponents/dist/types/PopoverPlacement.js';
33
import WrappingType from '@ui5/webcomponents/dist/types/WrappingType.js';
4-
import type { ButtonPropTypes, PopoverDomRef } from '@ui5/webcomponents-react';
54
import {
65
Button,
76
FlexBox,
@@ -13,6 +12,7 @@ import {
1312
Popover,
1413
Text,
1514
} from '@ui5/webcomponents-react';
15+
import type { ButtonPropTypes, PopoverDomRef } from '@ui5/webcomponents-react';
1616
import type { CommonProps } from '@ui5/webcomponents-react-base';
1717
import { clsx } from 'clsx';
1818
import { useRef, useState } from 'react';

.storybook/components/ProjectTemplate.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { addCustomCSSWithScoping } from '@ui5/webcomponents-react-base/internal/utils';
1616
import { clsx } from 'clsx';
1717
import type { ReactNode } from 'react';
18-
import { useRef, useState } from 'react';
18+
import { useId, useState } from 'react';
1919
import classes from './ProjectTemplate.module.css';
2020

2121
interface ProjectTemplatePropTypes {
@@ -57,7 +57,7 @@ export function ProjectTemplate(props: ProjectTemplatePropTypes) {
5757
note,
5858
} = props;
5959
const [popoverOpen, setPopoverOpen] = useState(false);
60-
const linkRef = useRef(null);
60+
const linkId = useId() + '-link';
6161

6262
return (
6363
<ThemeProvider>
@@ -67,7 +67,7 @@ export function ProjectTemplate(props: ProjectTemplatePropTypes) {
6767
<MessageStrip hideCloseButton design={MessageStripDesign.Critical} className={classes.unssupportedMessage}>
6868
Currently not supported by V2.{' '}
6969
<Link
70-
ref={linkRef}
70+
id={linkId}
7171
accessibleRole="Button"
7272
onClick={() => {
7373
setPopoverOpen(true);
@@ -78,7 +78,7 @@ export function ProjectTemplate(props: ProjectTemplatePropTypes) {
7878
</MessageStrip>
7979
<Popover
8080
className={classes.popover}
81-
opener={linkRef.current}
81+
opener={linkId}
8282
open={popoverOpen}
8383
onClose={() => {
8484
setPopoverOpen(false);

.storybook/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ export function useFakeStream(initialValue = '', typingDelay = 10, startingDelay
165165

166166
export function useStopStreamByESC(loading: boolean, stopStream: () => void, onStop?: () => void) {
167167
const loadingRef = useRef(loading);
168+
// Ref update during render doesn't trigger re-renders and is only read in event handler
169+
// eslint-disable-next-line react-hooks/refs
168170
loadingRef.current = loading;
169171

170172
useEffect(() => {

cypress/support/commands.tsx

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,20 @@ declare global {
4040
/**
4141
* Asserts that the element never gains the given attribute.
4242
*
43-
* __Note:__ An error is thrown if the attribute is not found, therefore it does not block the test if the subject
43+
* __Note:__ An error is thrown if the attribute is found, therefore it does not block the test if the subject
4444
* never includes the given attribute.
4545
*
46-
*
4746
* @param attributeName - The name of the attribute which must not appear.
48-
* @param observerTime - How long (in ms) to watch for mutations (default: 500).
47+
* @param options
48+
* @param options.observerTime - How long (in ms) to watch for mutations (default: 500).
49+
* @param options.delayed - How long (in ms) to wait before starting observation (default: 0).
4950
* @example
50-
* cy.get('button').shouldNeverHaveAttribute('disabled', 1000);
51+
* cy.get('button').shouldNeverHaveAttribute('disabled', { observerTime: 500, delayed: 100 });
5152
*/
52-
shouldNeverHaveAttribute(attributeName: string, observerTime?: number): Chainable<JQuery<HTMLElement>>;
53+
shouldNeverHaveAttribute(
54+
attributeName: string,
55+
options?: { observerTime?: number; delayed?: number },
56+
): Chainable<JQuery<HTMLElement>>;
5357
}
5458
}
5559
}
@@ -80,35 +84,49 @@ Cypress.Commands.add(
8084
},
8185
);
8286

83-
Cypress.Commands.add(
84-
'shouldNeverHaveAttribute',
85-
{ prevSubject: 'element' },
86-
(subject, attributeName, observerTime = 500) => {
87-
cy.wrap(subject).then(($el) => {
88-
const el = $el[0];
89-
const observer = new MutationObserver((mutations) => {
90-
for (const mutation of mutations) {
91-
if (mutation.attributeName === attributeName) {
92-
Cypress.log({
93-
name: 'shouldNeverHaveAttribute',
94-
message: `${attributeName} was found!`,
95-
consoleProps: () => ({
96-
attributeName,
97-
element: el,
98-
}),
99-
});
87+
const activeObservers: MutationObserver[] = [];
10088

101-
observer.disconnect();
102-
throw new Error(`${attributeName} was found!`);
103-
}
104-
}
105-
});
89+
Cypress.Commands.add('shouldNeverHaveAttribute', { prevSubject: 'element' }, (subject, attributeName, options = {}) => {
90+
const { observerTime = 500, delayed = 0 } = options;
91+
// Disconnect all previous observers when a new assertion starts
92+
while (activeObservers.length > 0) {
93+
activeObservers.pop()?.disconnect();
94+
}
95+
96+
cy.wait(delayed);
10697

107-
observer.observe(el, { attributes: true });
98+
cy.wrap(subject).then(($el) => {
99+
const el = $el[0];
100+
const observer = new MutationObserver((mutations) => {
101+
for (const mutation of mutations) {
102+
if (mutation.attributeName === attributeName) {
103+
Cypress.log({
104+
name: 'shouldNeverHaveAttribute',
105+
message: `${attributeName} was found!`,
106+
consoleProps: () => ({
107+
attributeName,
108+
element: el,
109+
}),
110+
});
108111

109-
setTimeout(() => {
110-
observer.disconnect();
111-
}, observerTime);
112+
observer.disconnect();
113+
const index = activeObservers.indexOf(observer);
114+
if (index > -1) {
115+
activeObservers.splice(index, 1);
116+
}
117+
throw new Error(`${attributeName} was found!`);
118+
}
119+
}
112120
});
113-
},
114-
);
121+
observer.observe(el, { attributes: true });
122+
activeObservers.push(observer);
123+
124+
setTimeout(() => {
125+
observer.disconnect();
126+
const index = activeObservers.indexOf(observer);
127+
if (index > -1) {
128+
activeObservers.splice(index, 1);
129+
}
130+
}, observerTime);
131+
});
132+
});

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const config = tseslint.config(
4848
reactPlugin.configs.flat.recommended, // This is not a plugin object, but a shareable config object
4949
reactPlugin.configs.flat['jsx-runtime'], // Add this if you are using React 17+
5050
// eslint-plugin-react-hooks
51-
...reactHooksPlugin.configs.recommended,
51+
reactHooksPlugin.configs.flat.recommended,
5252
{
5353
languageOptions: {
5454
globals: {

examples/react-router-ts/package-lock.json

Lines changed: 22 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/react-router-ts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"eslint-plugin-import": "2.32.0",
3333
"eslint-plugin-jsx-a11y": "6.10.2",
3434
"eslint-plugin-react": "7.37.5",
35-
"eslint-plugin-react-hooks": "6.1.1",
35+
"eslint-plugin-react-hooks": "7.0.0",
3636
"globals": "17.3.0",
3737
"typescript": "5.8.3",
3838
"typescript-eslint": "8.54.0",

examples/vite-ts/eslint.config.mjs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
import js from '@eslint/js';
22
import globals from 'globals';
3-
import reactHooks from 'eslint-plugin-react-hooks';
43
import reactRefresh from 'eslint-plugin-react-refresh';
54
import tseslint from 'typescript-eslint';
5+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
66

7-
export default tseslint.config(
8-
{ ignores: ['dist'] },
9-
{
10-
extends: [js.configs.recommended, ...tseslint.configs.recommended],
11-
files: ['**/*.{ts,tsx}'],
12-
languageOptions: {
13-
ecmaVersion: 2020,
14-
globals: globals.browser,
15-
},
16-
plugins: {
17-
'react-hooks': reactHooks,
18-
'react-refresh': reactRefresh,
19-
},
20-
rules: {
21-
...reactHooks.configs.recommended.rules,
22-
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
23-
},
7+
export default tseslint.config({ ignores: ['dist'] }, reactHooksPlugin.configs.flat.recommended, {
8+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
9+
files: ['**/*.{ts,tsx}'],
10+
languageOptions: {
11+
ecmaVersion: 2020,
12+
globals: globals.browser,
2413
},
25-
);
14+
plugins: {
15+
'react-refresh': reactRefresh,
16+
},
17+
rules: {
18+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
19+
},
20+
});

0 commit comments

Comments
 (0)