Release/@gluestack UI/pin input@0.0.8 #170
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Next.js Latest | |
on: | |
push: | |
branches: [main, master] | |
pull_request: | |
branches: [main, master] | |
schedule: | |
- cron: '0 0 * * *' # Run daily at midnight UTC | |
jobs: | |
check-next-version: | |
runs-on: ubuntu-latest | |
outputs: | |
should_run: ${{ steps.check.outputs.should_run }} | |
latest_version: ${{ steps.check.outputs.latest_version }} | |
steps: | |
- id: check | |
run: | | |
LATEST=$(npm view next version) | |
CURRENT=$(cat .next-version 2>/dev/null || echo "") | |
echo "Latest version: $LATEST" | |
echo "Current version: $CURRENT" | |
if [ "$LATEST" != "$CURRENT" ]; then | |
echo "should_run=true" >> $GITHUB_OUTPUT | |
echo "latest_version=$LATEST" >> $GITHUB_OUTPUT | |
echo $LATEST > .next-version | |
else | |
echo "should_run=false" >> $GITHUB_OUTPUT | |
fi | |
test-next-latest: | |
needs: check-next-version | |
if: ${{ needs.check-next-version.outputs.should_run == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' }} | |
runs-on: ubuntu-latest | |
name: Next.js latest | |
steps: | |
- uses: actions/checkout@v3 | |
- name: Use Node.js | |
uses: actions/setup-node@v3 | |
with: | |
node-version: '18' | |
- name: Print Environment Info | |
run: | | |
node -v | |
npm -v | |
cat .next-version || echo ".next-version file not found" | |
- name: Create Next.js project | |
run: | | |
npx create-next-app@latest test-app --typescript --eslint --tailwind --app --src-dir --use-npm --no-experimental-app | |
cd test-app | |
- name: Install Gluestack UI | |
working-directory: test-app | |
run: | | |
npx gluestack-ui init --template-only --projectType nextjs | |
npx gluestack-ui add --all | |
- name: Rename original next.config file | |
working-directory: test-app | |
run: mv next.config.ts next.config.original.ts | |
- name: Create new next.config file | |
working-directory: test-app | |
run: | | |
cat <<EOT > next.config.ts | |
import { withGluestackUI } from "@gluestack/ui-next-adapter"; | |
const nextConfig = { | |
transpilePackages: ["nativewind", "react-native-css-interop"], | |
typescript: { | |
ignoreBuildErrors: true, | |
}, | |
}; | |
export default withGluestackUI(nextConfig); | |
EOT | |
- name: Copy data to src/app/registry.tsx | |
working-directory: test-app | |
run: | | |
cat <<EOT > src/app/registry.tsx | |
"use client"; | |
import React, { useRef, useState } from "react"; | |
import { useServerInsertedHTML } from "next/navigation"; | |
import { StyleRegistry, createStyleRegistry } from "styled-jsx"; | |
// @ts-ignore | |
import { AppRegistry } from "react-native-web"; | |
import { flush } from "@gluestack-ui/nativewind-utils/flush"; | |
export default function StyledJsxRegistry({ | |
children, | |
}: { | |
children: React.ReactNode; | |
}) { | |
// Only create stylesheet once with lazy initial state | |
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state | |
const [jsxStyleRegistry] = useState(() => createStyleRegistry()); | |
const isServerInserted = useRef(false); | |
useServerInsertedHTML(() => { | |
AppRegistry.registerComponent("Main", () => "main"); | |
const { getStyleElement } = AppRegistry.getApplication("Main"); | |
if (!isServerInserted.current) { | |
isServerInserted.current = true; | |
const styles = [getStyleElement(), jsxStyleRegistry.styles(), flush()]; | |
jsxStyleRegistry.flush(); | |
return <>{styles}</>; | |
} | |
}); | |
return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>; | |
} | |
EOT | |
- name: Copy data to components/ui/gluestack-ui-provider/index.web.tsx | |
working-directory: test-app | |
run: | | |
cat <<EOT > components/ui/gluestack-ui-provider/index.web.tsx | |
"use client"; | |
import React, { useEffect, useLayoutEffect } from "react"; | |
import { config } from "./config"; | |
import { OverlayProvider } from "@gluestack-ui/overlay"; | |
import { ToastProvider } from "@gluestack-ui/toast"; | |
import { setFlushStyles } from "@gluestack-ui/nativewind-utils/flush"; | |
import { script } from "./script"; | |
const variableStyleTagId = "nativewind-style"; | |
const createStyle = (styleTagId: string) => { | |
const style = document.createElement("style"); | |
style.id = styleTagId; | |
style.appendChild(document.createTextNode("")); | |
return style; | |
}; | |
export const useSafeLayoutEffect = | |
typeof window !== "undefined" ? useLayoutEffect : useEffect; | |
export function GluestackUIProvider({ | |
mode = "light", | |
...props | |
}: { | |
mode?: "light" | "dark" | "system"; | |
children?: React.ReactNode; | |
}) { | |
let cssVariablesWithMode = ""; | |
Object.keys(config).forEach((configKey) => { | |
cssVariablesWithMode += configKey === "dark" ? ".dark {" : ":root {"; | |
const cssVariables = Object.keys( | |
config[configKey as keyof typeof config] | |
).reduce((acc: string, curr: string) => { | |
acc += curr + ":" + config[configKey as keyof typeof config][curr]; | |
return acc; | |
}, ""); | |
cssVariablesWithMode += cssVariables+ "\n}"; | |
}); | |
setFlushStyles(cssVariablesWithMode); | |
const handleMediaQuery = React.useCallback((e: MediaQueryListEvent) => { | |
script(e.matches ? "dark" : "light"); | |
}, []); | |
useSafeLayoutEffect(() => { | |
if (mode !== "system") { | |
const documentElement = document.documentElement; | |
if (documentElement) { | |
documentElement.classList.add(mode); | |
documentElement.classList.remove(mode === "light" ? "dark" : "light"); | |
documentElement.style.colorScheme = mode; | |
} | |
} | |
}, [mode]); | |
useSafeLayoutEffect(() => { | |
if (mode !== "system") return; | |
const media = window.matchMedia("(prefers-color-scheme: dark)"); | |
media.addListener(handleMediaQuery); | |
return () => media.removeListener(handleMediaQuery); | |
}, [handleMediaQuery]); | |
useSafeLayoutEffect(() => { | |
if (typeof window !== "undefined") { | |
const documentElement = document.documentElement; | |
if (documentElement) { | |
const head = documentElement.querySelector("head"); | |
let style = head?.querySelector("[id="+variableStyleTagId+"]"); | |
if (!style) { | |
style = createStyle(variableStyleTagId); | |
style.innerHTML = cssVariablesWithMode; | |
if (head) head.appendChild(style); | |
} | |
} | |
} | |
}, []); | |
return ( | |
<> | |
<OverlayProvider> | |
<ToastProvider>{props.children}</ToastProvider> | |
</OverlayProvider> | |
</> | |
); | |
} | |
EOT | |
- name: Create patches folder | |
working-directory: test-app | |
run: | | |
mkdir -p patches | |
- name: Create react-native-web patch file | |
working-directory: test-app/patches | |
run: | | |
cat <<EOT > react-native-web+0.19.13.patch | |
diff --git a/node_modules/react-native-web/dist/cjs/exports/AppRegistry/renderApplication.js b/node_modules/react-native-web/dist/cjs/exports/AppRegistry/renderApplication.js | |
index 0c0cb2f..83fd94b 100644 | |
--- a/node_modules/react-native-web/dist/cjs/exports/AppRegistry/renderApplication.js | |
+++ b/node_modules/react-native-web/dist/cjs/exports/AppRegistry/renderApplication.js | |
@@ -1,6 +1,5 @@ | |
"use strict"; | |
-var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; | |
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | |
exports.__esModule = true; | |
exports.default = renderApplication; | |
@@ -8,7 +7,7 @@ exports.getApplication = getApplication; | |
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); | |
var _AppContainer = _interopRequireDefault(require("./AppContainer")); | |
var _invariant = _interopRequireDefault(require("fbjs/lib/invariant")); | |
-var _render = _interopRequireWildcard(require("../render")); | |
+var _render = require("../render"); | |
var _StyleSheet = _interopRequireDefault(require("../StyleSheet")); | |
var _react = _interopRequireDefault(require("react")); | |
/** | |
@@ -24,9 +23,8 @@ var _react = _interopRequireDefault(require("react")); | |
function renderApplication(RootComponent, WrapperComponent, callback, options) { | |
var shouldHydrate = options.hydrate, | |
initialProps = options.initialProps, | |
- mode = options.mode, | |
rootTag = options.rootTag; | |
- var renderFn = shouldHydrate ? mode === 'concurrent' ? _render.hydrate : _render.hydrateLegacy : mode === 'concurrent' ? _render.render : _render.default; | |
+ var renderFn = shouldHydrate ? _render.hydrate : _render.render; | |
(0, _invariant.default)(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag); | |
return renderFn(/*#__PURE__*/_react.default.createElement(_AppContainer.default, { | |
WrapperComponent: WrapperComponent, | |
diff --git a/node_modules/react-native-web/dist/cjs/exports/render/index.js b/node_modules/react-native-web/dist/cjs/exports/render/index.js | |
index b41ee11..18d9b2f 100644 | |
--- a/node_modules/react-native-web/dist/cjs/exports/render/index.js | |
+++ b/node_modules/react-native-web/dist/cjs/exports/render/index.js | |
@@ -10,15 +10,10 @@ | |
'use client'; | |
-var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | |
exports.__esModule = true; | |
-exports.default = renderLegacy; | |
+exports.default = render; | |
exports.hydrate = hydrate; | |
-exports.hydrateLegacy = hydrateLegacy; | |
-exports.render = render; | |
-var _reactDom = require("react-dom"); | |
var _client = require("react-dom/client"); | |
-var _unmountComponentAtNode = _interopRequireDefault(require("../unmountComponentAtNode")); | |
var _dom = require("../StyleSheet/dom"); | |
function hydrate(element, root) { | |
(0, _dom.createSheet)(root); | |
@@ -30,21 +25,3 @@ function render(element, root) { | |
reactRoot.render(element); | |
return reactRoot; | |
} | |
\ No newline at end of file | |
-function hydrateLegacy(element, root, callback) { | |
- (0, _dom.createSheet)(root); | |
- (0, _reactDom.hydrate)(element, root, callback); | |
- return { | |
- unmount: function unmount() { | |
- return (0, _unmountComponentAtNode.default)(root); | |
- } | |
- }; | |
-} | |
-function renderLegacy(element, root, callback) { | |
- (0, _dom.createSheet)(root); | |
- (0, _reactDom.render)(element, root, callback); | |
- return { | |
- unmount: function unmount() { | |
- return (0, _unmountComponentAtNode.default)(root); | |
- } | |
- }; | |
-} | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/cjs/exports/unmountComponentAtNode/index.js b/node_modules/react-native-web/dist/cjs/exports/unmountComponentAtNode/index.js | |
index 3ea3964..e740204 100644 | |
--- a/node_modules/react-native-web/dist/cjs/exports/unmountComponentAtNode/index.js | |
+++ b/node_modules/react-native-web/dist/cjs/exports/unmountComponentAtNode/index.js | |
@@ -1,8 +1,7 @@ | |
"use strict"; | |
exports.__esModule = true; | |
-exports.default = void 0; | |
-var _reactDom = require("react-dom"); | |
+exports.default = unmountComponentAtNode; | |
/** | |
* Copyright (c) Nicolas Gallagher. | |
* | |
@@ -11,5 +10,9 @@ var _reactDom = require("react-dom"); | |
* | |
* | |
*/ | |
-var _default = exports.default = _reactDom.unmountComponentAtNode; | |
+ | |
+function unmountComponentAtNode(rootTag) { | |
+ rootTag.unmount(); | |
+ return true; | |
+} | |
module.exports = exports.default; | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js b/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js | |
index b53dff6..c56c1dc 100644 | |
--- a/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js | |
+++ b/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js | |
@@ -11,15 +11,14 @@ import _extends from "@babel/runtime/helpers/extends"; | |
import AppContainer from './AppContainer'; | |
import invariant from 'fbjs/lib/invariant'; | |
-import renderLegacy, { hydrateLegacy, render, hydrate } from '../render'; | |
+import { render, hydrate } from '../render'; | |
import StyleSheet from '../StyleSheet'; | |
import React from 'react'; | |
export default function renderApplication(RootComponent, WrapperComponent, callback, options) { | |
var shouldHydrate = options.hydrate, | |
initialProps = options.initialProps, | |
- mode = options.mode, | |
rootTag = options.rootTag; | |
- var renderFn = shouldHydrate ? mode === 'concurrent' ? hydrate : hydrateLegacy : mode === 'concurrent' ? render : renderLegacy; | |
+ var renderFn = shouldHydrate ? hydrate : render; | |
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag); | |
return renderFn(/*#__PURE__*/React.createElement(AppContainer, { | |
WrapperComponent: WrapperComponent, | |
diff --git a/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js.flow b/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js.flow | |
index b9df2af..2b671ba 100644 | |
--- a/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js.flow | |
+++ b/node_modules/react-native-web/dist/exports/AppRegistry/renderApplication.js.flow | |
@@ -11,7 +11,7 @@ | |
import type { ComponentType, Node } from 'react'; | |
import AppContainer from './AppContainer'; | |
import invariant from 'fbjs/lib/invariant'; | |
-import renderLegacy, { hydrateLegacy, render, hydrate } from '../render'; | |
+import { render, hydrate } from '../render'; | |
import StyleSheet from '../StyleSheet'; | |
import React from 'react'; | |
export type Application = { | |
@@ -20,7 +20,6 @@ export type Application = { | |
declare export default function renderApplication<Props: Object>(RootComponent: ComponentType<Props>, WrapperComponent?: ?ComponentType<*>, callback?: () => void, options: { | |
hydrate: boolean, | |
initialProps: Props, | |
- mode: 'concurrent' | 'legacy', | |
rootTag: any, | |
}): Application; | |
declare export function getApplication(RootComponent: ComponentType<Object>, initialProps: Object, WrapperComponent?: ?ComponentType<*>): {| | |
diff --git a/node_modules/react-native-web/dist/exports/render/index.js b/node_modules/react-native-web/dist/exports/render/index.js | |
index aa91a2a..8f9a14d 100644 | |
--- a/node_modules/react-native-web/dist/exports/render/index.js | |
+++ b/node_modules/react-native-web/dist/exports/render/index.js | |
@@ -9,35 +9,15 @@ | |
'use client'; | |
-import { hydrate as domLegacyHydrate, render as domLegacyRender } from 'react-dom'; | |
import { createRoot as domCreateRoot, hydrateRoot as domHydrateRoot } from 'react-dom/client'; | |
-import unmountComponentAtNode from '../unmountComponentAtNode'; | |
import { createSheet } from '../StyleSheet/dom'; | |
export function hydrate(element, root) { | |
createSheet(root); | |
return domHydrateRoot(root, element); | |
} | |
-export function render(element, root) { | |
+export default function render(element, root) { | |
createSheet(root); | |
var reactRoot = domCreateRoot(root); | |
reactRoot.render(element); | |
return reactRoot; | |
} | |
\ No newline at end of file | |
-export function hydrateLegacy(element, root, callback) { | |
- createSheet(root); | |
- domLegacyHydrate(element, root, callback); | |
- return { | |
- unmount: function unmount() { | |
- return unmountComponentAtNode(root); | |
- } | |
- }; | |
-} | |
-export default function renderLegacy(element, root, callback) { | |
- createSheet(root); | |
- domLegacyRender(element, root, callback); | |
- return { | |
- unmount: function unmount() { | |
- return unmountComponentAtNode(root); | |
- } | |
- }; | |
-} | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/exports/render/index.js.flow b/node_modules/react-native-web/dist/exports/render/index.js.flow | |
index 1bd771e..729d57d 100644 | |
--- a/node_modules/react-native-web/dist/exports/render/index.js.flow | |
+++ b/node_modules/react-native-web/dist/exports/render/index.js.flow | |
@@ -9,11 +9,7 @@ | |
'use client'; | |
-import { hydrate as domLegacyHydrate, render as domLegacyRender } from 'react-dom'; | |
import { createRoot as domCreateRoot, hydrateRoot as domHydrateRoot } from 'react-dom/client'; | |
-import unmountComponentAtNode from '../unmountComponentAtNode'; | |
import { createSheet } from '../StyleSheet/dom'; | |
declare export function hydrate(element: any, root: any): any; | |
-declare export function render(element: any, root: any): any; | |
-declare export function hydrateLegacy(element: any, root: any, callback: any): any; | |
-declare export default function renderLegacy(element: any, root: any, callback: any): any; | |
\ No newline at end of file | |
+declare export default function render(element: any, root: any): any; | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js b/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js | |
index 925051c..cea4dee 100644 | |
--- a/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js | |
+++ b/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js | |
@@ -7,5 +7,7 @@ | |
* | |
*/ | |
-import { unmountComponentAtNode } from 'react-dom'; | |
-export default unmountComponentAtNode; | |
\ No newline at end of file | |
+export default function unmountComponentAtNode(rootTag) { | |
+ rootTag.unmount(); | |
+ return true; | |
+} | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js.flow b/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js.flow | |
index b950090..90ec151 100644 | |
--- a/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js.flow | |
+++ b/node_modules/react-native-web/dist/exports/unmountComponentAtNode/index.js.flow | |
@@ -6,6 +6,4 @@ | |
* | |
* @noflow | |
*/ | |
- | |
-import { unmountComponentAtNode } from 'react-dom'; | |
-export default unmountComponentAtNode; | |
\ No newline at end of file | |
+declare export default function unmountComponentAtNode(rootTag: any): any; | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.js.flow b/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.js.flow | |
new file mode 100644 | |
index 0000000..9eb6144 | |
--- /dev/null | |
+++ b/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.js.flow | |
@@ -0,0 +1,191 @@ | |
+/** | |
+ * Copyright (c) Meta Platforms, Inc. and affiliates. | |
+ * | |
+ * This source code is licensed under the MIT license found in the | |
+ * LICENSE file in the root directory of this source tree. | |
+ * | |
+ * @flow strict-local | |
+ */ | |
+ | |
+import * as React from 'react'; | |
+import { act, render } from '@testing-library/react'; | |
+import { createEventTarget } from 'dom-event-testing-library'; | |
+import { addEventListener } from '..'; | |
+describe('addEventListener', () => { | |
+ describe('addEventListener()', () => { | |
+ test('event dispatched on target', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('event dispatched on parent', () => { | |
+ const listener = jest.fn(); | |
+ const listenerCapture = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const parentRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const parent = createEventTarget(parentRef.current); | |
+ act(() => { | |
+ parent.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(0); | |
+ expect(listenerCapture).toBeCalledTimes(0); | |
+ }); | |
+ test('event dispatched on child', () => { | |
+ const log = []; | |
+ const listener = jest.fn(() => { | |
+ log.push('bubble'); | |
+ }); | |
+ const listenerCapture = jest.fn(() => { | |
+ log.push('capture'); | |
+ }); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(1); | |
+ expect(listener).toBeCalledTimes(1); | |
+ expect(log).toEqual(['capture', 'bubble']); | |
+ }); | |
+ test('event dispatched on text node', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const text = createEventTarget(childRef.current.firstChild); | |
+ act(() => { | |
+ text.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listener can be attached to document', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ render(<Component target={document} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listener can be attached to window ', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ render(<Component target={window} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('custom event dispatched on target', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ act(() => { | |
+ const event = new CustomEvent('magic-event', { | |
+ bubbles: true | |
+ }); | |
+ targetRef.current.dispatchEvent(event); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listeners can be set on multiple targets simultaneously', () => { | |
+ const log = []; | |
+ const targetRef = React.createRef(); | |
+ const parentRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ const listener = jest.fn(e => { | |
+ log.push(['bubble', e.currentTarget.id]); | |
+ }); | |
+ const listenerCapture = jest.fn(e => { | |
+ log.push(['capture', e.currentTarget.id]); | |
+ }); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(2); | |
+ expect(listener).toBeCalledTimes(2); | |
+ expect(log).toEqual([['capture', 'parent'], ['capture', 'target'], ['bubble', 'target'], ['bubble', 'parent']]); | |
+ }); | |
+ test('listeners are specific to each event handle', () => { | |
+ const log = []; | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ const listener = jest.fn(e => { | |
+ log.push(['bubble', 'target']); | |
+ }); | |
+ const listenerAlt = jest.fn(e => { | |
+ log.push(['bubble', 'target-alt']); | |
+ }); | |
+ const listenerCapture = jest.fn(e => { | |
+ log.push(['capture', 'target']); | |
+ }); | |
+ const listenerCaptureAlt = jest.fn(e => { | |
+ log.push(['capture', 'target-alt']); | |
+ }); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(1); | |
+ expect(listenerCaptureAlt).toBeCalledTimes(1); | |
+ expect(listener).toBeCalledTimes(1); | |
+ expect(listenerAlt).toBeCalledTimes(1); | |
+ expect(log).toEqual([['capture', 'target'], ['capture', 'target-alt'], ['bubble', 'target'], ['bubble', 'target-alt']]); | |
+ }); | |
+ }); | |
+ describe('stopPropagation and stopImmediatePropagation', () => { | |
+ test('stopPropagation works as expected', () => { | |
+ const childListener = jest.fn(e => { | |
+ e.stopPropagation(); | |
+ }); | |
+ const targetListener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(childListener).toBeCalledTimes(1); | |
+ expect(targetListener).toBeCalledTimes(0); | |
+ }); | |
+ test('stopImmediatePropagation works as expected', () => { | |
+ const firstListener = jest.fn(e => { | |
+ e.stopImmediatePropagation(); | |
+ }); | |
+ const secondListener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(firstListener).toBeCalledTimes(1); | |
+ expect(secondListener).toBeCalledTimes(0); | |
+ }); | |
+ }); | |
+}); | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.node.js.flow b/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.node.js.flow | |
new file mode 100644 | |
index 0000000..c1805b7 | |
--- /dev/null | |
+++ b/node_modules/react-native-web/dist/modules/addEventListener/__tests__/index-test.node.js.flow | |
@@ -0,0 +1,21 @@ | |
+/** | |
+ * Copyright (c) Meta Platforms, Inc. and affiliates. | |
+ * | |
+ * This source code is licensed under the MIT license found in the | |
+ * LICENSE file in the root directory of this source tree. | |
+ * | |
+ * @flow strict-local | |
+ */ | |
+ | |
+import * as React from 'react'; | |
+import * as ReactDOMServer from 'react-dom/server'; | |
+import { addEventListener } from '..'; | |
+describe('addEventListener', () => { | |
+ test('can render correctly using ReactDOMServer', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ const output = ReactDOMServer.renderToString(<Component />); | |
+ expect(output).toBe('<div></div>'); | |
+ }); | |
+}); | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/modules/useEvent/__tests__/index-test.js.flow b/node_modules/react-native-web/dist/modules/useEvent/__tests__/index-test.js.flow | |
new file mode 100644 | |
index 0000000..9b57364 | |
--- /dev/null | |
+++ b/node_modules/react-native-web/dist/modules/useEvent/__tests__/index-test.js.flow | |
@@ -0,0 +1,247 @@ | |
+/** | |
+ * Copyright (c) Meta Platforms, Inc. and affiliates. | |
+ * | |
+ * This source code is licensed under the MIT license found in the | |
+ * LICENSE file in the root directory of this source tree. | |
+ * | |
+ * @flow strict-local | |
+ */ | |
+ | |
+import * as React from 'react'; | |
+import { act, render } from '@testing-library/react'; | |
+import { createEventTarget } from 'dom-event-testing-library'; | |
+import useEvent from '..'; | |
+describe('use-event', () => { | |
+ describe('setListener()', () => { | |
+ test('event dispatched on target', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('event dispatched on parent', () => { | |
+ const listener = jest.fn(); | |
+ const listenerCapture = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const parentRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const parent = createEventTarget(parentRef.current); | |
+ act(() => { | |
+ parent.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(0); | |
+ expect(listenerCapture).toBeCalledTimes(0); | |
+ }); | |
+ test('event dispatched on child', () => { | |
+ const log = []; | |
+ const listener = jest.fn(() => { | |
+ log.push('bubble'); | |
+ }); | |
+ const listenerCapture = jest.fn(() => { | |
+ log.push('capture'); | |
+ }); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(1); | |
+ expect(listener).toBeCalledTimes(1); | |
+ expect(log).toEqual(['capture', 'bubble']); | |
+ }); | |
+ test('event dispatched on text node', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const text = createEventTarget(childRef.current.firstChild); | |
+ act(() => { | |
+ text.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listener can be attached to document ', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ render(<Component target={document} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listener can be attached to window ', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ render(<Component target={window} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listener is replaceable', () => { | |
+ const listener = jest.fn(); | |
+ const listenerAlt = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ const { | |
+ rerender | |
+ } = render(<Component onClick={listener} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ rerender(<Component onClick={listenerAlt} />); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ expect(listenerAlt).toBeCalledTimes(1); | |
+ }); | |
+ test('listener is removed when value is null', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(arg0: any): any; | |
+ const { | |
+ unmount | |
+ } = render(<Component off={false} />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ | |
+ // this should unset the listener | |
+ unmount(); | |
+ listener.mockClear(); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(listener).toBeCalledTimes(0); | |
+ }); | |
+ test('custom event dispatched on target', () => { | |
+ const listener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ act(() => { | |
+ const event = new CustomEvent('magic-event', { | |
+ bubbles: true | |
+ }); | |
+ targetRef.current.dispatchEvent(event); | |
+ }); | |
+ expect(listener).toBeCalledTimes(1); | |
+ }); | |
+ test('listeners can be set on multiple targets simultaneously', () => { | |
+ const log = []; | |
+ const targetRef = React.createRef(); | |
+ const parentRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ const listener = jest.fn(e => { | |
+ log.push(['bubble', e.currentTarget.id]); | |
+ }); | |
+ const listenerCapture = jest.fn(e => { | |
+ log.push(['capture', e.currentTarget.id]); | |
+ }); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(2); | |
+ expect(listener).toBeCalledTimes(2); | |
+ expect(log).toEqual([['capture', 'parent'], ['capture', 'target'], ['bubble', 'target'], ['bubble', 'parent']]); | |
+ }); | |
+ test('listeners are specific to each event handle', () => { | |
+ const log = []; | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ const listener = jest.fn(e => { | |
+ log.push(['bubble', 'target']); | |
+ }); | |
+ const listenerAlt = jest.fn(e => { | |
+ log.push(['bubble', 'target-alt']); | |
+ }); | |
+ const listenerCapture = jest.fn(e => { | |
+ log.push(['capture', 'target']); | |
+ }); | |
+ const listenerCaptureAlt = jest.fn(e => { | |
+ log.push(['capture', 'target-alt']); | |
+ }); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(listenerCapture).toBeCalledTimes(1); | |
+ expect(listenerCaptureAlt).toBeCalledTimes(1); | |
+ expect(listener).toBeCalledTimes(1); | |
+ expect(listenerAlt).toBeCalledTimes(1); | |
+ expect(log).toEqual([['capture', 'target'], ['capture', 'target-alt'], ['bubble', 'target'], ['bubble', 'target-alt']]); | |
+ }); | |
+ }); | |
+ describe('cleanup', () => { | |
+ test('removes all listeners for given event type from targets', () => { | |
+ const clickListener = jest.fn(); | |
+ declare function Component(): any; | |
+ const { | |
+ unmount | |
+ } = render(<Component />); | |
+ unmount(); | |
+ const target = createEventTarget(document); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(clickListener).toBeCalledTimes(0); | |
+ }); | |
+ }); | |
+ describe('stopPropagation and stopImmediatePropagation', () => { | |
+ test('stopPropagation works as expected', () => { | |
+ const childListener = jest.fn(e => { | |
+ e.stopPropagation(); | |
+ }); | |
+ const targetListener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ const childRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const child = createEventTarget(childRef.current); | |
+ act(() => { | |
+ child.click(); | |
+ }); | |
+ expect(childListener).toBeCalledTimes(1); | |
+ expect(targetListener).toBeCalledTimes(0); | |
+ }); | |
+ test('stopImmediatePropagation works as expected', () => { | |
+ const firstListener = jest.fn(e => { | |
+ e.stopImmediatePropagation(); | |
+ }); | |
+ const secondListener = jest.fn(); | |
+ const targetRef = React.createRef(); | |
+ declare function Component(): any; | |
+ render(<Component />); | |
+ const target = createEventTarget(targetRef.current); | |
+ act(() => { | |
+ target.click(); | |
+ }); | |
+ expect(firstListener).toBeCalledTimes(1); | |
+ expect(secondListener).toBeCalledTimes(0); | |
+ }); | |
+ }); | |
+}); | |
\ No newline at end of file | |
diff --git a/node_modules/react-native-web/dist/modules/useStable/__tests__/index-test.js.flow b/node_modules/react-native-web/dist/modules/useStable/__tests__/index-test.js.flow | |
new file mode 100644 | |
index 0000000..49edfc6 | |
--- /dev/null | |
+++ b/node_modules/react-native-web/dist/modules/useStable/__tests__/index-test.js.flow | |
@@ -0,0 +1,54 @@ | |
+/** | |
+ * Copyright (c) Meta Platforms, Inc. and affiliates. | |
+ * | |
+ * This source code is licensed under the MIT license found in the | |
+ * LICENSE file in the root directory of this source tree. | |
+ * | |
+ * @flow | |
+ */ | |
+ | |
+import * as React from 'react'; | |
+import { render } from '@testing-library/react'; | |
+import useStable from '..'; | |
+describe('useStable', () => { | |
+ let spy = {}; | |
+ declare var TestComponent: (arg0: any) => React.Node; | |
+ beforeEach(() => { | |
+ spy = {}; | |
+ }); | |
+ test('correctly sets the initial value', () => { | |
+ declare var initialValueCallback: () => any; | |
+ render(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(spy.value).toBe(5); | |
+ }); | |
+ test('does not change the value', () => { | |
+ let counter = 0; | |
+ declare var initialValueCallback: () => any; | |
+ const { | |
+ rerender | |
+ } = render(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(spy.value).toBe(0); | |
+ rerender(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(spy.value).toBe(0); | |
+ }); | |
+ test('only calls the callback once', () => { | |
+ let counter = 0; | |
+ declare var initialValueCallback: () => any; | |
+ const { | |
+ rerender | |
+ } = render(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(counter).toBe(1); | |
+ rerender(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(counter).toBe(1); | |
+ }); | |
+ test('does not change the value if the callback initially returns null', () => { | |
+ let counter = 0; | |
+ declare var initialValueCallback: () => any; | |
+ const { | |
+ rerender | |
+ } = render(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(spy.value).toBe(null); | |
+ rerender(<TestComponent initialValueCallback={initialValueCallback} />); | |
+ expect(spy.value).toBe(null); | |
+ }); | |
+}); | |
\ No newline at end of file | |
EOT | |
- name: Add Button component | |
working-directory: test-app | |
run: | | |
cat <<EOT > src/app/page.tsx | |
import { | |
Button, | |
ButtonText, | |
ButtonSpinner, | |
ButtonIcon, | |
ButtonGroup, | |
} from "@/components/ui/button"; | |
export default function Home() { | |
return ( | |
<main className="flex min-h-screen flex-col items-center justify-between p-24"> | |
<Button size="md" variant="solid" action="primary"> | |
<ButtonText>Hello World!</ButtonText> | |
</Button> | |
</main> | |
); | |
} | |
EOT | |
- name: Add postinstall script to package.json | |
working-directory: test-app | |
run: | | |
jq '.scripts.postinstall = "patch-package"' package.json > tmp.$$.json && mv tmp.$$.json package.json | |
- name: Run postinstall to apply patches | |
working-directory: test-app | |
run: npm run postinstall | |
- name: Install Babel dependencies | |
working-directory: test-app | |
run: | | |
npm install @babel/preset-react babel-loader --save-dev | |
- name: Install dependencies and clean cache | |
working-directory: test-app | |
run: | | |
npm install | |
# Clean npm cache to avoid potential issues with older versions | |
npm cache clean --force | |
- name: Build Next.js app | |
working-directory: test-app | |
env: | |
NEXT_TELEMETRY_DISABLED: 1 | |
run: | | |
echo "{ \"extends\": \"next/core-web-vitals\", \"rules\": {} }" > .eslintrc.json | |
npm run build -- --no-lint | |
- name: Start Next.js app | |
working-directory: test-app | |
run: | | |
npm run start & | |
sleep 20 | |
- name: Fetch and Log Server Response | |
run: | | |
curl -s http://localhost:3000 || echo "Failed to reach the server" | |
curl -s http://localhost:3000 > server_response.html | |
cat server_response.html | |
- name: Check if button is rendered | |
run: | | |
RESPONSE=$(curl -s http://localhost:3000) | |
echo "$RESPONSE" | grep -q "Hello World!" && echo "Button found" || (echo "Button not found" && exit 1) | |
notify: | |
needs: test-next-latest | |
if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main' | |
runs-on: ubuntu-latest | |
steps: | |
- name: Slack Notification | |
uses: 8398a7/action-slack@v3 | |
with: | |
status: ${{ job.status }} | |
text: 'Next.js Latest Test: ${{ job.status }}' | |
fields: repo,commit,action,eventName | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |