@ankhorage/surface 9.4.10 → 9.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/core/responsive/ResponsiveProvider.d.ts.map +1 -1
- package/dist/core/responsive/ResponsiveProvider.js +2 -2
- package/dist/core/responsive/ResponsiveProvider.js.map +1 -1
- package/dist/core/responsive/useBreakpoint.d.ts +1 -0
- package/dist/core/responsive/useBreakpoint.d.ts.map +1 -1
- package/dist/core/responsive/useBreakpoint.js +3 -2
- package/dist/core/responsive/useBreakpoint.js.map +1 -1
- package/dist/core/responsive/useResponsiveWidth.d.ts +3 -0
- package/dist/core/responsive/useResponsiveWidth.d.ts.map +1 -0
- package/dist/core/responsive/useResponsiveWidth.js +21 -0
- package/dist/core/responsive/useResponsiveWidth.js.map +1 -0
- package/package.json +2 -2
- package/src/core/responsive/ResponsiveProvider.tsx +2 -2
- package/src/core/responsive/useBreakpoint.ts +3 -3
- package/src/core/responsive/useResponsiveWidth.ts +25 -0
- package/src/webAcceptance.test.tsx +75 -19
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/surface
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Cross-platform UI foundation for React Native and React Native Web. Provides theming, layout primitives, and core building blocks for modern apps.
|
|
9
9
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResponsiveProvider.d.ts","sourceRoot":"","sources":["../../../src/core/responsive/ResponsiveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ResponsiveProvider.d.ts","sourceRoot":"","sources":["../../../src/core/responsive/ResponsiveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAG3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAKjD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAY7E;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,CAQxD"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { createContext, use, useMemo } from 'react';
|
|
2
|
-
import { useWindowDimensions } from 'react-native';
|
|
3
2
|
import { getBreakpointFromWidth } from './getBreakpointFromWidth';
|
|
3
|
+
import { useResponsiveWidth } from './useResponsiveWidth';
|
|
4
4
|
const ResponsiveContext = createContext(null);
|
|
5
5
|
export function ResponsiveProvider({ children }) {
|
|
6
|
-
const
|
|
6
|
+
const width = useResponsiveWidth();
|
|
7
7
|
const value = useMemo(() => ({
|
|
8
8
|
breakpoint: getBreakpointFromWidth(width),
|
|
9
9
|
width,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResponsiveProvider.js","sourceRoot":"","sources":["../../../src/core/responsive/ResponsiveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ResponsiveProvider.js","sourceRoot":"","sources":["../../../src/core/responsive/ResponsiveProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,iBAAiB,GAAG,aAAa,CAA2B,IAAI,CAAC,CAAC;AAExE,MAAM,UAAU,kBAAkB,CAAC,EAAE,QAAQ,EAAiC;IAC5E,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IAEnC,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,UAAU,EAAE,sBAAsB,CAAC,KAAK,CAAC;QACzC,KAAK;KACN,CAAC,EACF,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import React, { createContext, use, useMemo } from 'react';\n\nimport { getBreakpointFromWidth } from './getBreakpointFromWidth';\nimport type { ResponsiveRuntime } from './types';\nimport { useResponsiveWidth } from './useResponsiveWidth';\n\nconst ResponsiveContext = createContext<ResponsiveRuntime | null>(null);\n\nexport function ResponsiveProvider({ children }: { children: React.ReactNode }) {\n const width = useResponsiveWidth();\n\n const value = useMemo<ResponsiveRuntime>(\n () => ({\n breakpoint: getBreakpointFromWidth(width),\n width,\n }),\n [width],\n );\n\n return <ResponsiveContext value={value}>{children}</ResponsiveContext>;\n}\n\nexport function useResponsiveRuntime(): ResponsiveRuntime {\n const runtime = use(ResponsiveContext);\n\n if (!runtime) {\n throw new Error('useResponsiveRuntime must be used within a ResponsiveProvider');\n }\n\n return runtime;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBreakpoint.d.ts","sourceRoot":"","sources":["../../../src/core/responsive/useBreakpoint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useBreakpoint.d.ts","sourceRoot":"","sources":["../../../src/core/responsive/useBreakpoint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,wFAAwF;AACxF,wBAAgB,aAAa,IAAI,UAAU,CAG1C"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useWindowDimensions } from 'react-native';
|
|
2
1
|
import { getBreakpointFromWidth } from './getBreakpointFromWidth';
|
|
2
|
+
import { useResponsiveWidth } from './useResponsiveWidth';
|
|
3
|
+
/*** Returns the hydration-stable responsive breakpoint without requiring a provider. */
|
|
3
4
|
export function useBreakpoint() {
|
|
4
|
-
const
|
|
5
|
+
const width = useResponsiveWidth();
|
|
5
6
|
return getBreakpointFromWidth(width);
|
|
6
7
|
}
|
|
7
8
|
//# sourceMappingURL=useBreakpoint.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBreakpoint.js","sourceRoot":"","sources":["../../../src/core/responsive/useBreakpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"useBreakpoint.js","sourceRoot":"","sources":["../../../src/core/responsive/useBreakpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,wFAAwF;AACxF,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC","sourcesContent":["import { getBreakpointFromWidth } from './getBreakpointFromWidth';\nimport type { Breakpoint } from './types';\nimport { useResponsiveWidth } from './useResponsiveWidth';\n\n/*** Returns the hydration-stable responsive breakpoint without requiring a provider. */\nexport function useBreakpoint(): Breakpoint {\n const width = useResponsiveWidth();\n return getBreakpointFromWidth(width);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResponsiveWidth.d.ts","sourceRoot":"","sources":["../../../src/core/responsive/useResponsiveWidth.ts"],"names":[],"mappings":"AAKA,kFAAkF;AAClF,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { Dimensions } from 'react-native';
|
|
3
|
+
const serverWidth = 0;
|
|
4
|
+
/*** Reads the viewport width without changing the server and hydration render. */
|
|
5
|
+
export function useResponsiveWidth() {
|
|
6
|
+
return useSyncExternalStore(subscribeToWindowDimensions, getWindowWidth, getServerWidth);
|
|
7
|
+
}
|
|
8
|
+
/*** Subscribes to viewport changes after hydration. */
|
|
9
|
+
function subscribeToWindowDimensions(onStoreChange) {
|
|
10
|
+
const subscription = Dimensions.addEventListener('change', onStoreChange);
|
|
11
|
+
return () => subscription.remove();
|
|
12
|
+
}
|
|
13
|
+
/*** Returns the current native or browser viewport width. */
|
|
14
|
+
function getWindowWidth() {
|
|
15
|
+
return Dimensions.get('window').width;
|
|
16
|
+
}
|
|
17
|
+
/*** Supplies the deterministic responsive width used by SSR and first hydration. */
|
|
18
|
+
function getServerWidth() {
|
|
19
|
+
return serverWidth;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=useResponsiveWidth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useResponsiveWidth.js","sourceRoot":"","sources":["../../../src/core/responsive/useResponsiveWidth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,kFAAkF;AAClF,MAAM,UAAU,kBAAkB;IAChC,OAAO,oBAAoB,CAAC,2BAA2B,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;AAC3F,CAAC;AAED,uDAAuD;AACvD,SAAS,2BAA2B,CAAC,aAAyB;IAC5D,MAAM,YAAY,GAAG,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAC1E,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;AACrC,CAAC;AAED,6DAA6D;AAC7D,SAAS,cAAc;IACrB,OAAO,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;AACxC,CAAC;AAED,oFAAoF;AACpF,SAAS,cAAc;IACrB,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { Dimensions } from 'react-native';\n\nconst serverWidth = 0;\n\n/*** Reads the viewport width without changing the server and hydration render. */\nexport function useResponsiveWidth(): number {\n return useSyncExternalStore(subscribeToWindowDimensions, getWindowWidth, getServerWidth);\n}\n\n/*** Subscribes to viewport changes after hydration. */\nfunction subscribeToWindowDimensions(onStoreChange: () => void): () => void {\n const subscription = Dimensions.addEventListener('change', onStoreChange);\n return () => subscription.remove();\n}\n\n/*** Returns the current native or browser viewport width. */\nfunction getWindowWidth(): number {\n return Dimensions.get('window').width;\n}\n\n/*** Supplies the deterministic responsive width used by SSR and first hydration. */\nfunction getServerWidth(): number {\n return serverWidth;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/surface",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.4.
|
|
4
|
+
"version": "9.4.11",
|
|
5
5
|
"description": "Stable UI foundation for React Native and React Native Web.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/surface#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
115
|
"@ankhorage/capability": "^0.2.2",
|
|
116
|
-
"@ankhorage/devtools": "^2.3.
|
|
116
|
+
"@ankhorage/devtools": "^2.3.24",
|
|
117
117
|
"@ankhorage/paradox": "^0.3.5",
|
|
118
118
|
"@react-native-vector-icons/fontawesome": "^13.1.5",
|
|
119
119
|
"@react-native-vector-icons/fontawesome5": "^13.1.5",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React, { createContext, use, useMemo } from 'react';
|
|
2
|
-
import { useWindowDimensions } from 'react-native';
|
|
3
2
|
|
|
4
3
|
import { getBreakpointFromWidth } from './getBreakpointFromWidth';
|
|
5
4
|
import type { ResponsiveRuntime } from './types';
|
|
5
|
+
import { useResponsiveWidth } from './useResponsiveWidth';
|
|
6
6
|
|
|
7
7
|
const ResponsiveContext = createContext<ResponsiveRuntime | null>(null);
|
|
8
8
|
|
|
9
9
|
export function ResponsiveProvider({ children }: { children: React.ReactNode }) {
|
|
10
|
-
const
|
|
10
|
+
const width = useResponsiveWidth();
|
|
11
11
|
|
|
12
12
|
const value = useMemo<ResponsiveRuntime>(
|
|
13
13
|
() => ({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useWindowDimensions } from 'react-native';
|
|
2
|
-
|
|
3
1
|
import { getBreakpointFromWidth } from './getBreakpointFromWidth';
|
|
4
2
|
import type { Breakpoint } from './types';
|
|
3
|
+
import { useResponsiveWidth } from './useResponsiveWidth';
|
|
5
4
|
|
|
5
|
+
/*** Returns the hydration-stable responsive breakpoint without requiring a provider. */
|
|
6
6
|
export function useBreakpoint(): Breakpoint {
|
|
7
|
-
const
|
|
7
|
+
const width = useResponsiveWidth();
|
|
8
8
|
return getBreakpointFromWidth(width);
|
|
9
9
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { Dimensions } from 'react-native';
|
|
3
|
+
|
|
4
|
+
const serverWidth = 0;
|
|
5
|
+
|
|
6
|
+
/*** Reads the viewport width without changing the server and hydration render. */
|
|
7
|
+
export function useResponsiveWidth(): number {
|
|
8
|
+
return useSyncExternalStore(subscribeToWindowDimensions, getWindowWidth, getServerWidth);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/*** Subscribes to viewport changes after hydration. */
|
|
12
|
+
function subscribeToWindowDimensions(onStoreChange: () => void): () => void {
|
|
13
|
+
const subscription = Dimensions.addEventListener('change', onStoreChange);
|
|
14
|
+
return () => subscription.remove();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/*** Returns the current native or browser viewport width. */
|
|
18
|
+
function getWindowWidth(): number {
|
|
19
|
+
return Dimensions.get('window').width;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/*** Supplies the deterministic responsive width used by SSR and first hydration. */
|
|
23
|
+
function getServerWidth(): number {
|
|
24
|
+
return serverWidth;
|
|
25
|
+
}
|
|
@@ -2,7 +2,18 @@ import { expect, mock, test } from 'bun:test';
|
|
|
2
2
|
import { Window } from 'happy-dom';
|
|
3
3
|
import React, { act } from 'react';
|
|
4
4
|
import { renderToString } from 'react-dom/server';
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
const browserWindow = new Window({ url: 'https://surface.test/' });
|
|
7
|
+
Object.assign(globalThis, {
|
|
8
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
9
|
+
Node: browserWindow.Node,
|
|
10
|
+
ShadowRoot: browserWindow.ShadowRoot,
|
|
11
|
+
document: browserWindow.document,
|
|
12
|
+
navigator: browserWindow.navigator,
|
|
13
|
+
window: browserWindow,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const ReactNativeWeb = await import('react-native-web');
|
|
6
17
|
|
|
7
18
|
await mock.module('react-native', () => ReactNativeWeb);
|
|
8
19
|
|
|
@@ -10,7 +21,7 @@ const scrollContentStyle = { padding: 4 };
|
|
|
10
21
|
|
|
11
22
|
const { Grid, ScrollView, View } = await import('./features/layout/public');
|
|
12
23
|
const { KeyboardAvoidingView } = await import('./features/keyboard-avoiding-view/public');
|
|
13
|
-
const { Show } = await import('./core/responsive
|
|
24
|
+
const { Show, useBreakpoint } = await import('./core/responsive');
|
|
14
25
|
const { OverlayProvider } = await import('./internal/overlay/OverlayProvider');
|
|
15
26
|
const { ThemeProvider } = await import('./features/theme/runtime');
|
|
16
27
|
|
|
@@ -24,34 +35,38 @@ function ResponsiveAcceptanceTree() {
|
|
|
24
35
|
<ReactNativeWeb.View testID="grid-second" />
|
|
25
36
|
</Grid>
|
|
26
37
|
<Show
|
|
27
|
-
fallback={
|
|
38
|
+
fallback={
|
|
39
|
+
<ReactNativeWeb.View testID="mobile-branch">
|
|
40
|
+
<ReactNativeWeb.Text>static-fallback</ReactNativeWeb.Text>
|
|
41
|
+
</ReactNativeWeb.View>
|
|
42
|
+
}
|
|
28
43
|
when={{ base: false, md: true }}
|
|
29
44
|
>
|
|
30
|
-
<ReactNativeWeb.Text>wide-content</ReactNativeWeb.Text>
|
|
45
|
+
<ReactNativeWeb.Text testID="desktop-branch">wide-content</ReactNativeWeb.Text>
|
|
31
46
|
</Show>
|
|
32
47
|
</ScrollView>
|
|
33
48
|
</View>
|
|
49
|
+
<BreakpointProbe />
|
|
34
50
|
</ThemeProvider>
|
|
35
51
|
);
|
|
36
52
|
}
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
function BreakpointProbe() {
|
|
55
|
+
return <ReactNativeWeb.Text testID="breakpoint-probe">{useBreakpoint()}</ReactNativeWeb.Text>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
test('RN Web 0.21 preserves SSR responsive structure during desktop hydration', async () => {
|
|
59
|
+
act(() => setViewportWidth(0));
|
|
39
60
|
const markup = renderToString(<ResponsiveAcceptanceTree />);
|
|
40
61
|
|
|
41
62
|
expect(markup).toContain('data-testid="container"');
|
|
42
63
|
expect(markup).toContain('data-testid="scroll-view"');
|
|
43
64
|
expect(markup).toContain('data-testid="grid"');
|
|
65
|
+
expect(markup).toContain('base');
|
|
44
66
|
expect(markup).toContain('static-fallback');
|
|
45
67
|
expect(markup).not.toContain('wide-content');
|
|
46
68
|
|
|
47
|
-
|
|
48
|
-
Object.assign(globalThis, {
|
|
49
|
-
IS_REACT_ACT_ENVIRONMENT: true,
|
|
50
|
-
Node: browserWindow.Node,
|
|
51
|
-
document: browserWindow.document,
|
|
52
|
-
navigator: browserWindow.navigator,
|
|
53
|
-
window: browserWindow,
|
|
54
|
-
});
|
|
69
|
+
act(() => setViewportWidth(1440));
|
|
55
70
|
const container = browserWindow.document.createElement('div');
|
|
56
71
|
container.innerHTML = markup;
|
|
57
72
|
browserWindow.document.body.append(container);
|
|
@@ -64,25 +79,66 @@ test('RN Web 0.21 statically renders and hydrates representative responsive prim
|
|
|
64
79
|
try {
|
|
65
80
|
const { hydrateRoot } = await import('react-dom/client');
|
|
66
81
|
const root = hydrateRoot(container as unknown as Element, <ResponsiveAcceptanceTree />);
|
|
82
|
+
|
|
83
|
+
expect(container.querySelector('[data-testid="mobile-branch"]')).not.toBeNull();
|
|
84
|
+
expect(container.querySelector('[data-testid="desktop-branch"]')).toBeNull();
|
|
85
|
+
expect(container.querySelector('[data-testid="breakpoint-probe"]')?.textContent).toBe('base');
|
|
67
86
|
await act(async () => Promise.resolve());
|
|
68
87
|
|
|
69
88
|
expect(container.querySelector('[data-testid="container"]')).not.toBeNull();
|
|
70
89
|
expect(container.querySelector('[data-testid="scroll-view"]')).not.toBeNull();
|
|
71
90
|
expect(container.querySelectorAll('[data-testid^="grid-"]')).toHaveLength(2);
|
|
91
|
+
expect(container.querySelector('[data-testid="mobile-branch"]')).toBeNull();
|
|
92
|
+
expect(container.querySelector('[data-testid="desktop-branch"]')).not.toBeNull();
|
|
93
|
+
expect(container.querySelector('[data-testid="breakpoint-probe"]')?.textContent).toBe('xl');
|
|
94
|
+
expect(hydrationErrors).toEqual([]);
|
|
95
|
+
|
|
96
|
+
act(() => root.unmount());
|
|
97
|
+
} finally {
|
|
98
|
+
console.error = originalError;
|
|
99
|
+
container.remove();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('RN Web 0.21 keeps the server responsive structure at a narrow viewport', async () => {
|
|
104
|
+
act(() => setViewportWidth(0));
|
|
105
|
+
const markup = renderToString(<ResponsiveAcceptanceTree />);
|
|
106
|
+
act(() => setViewportWidth(375));
|
|
107
|
+
const container = browserWindow.document.createElement('div');
|
|
108
|
+
container.innerHTML = markup;
|
|
109
|
+
browserWindow.document.body.append(container);
|
|
110
|
+
const hydrationErrors: string[] = [];
|
|
111
|
+
const originalError = console.error;
|
|
112
|
+
console.error = (...values: unknown[]) => {
|
|
113
|
+
hydrationErrors.push(values.map(String).join(' '));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const { hydrateRoot } = await import('react-dom/client');
|
|
118
|
+
const root = hydrateRoot(container as unknown as Element, <ResponsiveAcceptanceTree />);
|
|
119
|
+
await act(async () => Promise.resolve());
|
|
120
|
+
|
|
121
|
+
expect(container.querySelector('[data-testid="mobile-branch"]')).not.toBeNull();
|
|
122
|
+
expect(container.querySelector('[data-testid="desktop-branch"]')).toBeNull();
|
|
123
|
+
expect(container.querySelector('[data-testid="breakpoint-probe"]')?.textContent).toBe('base');
|
|
124
|
+
expect(container.querySelectorAll('[data-testid^="grid-"]')).toHaveLength(2);
|
|
72
125
|
expect(hydrationErrors).toEqual([]);
|
|
73
126
|
|
|
74
127
|
act(() => root.unmount());
|
|
75
128
|
} finally {
|
|
76
129
|
console.error = originalError;
|
|
77
|
-
|
|
78
|
-
Reflect.deleteProperty(globalThis, 'IS_REACT_ACT_ENVIRONMENT');
|
|
79
|
-
Reflect.deleteProperty(globalThis, 'Node');
|
|
80
|
-
Reflect.deleteProperty(globalThis, 'document');
|
|
81
|
-
Reflect.deleteProperty(globalThis, 'navigator');
|
|
82
|
-
Reflect.deleteProperty(globalThis, 'window');
|
|
130
|
+
container.remove();
|
|
83
131
|
}
|
|
84
132
|
});
|
|
85
133
|
|
|
134
|
+
function setViewportWidth(width: number) {
|
|
135
|
+
Object.defineProperty(browserWindow.document.documentElement, 'clientWidth', {
|
|
136
|
+
configurable: true,
|
|
137
|
+
value: width,
|
|
138
|
+
});
|
|
139
|
+
browserWindow.dispatchEvent(new browserWindow.Event('resize'));
|
|
140
|
+
}
|
|
141
|
+
|
|
86
142
|
test('RN Web 0.21 compiles box-none overlay pointer events into a hit-test-safe class', () => {
|
|
87
143
|
const markup = renderToString(
|
|
88
144
|
<OverlayProvider>
|