Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,61 @@ jobs:
path: playwright-report/
retention-days: 1

# The packages take React ^18 || ^19 as a peer. This builds a React 18.3 app
# with the Vite plugin and the UI kit, then checks refs, context and events
# in Chromium, so React-19-only APIs cannot slip back in.
react-18:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- uses: actions-rust-lang/setup-rust-toolchain@v2

- name: Cache cargo registry + target
uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-react-18-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.4.0"
name: Install bun

- name: Cache bun dependencies
uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-react-18-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- uses: jetli/wasm-pack-action@v0.4.0
with:
version: "latest"
- name: Install Node.js
uses: actions/setup-node@v7
with:
node-version: 24
- run: bun install
- run: bun run build
- name: Type-check and build the React 18 app
working-directory: apps/react-18
run: |
bun ../../node_modules/@typescript/native/bin/tsc
bun run build
- name: Cache Playwright Browsers
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-react-18-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- name: Install Playwright Browsers
run: bunx playwright install chromium --with-deps
- name: Run React 18 E2E Tests
working-directory: apps/react-18
run: bun run test:e2e

# @devup-ui/vite-plugin must keep the CSS every server page needs when the
# build is vinext/RSC. The fixture shares one Devup UI header between server
# list pages and a client page, and the tests read the styles the browser
Expand Down
24 changes: 24 additions & 0 deletions apps/react-18/e2e/react-18.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, test } from '@playwright/test'

test('Devup UI runs on React 18', async ({ page }) => {
const errors: string[] = []
page.on('pageerror', (error) => errors.push(error.message))
page.on('console', (message) => {
if (message.type() === 'error') errors.push(message.text())
})
await page.goto('/')

await expect(page.getByTestId('version')).toHaveText(/^18\./)
await expect(page.getByTestId('refs')).toHaveText(
'button,input,input,input,textarea,input',
)

await page.getByText('select', { exact: true }).click()
await page.getByText('second', { exact: true }).click()
await expect(page.getByTestId('selected')).toHaveText('second')

await page.getByTestId('increase').click()
await expect(page.getByTestId('stepper')).toHaveValue('1')

expect(errors).toEqual([])
})
12 changes: 12 additions & 0 deletions apps/react-18/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Devup UI on React 18</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions apps/react-18/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "react-18-example",
"version": "0.1.0",
"type": "module",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview --port 3098 --strictPort",
"test:e2e": "bunx playwright test -c playwright.config.ts",
"lint": "bun ../../node_modules/@typescript/native/bin/tsc && eslint"
},
"dependencies": {
"@devup-ui/components": "workspace:^",
"@devup-ui/react": "workspace:^",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@devup-ui/vite-plugin": "workspace:^",
"@playwright/test": "^1.63.0",
"@types/react": "^18.3",
"@types/react-dom": "^18.3",
"@vitejs/plugin-react": "^6.1",
"typescript": "^7.0.2",
"vite": "^8.3"
}
}
18 changes: 18 additions & 0 deletions apps/react-18/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
testDir: './e2e',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: 'list',
use: {
baseURL: 'http://localhost:3098',
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: {
command: 'bun run preview',
url: 'http://localhost:3098',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
})
64 changes: 64 additions & 0 deletions apps/react-18/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Button,
Checkbox,
Input,
Radio,
Select,
SelectContainer,
SelectOption,
SelectTrigger,
Stepper,
StepperDecreaseButton,
StepperIncreaseButton,
StepperInput,
Textarea,
} from '@devup-ui/components'
import { Box, styled } from '@devup-ui/react'
import { useEffect, useRef, useState, version } from 'react'

const Field = styled('input', { color: 'red', p: 1 })

export function App() {
const buttonRef = useRef<HTMLButtonElement>(null)
const checkboxRef = useRef<HTMLInputElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const radioRef = useRef<HTMLInputElement>(null)
const textareaRef = useRef<HTMLTextAreaElement>(null)
const fieldRef = useRef<HTMLInputElement>(null)
const [refs, setRefs] = useState('')
const [selected, setSelected] = useState('')

useEffect(() => {
setRefs(
[buttonRef, checkboxRef, inputRef, radioRef, textareaRef, fieldRef]
.map((ref) => ref.current?.tagName.toLowerCase() ?? 'null')
.join(','),
)
}, [])

return (
<Box p={4}>
<output data-testid="version">{version}</output>
<Button ref={buttonRef}>button</Button>
<Checkbox ref={checkboxRef}>checkbox</Checkbox>
<Input ref={inputRef} />
<Radio ref={radioRef}>radio</Radio>
<Textarea ref={textareaRef} />
<Field ref={fieldRef} />
<output data-testid="refs">{refs}</output>
<Select onChange={(value) => setSelected(String(value))}>
<SelectTrigger>select</SelectTrigger>
<SelectContainer>
<SelectOption value="first">first</SelectOption>
<SelectOption value="second">second</SelectOption>
</SelectContainer>
</Select>
<output data-testid="selected">{selected}</output>
<Stepper>
<StepperDecreaseButton />
<StepperInput data-testid="stepper" />
<StepperIncreaseButton data-testid="increase" />
</Stepper>
</Box>
)
}
10 changes: 10 additions & 0 deletions apps/react-18/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'

import { App } from './App'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
23 changes: 23 additions & 0 deletions apps/react-18/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"paths": {
"react": ["./node_modules/@types/react"],
"react/*": ["./node_modules/@types/react/*"],
"react-dom": ["./node_modules/@types/react-dom"],
"react-dom/*": ["./node_modules/@types/react-dom/*"]
}
},
"include": ["**/*.ts", "**/*.tsx", "df/*.d.ts"],
"exclude": ["node_modules", "dist"]
}
16 changes: 16 additions & 0 deletions apps/react-18/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DevupUI } from '@devup-ui/vite-plugin'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
// The workspace packages keep React 19 for their own tests; a consumer
// install resolves their React peer to the app's single copy, as this does
resolve: { dedupe: ['react', 'react-dom'] },
plugins: [
react(),
DevupUI({
include: ['@devup-ui/components'],
singleCss: true,
}),
],
})
31 changes: 31 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading