add React 18 compatibility (#1326)

* bump dev dependencies to React 18

* setup Jest to include `IS_REACT_ACT_ENVIRONMENT`

* prefer `useId` from React 18 if it exists

In React 16 & 17, where `useId` doesn't exist, we will fallback to our
implementation we have been using up until now.

The `useId` exposed by React 18, ensures stable references even in SSR
environments.

* update expected events

React 18 now uses the proper events:
- `blur` -> `focusout`
- `focus` -> `focusin`

* ensure to wait a bit longer

This is a bit unfortunate, but since React 18 now does an extra
unmount/remount in `StrictMode` to ensure that your code is
ConcurrentMode ready, it takes a bit longer to settle what the DOM sees.

That said, this is a temporary "hack". We are going to experiment with
using tools like Puppeteer/Playwright to run our tests in an actual
browser instead to eliminate all the weird details that we have to keep
in mind.

* prefer `.focus()` over `fireEvent.focus(el)`

* abstract `microTask` polyfill code

* prefer our `focus(el)` function over `el.focus()`

Internally we would still use `el.focus()`, but this allows us to have
more control over that `focus` function.

* add React 18 to the React Playground

* improve hooks for React 18

- Improving the cleanup of useEffect hooks
- useIsoMorphicEffect instead of normal useEffect, so that we can use
  useLayoutEffect to be a bit quicker.

* improve disposables

- This allows us to add event listeners on a node, and get automatic
  cleanup once `dispose` gets called.
- We also return all the `d.add` calls, so that we can cleanup specific
  parts only instead of everything or nothing.

* reimplement the Transition component to be React 18 ready

* wait an additional frame for everything to settle

* update playground examples

* suppressConsoleLogs for RadioGroup components

* update changelog

* keep the `to` classes for a smoother transition

In the next transition we will remove _all_ classes provided and re-add
the once we need.

---

Some extra special thanks:

- Thanks @silvenon for your initial work on the `transition` events in #926
- Thanks @thecrypticace for doing late-night debugging sessions

Co-authored-by: =?UTF-8?q?Matija=20Marohni=C4=87?= <matija.marohnic@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This commit is contained in:
Robin Malfait
2022-04-12 18:23:07 +02:00
parent ab6310c278
commit 0162c57d88
34 changed files with 1640 additions and 1321 deletions
+1
View File
@@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Mimic browser select on focus when navigating via `Tab` ([#1272](https://github.com/tailwindlabs/headlessui/pull/1272))
- Ensure that there is always an active option in the `Combobox` ([#1279](https://github.com/tailwindlabs/headlessui/pull/1279), [#1281](https://github.com/tailwindlabs/headlessui/pull/1281))
- Allow `Enter` for form submit in `RadioGroup`, `Switch` and `Combobox` improvements ([#1285](https://github.com/tailwindlabs/headlessui/pull/1285))
- add React 18 compatibility ([#1326](https://github.com/tailwindlabs/headlessui/pull/1326))
### Added
+4 -2
View File
@@ -1,12 +1,14 @@
module.exports = function createJestConfig(root, options) {
let { setupFilesAfterEnv = [], transform = {}, ...rest } = options
return Object.assign(
{
rootDir: root,
setupFilesAfterEnv: ['<rootDir>../../jest/custom-matchers.ts'],
setupFilesAfterEnv: ['<rootDir>../../jest/custom-matchers.ts', ...setupFilesAfterEnv],
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
...transform,
},
},
options
rest
)
}
+1 -1
View File
@@ -39,7 +39,7 @@
"devDependencies": {
"@swc/core": "^1.2.131",
"@swc/jest": "^0.2.17",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/jest-dom": "^5.16.4",
"@types/node": "^14.14.22",
"esbuild": "^0.14.11",
"fast-glob": "^3.2.11",
+4 -1
View File
@@ -1,2 +1,5 @@
let create = require('../../jest/create-jest-config.cjs')
module.exports = create(__dirname, { displayName: 'React' })
module.exports = create(__dirname, {
displayName: 'React',
setupFilesAfterEnv: ['./jest.setup.js'],
})
+1
View File
@@ -0,0 +1 @@
globalThis.IS_REACT_ACT_ENVIRONMENT = true
+5 -5
View File
@@ -42,12 +42,12 @@
"react-dom": "^16 || ^17 || ^18"
},
"devDependencies": {
"@testing-library/react": "^11.2.3",
"@types/react": "16.14.21",
"@types/react-dom": "^16.9.0",
"@testing-library/react": "^13.0.0",
"@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14",
"esbuild": "^0.11.18",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"snapshot-diff": "^0.8.1"
}
}
@@ -866,7 +866,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Enter)
@@ -915,7 +915,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Try to focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Try to open the combobox
await press(Keys.Enter)
@@ -951,7 +951,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Enter)
@@ -1000,7 +1000,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleHidden })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Enter)
@@ -1073,7 +1073,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Enter)
@@ -1114,7 +1114,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Enter)
@@ -1153,7 +1153,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Space)
@@ -1200,7 +1200,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Try to open the combobox
await press(Keys.Space)
@@ -1238,7 +1238,7 @@ describe('Keyboard interactions', () => {
})
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Space)
@@ -1278,7 +1278,7 @@ describe('Keyboard interactions', () => {
})
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Space)
@@ -1319,7 +1319,7 @@ describe('Keyboard interactions', () => {
})
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.Space)
@@ -1358,7 +1358,7 @@ describe('Keyboard interactions', () => {
assertComboboxButtonLinkedWithCombobox()
// Re-focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
assertActiveElement(getComboboxButton())
// Close combobox
@@ -1397,7 +1397,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowDown)
@@ -1443,7 +1443,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Try to open the combobox
await press(Keys.ArrowDown)
@@ -1479,7 +1479,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowDown)
@@ -1517,7 +1517,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowDown)
@@ -1552,7 +1552,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowUp)
@@ -1598,7 +1598,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Try to open the combobox
await press(Keys.ArrowUp)
@@ -1634,7 +1634,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowUp)
@@ -1672,7 +1672,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowUp)
@@ -1709,7 +1709,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the button
getComboboxButton()?.focus()
await focus(getComboboxButton())
// Open combobox
await press(Keys.ArrowUp)
@@ -1899,7 +1899,7 @@ describe('Keyboard interactions', () => {
render(<Example />)
// Focus the input field
getComboboxInput()?.focus()
await focus(getComboboxInput())
assertActiveElement(getComboboxInput())
// Press enter (which should submit the form)
@@ -2212,7 +2212,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowDown)
@@ -2258,7 +2258,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Try to open the combobox
await press(Keys.ArrowDown)
@@ -2294,7 +2294,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowDown)
@@ -2332,7 +2332,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowDown)
@@ -2527,7 +2527,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -2573,7 +2573,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Try to open the combobox
await press(Keys.ArrowUp)
@@ -2609,7 +2609,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -2647,7 +2647,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -2684,7 +2684,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -2766,7 +2766,7 @@ describe('Keyboard interactions', () => {
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -3099,7 +3099,7 @@ describe('Keyboard interactions', () => {
)
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -3243,7 +3243,7 @@ describe('Keyboard interactions', () => {
)
// Focus the input
getComboboxInput()?.focus()
await focus(getComboboxInput())
// Open combobox
await press(Keys.ArrowUp)
@@ -12,7 +12,7 @@ import {
assertActiveElement,
getByText,
} from '../../test-utils/accessibility-assertions'
import { click, press, Keys, MouseButton } from '../../test-utils/interactions'
import { click, press, focus, Keys, MouseButton } from '../../test-utils/interactions'
import { Transition } from '../transitions/transition'
jest.mock('../../hooks/use-id')
@@ -133,7 +133,7 @@ describe('Rendering', () => {
)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -174,7 +174,7 @@ describe('Rendering', () => {
)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -218,7 +218,7 @@ describe('Rendering', () => {
render(<Example />)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -437,7 +437,7 @@ describe('Rendering', () => {
)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -474,7 +474,7 @@ describe('Rendering', () => {
)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -514,7 +514,7 @@ describe('Rendering', () => {
render(<Example />)
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Ensure the button is focused
assertActiveElement(getDisclosureButton())
@@ -579,14 +579,15 @@ describe('Composition', () => {
// Wait for all transitions to finish
await nextFrame()
await nextFrame()
// Verify that we tracked the `mounts` and `unmounts` in the correct order
expect(orderFn.mock.calls).toEqual([
['Mounting - Disclosure'],
['Mounting - Transition'],
['Mounting - Transition.Child'],
['Unmounting - Transition.Child'],
['Unmounting - Transition'],
['Unmounting - Transition.Child'],
])
})
)
@@ -611,7 +612,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Open disclosure
await press(Keys.Enter)
@@ -646,7 +647,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Try to open the disclosure
await press(Keys.Enter)
@@ -677,7 +678,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Open disclosure
await press(Keys.Enter)
@@ -717,7 +718,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Open disclosure
await press(Keys.Space)
@@ -748,7 +749,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Try to open the disclosure
await press(Keys.Space)
@@ -779,7 +780,7 @@ describe('Keyboard interactions', () => {
assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })
// Focus the button
getDisclosureButton()?.focus()
await focus(getDisclosureButton())
// Open disclosure
await press(Keys.Space)
@@ -741,7 +741,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -787,7 +787,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Try to open the listbox
await press(Keys.Enter)
@@ -822,7 +822,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -867,7 +867,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleHidden })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -936,7 +936,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -973,7 +973,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1007,7 +1007,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1044,7 +1044,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1083,7 +1083,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1219,7 +1219,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1262,7 +1262,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Try to open the listbox
await press(Keys.Space)
@@ -1297,7 +1297,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1334,7 +1334,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1368,7 +1368,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1405,7 +1405,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1444,7 +1444,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1536,7 +1536,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Space)
@@ -1585,7 +1585,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1636,7 +1636,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1689,7 +1689,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowDown)
@@ -1734,7 +1734,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Try to open the listbox
await press(Keys.ArrowDown)
@@ -1769,7 +1769,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowDown)
@@ -1806,7 +1806,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowDown)
@@ -1838,7 +1838,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1886,7 +1886,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1928,7 +1928,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -1964,7 +1964,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2012,7 +2012,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2057,7 +2057,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Try to open the listbox
await press(Keys.ArrowUp)
@@ -2092,7 +2092,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2129,7 +2129,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2165,7 +2165,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2203,7 +2203,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2245,7 +2245,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2302,7 +2302,7 @@ describe('Keyboard interactions', () => {
assertListbox({ state: ListboxState.InvisibleUnmounted })
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2354,7 +2354,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2390,7 +2390,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2494,7 +2494,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2530,7 +2530,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.Enter)
@@ -2634,7 +2634,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2773,7 +2773,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2945,7 +2945,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -2984,7 +2984,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -3025,7 +3025,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -3058,7 +3058,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getListboxButton()?.focus()
await focus(getListboxButton())
// Open listbox
await press(Keys.ArrowUp)
@@ -677,7 +677,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -721,7 +721,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Try to open the menu
await press(Keys.Enter)
@@ -748,7 +748,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -781,7 +781,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -818,7 +818,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -857,7 +857,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1041,7 +1041,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1083,7 +1083,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Try to open the menu
await press(Keys.Space)
@@ -1110,7 +1110,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1143,7 +1143,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1180,7 +1180,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1219,7 +1219,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1331,7 +1331,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Space)
@@ -1379,7 +1379,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1428,7 +1428,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1479,7 +1479,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowDown)
@@ -1523,7 +1523,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Try to open the menu
await press(Keys.ArrowDown)
@@ -1550,7 +1550,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowDown)
@@ -1581,7 +1581,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1629,7 +1629,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1671,7 +1671,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1707,7 +1707,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -1751,7 +1751,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Try to open the menu
await press(Keys.ArrowUp)
@@ -1778,7 +1778,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -1813,7 +1813,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -1851,7 +1851,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1893,7 +1893,7 @@ describe('Keyboard interactions', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -1943,7 +1943,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -1979,7 +1979,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -2083,7 +2083,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -2119,7 +2119,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.Enter)
@@ -2223,7 +2223,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -2362,7 +2362,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -2534,7 +2534,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -2573,7 +2573,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -2614,7 +2614,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -2647,7 +2647,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getMenuButton()?.focus()
await focus(getMenuButton())
// Open menu
await press(Keys.ArrowUp)
@@ -14,7 +14,7 @@ import {
assertContainsActiveElement,
getPopoverOverlay,
} from '../../test-utils/accessibility-assertions'
import { click, press, Keys, MouseButton, shift } from '../../test-utils/interactions'
import { click, press, focus, Keys, MouseButton, shift } from '../../test-utils/interactions'
import { Portal } from '../portal/portal'
import { Transition } from '../transitions/transition'
@@ -156,7 +156,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -197,7 +197,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -241,7 +241,7 @@ describe('Rendering', () => {
render(<Example />)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -431,7 +431,7 @@ describe('Rendering', () => {
</Popover>
)
getPopoverButton()?.focus()
await focus(getPopoverButton())
assertPopoverButton({ state: PopoverState.InvisibleHidden })
assertPopoverPanel({ state: PopoverState.InvisibleHidden })
@@ -462,7 +462,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -489,7 +489,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -502,7 +502,7 @@ describe('Rendering', () => {
assertActiveElement(getByText('Link 1'))
// Focus the button again
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the Popover is closed again
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
@@ -525,7 +525,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -552,7 +552,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -579,7 +579,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -616,7 +616,7 @@ describe('Rendering', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -656,7 +656,7 @@ describe('Rendering', () => {
render(<Example />)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Ensure the button is focused
assertActiveElement(getPopoverButton())
@@ -715,14 +715,15 @@ describe('Composition', () => {
// Wait for all transitions to finish
await nextFrame()
await nextFrame()
// Verify that we tracked the `mounts` and `unmounts` in the correct order
expect(orderFn.mock.calls).toEqual([
['Mounting - Popover'],
['Mounting - Transition'],
['Mounting - Transition.Child'],
['Unmounting - Transition.Child'],
['Unmounting - Transition'],
['Unmounting - Transition.Child'],
])
})
)
@@ -747,7 +748,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await press(Keys.Enter)
@@ -782,7 +783,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Try to open the popover
await press(Keys.Enter)
@@ -813,7 +814,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await press(Keys.Enter)
@@ -918,7 +919,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Verify popover is closed
assertPopoverButton({ state: PopoverState.InvisibleUnmounted })
@@ -953,7 +954,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Verify popover is closed
assertPopoverButton({ state: PopoverState.InvisibleUnmounted })
@@ -1425,7 +1426,7 @@ describe('Keyboard interactions', () => {
)
// Focus the button of the Popover
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await click(getPopoverButton())
@@ -1675,7 +1676,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await press(Keys.Space)
@@ -1706,7 +1707,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Try to open the popover
await press(Keys.Space)
@@ -1737,7 +1738,7 @@ describe('Keyboard interactions', () => {
assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })
// Focus the button
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await press(Keys.Space)
@@ -1963,7 +1964,7 @@ describe('Mouse interactions', () => {
</Popover>
)
getPopoverButton()?.focus()
await focus(getPopoverButton())
// Open popover
await click(getPopoverButton())
File diff suppressed because it is too large Load Diff
@@ -10,7 +10,7 @@ import {
getSwitchLabel,
getByText,
} from '../../test-utils/accessibility-assertions'
import { press, click, Keys } from '../../test-utils/interactions'
import { press, click, focus, Keys } from '../../test-utils/interactions'
jest.mock('../../hooks/use-id')
@@ -229,7 +229,7 @@ describe('Keyboard interactions', () => {
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
await focus(getSwitch())
// Toggle
await press(Keys.Space)
@@ -254,7 +254,7 @@ describe('Keyboard interactions', () => {
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
await focus(getSwitch())
// Try to toggle
await press(Keys.Enter)
@@ -284,7 +284,7 @@ describe('Keyboard interactions', () => {
render(<Example />)
// Focus the input field
getSwitch()?.focus()
await focus(getSwitch())
assertActiveElement(getSwitch())
// Press enter (which should submit the form)
@@ -309,7 +309,7 @@ describe('Keyboard interactions', () => {
assertSwitch({ state: SwitchState.Off })
// Focus the switch
getSwitch()?.focus()
await focus(getSwitch())
// Expect the switch to be active
assertActiveElement(getSwitch())
@@ -474,7 +474,7 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter to\\"
+ class=\\"\\""
+ class=\\"to\\""
`)
})
@@ -525,7 +525,7 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter to\\"
+ class=\\"\\""
+ class=\\"to\\""
`)
})
@@ -573,7 +573,7 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter to\\"
+ class=\\"\\""
+ class=\\"to\\""
`)
})
@@ -622,7 +622,7 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter to\\"
+ class=\\"\\""
+ class=\\"to\\""
`)
})
@@ -724,7 +724,7 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"leave to\\"
+ class=\\"\\"
+ class=\\"to\\"
+ hidden=\\"\\"
+ style=\\"display: none;\\""
`)
@@ -794,10 +794,10 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter enter-to\\"
+ class=\\"\\"
+ class=\\"enter-to\\"
Render 4:
- class=\\"\\"
- class=\\"enter-to\\"
+ class=\\"leave leave-from\\"
Render 5:
@@ -883,10 +883,10 @@ describe('Transitions', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter enter-to\\"
+ class=\\"\\"
+ class=\\"enter-to\\"
Render 4:
- class=\\"\\"
- class=\\"enter-to\\"
+ class=\\"leave leave-from\\"
Render 5:
@@ -896,12 +896,12 @@ describe('Transitions', () => {
Render 6: Transition took at least 75ms (yes)
- class=\\"leave leave-to\\"
- style=\\"\\"
+ class=\\"\\"
+ class=\\"leave-to\\"
+ hidden=\\"\\"
+ style=\\"display: none;\\"
Render 7:
- class=\\"\\"
- class=\\"leave-to\\"
- hidden=\\"\\"
- style=\\"display: none;\\"
+ class=\\"enter enter-from\\"
@@ -913,7 +913,7 @@ describe('Transitions', () => {
Render 9: Transition took at least 75ms (yes)
- class=\\"enter enter-to\\"
+ class=\\"\\""
+ class=\\"enter-to\\""
`)
})
)
@@ -1174,10 +1174,10 @@ describe('Events', () => {
Render 3: Transition took at least 50ms (yes)
- class=\\"enter enter-to\\"
+ class=\\"\\"
+ class=\\"enter-to\\"
Render 4:
- class=\\"\\"
- class=\\"enter-to\\"
+ class=\\"leave leave-from\\"
Render 5:
@@ -13,13 +13,6 @@ import React, {
Ref,
} from 'react'
import { Props } from '../../types'
import { useId } from '../../hooks/use-id'
import { useIsInitialRender } from '../../hooks/use-is-initial-render'
import { match } from '../../utils/match'
import { useIsMounted } from '../../hooks/use-is-mounted'
import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'
import {
Features,
forwardRefWithAs,
@@ -27,19 +20,21 @@ import {
render,
RenderStrategy,
} from '../../utils/render'
import { Reason, transition } from './utils/transition'
import { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'
import { match } from '../../utils/match'
import { microTask } from '../../utils/micro-task'
import { useId } from '../../hooks/use-id'
import { useIsMounted } from '../../hooks/use-is-mounted'
import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'
import { useLatestValue } from '../../hooks/use-latest-value'
import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'
import { useSyncRefs } from '../../hooks/use-sync-refs'
import { useLatestValue } from '../../hooks/use-latest-value'
import { useTransition } from '../../hooks/use-transition'
type ID = ReturnType<typeof useId>
function useSplitClasses(classes: string = '') {
return useMemo(
() => classes.split(' ').filter((className) => className.trim().length > 1),
[classes]
)
function splitClasses(classes: string = '') {
return classes.split(' ').filter((className) => className.trim().length > 1)
}
interface TransitionContextValues {
@@ -135,9 +130,11 @@ function useNesting(done?: () => void) {
},
})
if (!hasChildren(transitionableChildren) && mounted.current) {
doneRef.current?.()
}
microTask(() => {
if (!hasChildren(transitionableChildren) && mounted.current) {
doneRef.current?.()
}
})
})
let register = useLatestValue((childId: ID) => {
@@ -220,23 +217,23 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
let id = useId()
let isTransitioning = useRef(false)
let transitionInFlight = useRef(false)
let nesting = useNesting(() => {
// When all children have been unmounted we can only hide ourselves if and only if we are not
// transitioning ourselves. Otherwise we would unmount before the transitions are finished.
if (!isTransitioning.current) {
if (!transitionInFlight.current) {
setState(TreeStates.Hidden)
unregister.current(id)
}
})
useIsoMorphicEffect(() => {
useEffect(() => {
if (!id) return
return register.current(id)
}, [register, id])
useIsoMorphicEffect(() => {
useEffect(() => {
// If we are in another mode than the Hidden mode then ignore
if (strategy !== RenderStrategy.Hidden) return
if (!id) return
@@ -253,16 +250,15 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
})
}, [state, id, register, unregister, show, strategy])
let enterClasses = useSplitClasses(enter)
let enterFromClasses = useSplitClasses(enterFrom)
let enterToClasses = useSplitClasses(enterTo)
let enteredClasses = useSplitClasses(entered)
let leaveClasses = useSplitClasses(leave)
let leaveFromClasses = useSplitClasses(leaveFrom)
let leaveToClasses = useSplitClasses(leaveTo)
let classes = useLatestValue({
enter: splitClasses(enter),
enterFrom: splitClasses(enterFrom),
enterTo: splitClasses(enterTo),
entered: splitClasses(entered),
leave: splitClasses(leave),
leaveFrom: splitClasses(leaveFrom),
leaveTo: splitClasses(leaveTo),
})
let events = useEvents({ beforeEnter, afterEnter, beforeLeave, afterLeave })
let ready = useServerHandoffComplete()
@@ -276,69 +272,30 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
// Skipping initial transition
let skip = initial && !appear
useIsoMorphicEffect(() => {
let node = container.current
if (!node) return
if (!ready) return
if (skip) return
if (show === prevShow.current) return
let transitionDirection = (() => {
if (!ready) return 'idle'
if (skip) return 'idle'
if (prevShow.current === show) return 'idle'
return show ? 'enter' : 'leave'
})() as 'enter' | 'leave' | 'idle'
isTransitioning.current = true
if (show) events.current.beforeEnter()
if (!show) events.current.beforeLeave()
return show
? transition(
node,
enterClasses,
enterFromClasses,
enterToClasses,
enteredClasses,
(reason) => {
isTransitioning.current = false
if (reason === Reason.Finished) events.current.afterEnter()
}
)
: transition(
node,
leaveClasses,
leaveFromClasses,
leaveToClasses,
enteredClasses,
(reason) => {
isTransitioning.current = false
if (reason !== Reason.Finished) return
// When we don't have children anymore we can safely unregister from the parent and hide
// ourselves.
if (!hasChildren(nesting)) {
setState(TreeStates.Hidden)
unregister.current(id)
events.current.afterLeave()
}
}
)
}, [
events,
id,
isTransitioning,
unregister,
nesting,
useTransition({
container,
skip,
show,
ready,
enterClasses,
enterFromClasses,
enterToClasses,
leaveClasses,
leaveFromClasses,
leaveToClasses,
])
classes,
events,
direction: transitionDirection,
onStart: useLatestValue(() => {}),
onStop: useLatestValue((direction) => {
if (direction === 'leave' && !hasChildren(nesting)) {
// When we don't have children anymore we can safely unregister from the parent and hide
// ourselves.
setState(TreeStates.Hidden)
unregister.current(id)
}
}),
})
useIsoMorphicEffect(() => {
useEffect(() => {
if (!skip) return
if (strategy === RenderStrategy.Hidden) {
@@ -379,6 +336,9 @@ let TransitionRoot = forwardRefWithAs(function Transition<
let { show, appear = false, unmount, ...theirProps } = props as typeof props
let transitionRef = useSyncRefs(ref)
// The TransitionChild will also call this hook, and we have to make sure that we are ready.
useServerHandoffComplete()
let usesOpenClosedState = useOpenClosed()
if (show === undefined && usesOpenClosedState !== null) {
@@ -398,7 +358,23 @@ let TransitionRoot = forwardRefWithAs(function Transition<
setState(TreeStates.Hidden)
})
let initial = useIsInitialRender()
let [initial, setInitial] = useState(true)
// Change the `initial` value
let changes = useRef([show])
useIsoMorphicEffect(() => {
// We can skip this effect
if (initial === false) {
return
}
// Track the changes
if (changes.current.at(-1) !== show) {
changes.current.push(show)
setInitial(false)
}
}, [changes, show])
let transitionBag = useMemo<TransitionContextValues>(
() => ({ show: show as boolean, appear, initial }),
[show, appear, initial]
@@ -440,10 +416,14 @@ function Child<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(
let hasTransitionContext = useContext(TransitionContext) !== null
let hasOpenClosedContext = useOpenClosed() !== null
return !hasTransitionContext && hasOpenClosedContext ? (
<TransitionRoot {...props} />
) : (
<TransitionChild {...props} />
return (
<>
{!hasTransitionContext && hasOpenClosedContext ? (
<TransitionRoot {...props} />
) : (
<TransitionChild {...props} />
)}
</>
)
}
@@ -27,7 +27,20 @@ it('should be possible to transition', async () => {
)
await new Promise((resolve) => {
transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
transition(
element,
{
enter: ['enter'],
enterFrom: ['enterFrom'],
enterTo: ['enterTo'],
leave: [],
leaveFrom: [],
leaveTo: [],
entered: ['entered'],
},
true, // Show
resolve
)
})
await new Promise((resolve) => d.nextFrame(resolve))
@@ -42,7 +55,7 @@ it('should be possible to transition', async () => {
// necessary to put the classes on the element and immediately remove them.
// Cleanup phase
expect(snapshots[2].content).toEqual('<div class="entered"></div>')
expect(snapshots[2].content).toEqual('<div class="enterTo entered"></div>')
d.dispose()
})
@@ -71,11 +84,24 @@ it('should wait the correct amount of time to finish a transition', async () =>
)
let reason = await new Promise((resolve) => {
transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
transition(
element,
{
enter: ['enter'],
enterFrom: ['enterFrom'],
enterTo: ['enterTo'],
leave: [],
leaveFrom: [],
leaveTo: [],
entered: ['entered'],
},
true, // Show
resolve
)
})
await new Promise((resolve) => d.nextFrame(resolve))
expect(reason).toBe(Reason.Finished)
expect(reason).toBe(Reason.Ended)
// Initial render:
expect(snapshots[0].content).toEqual(`<div style="transition-duration: ${duration}ms;"></div>`)
@@ -98,7 +124,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
// Cleanup phase
expect(snapshots[3].content).toEqual(
`<div style="transition-duration: ${duration}ms;" class="entered"></div>`
`<div style="transition-duration: ${duration}ms;" class="enterTo entered"></div>`
)
})
@@ -128,11 +154,24 @@ it('should keep the delay time into account', async () => {
)
let reason = await new Promise((resolve) => {
transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
transition(
element,
{
enter: ['enter'],
enterFrom: ['enterFrom'],
enterTo: ['enterTo'],
leave: [],
leaveFrom: [],
leaveTo: [],
entered: ['entered'],
},
true, // Show
resolve
)
})
await new Promise((resolve) => d.nextFrame(resolve))
expect(reason).toBe(Reason.Finished)
expect(reason).toBe(Reason.Ended)
let estimatedDuration = Number(
(snapshots[snapshots.length - 1].recordedAt - snapshots[snapshots.length - 2].recordedAt) /
@@ -141,53 +180,3 @@ it('should keep the delay time into account', async () => {
expect(estimatedDuration).toBeWithinRenderFrame(duration + delayDuration)
})
it('should be possible to cancel a transition at any time', async () => {
let d = disposables()
let snapshots: {
content: string
recordedAt: bigint
relativeTime: number
}[] = []
let element = document.createElement('div')
document.body.appendChild(element)
// This duration is so overkill, however it will demonstrate that we can cancel transitions.
let duration = 5000
element.style.transitionDuration = `${duration}ms`
d.add(
reportChanges(
() => document.body.innerHTML,
(content) => {
let recordedAt = process.hrtime.bigint()
let total = snapshots.length
snapshots.push({
content,
recordedAt,
relativeTime:
total === 0 ? 0 : Number((recordedAt - snapshots[total - 1].recordedAt) / BigInt(1e6)),
})
}
)
)
expect.assertions(2)
// Setup the transition
let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], (reason) => {
expect(reason).toBe(Reason.Cancelled)
})
// Wait for a bit
await new Promise((resolve) => setTimeout(resolve, 20))
// Cancel the transition
cancel()
await new Promise((resolve) => d.nextFrame(resolve))
expect(snapshots.map((snapshot) => snapshot.content).join('\n')).not.toContain('enterTo')
})
@@ -1,5 +1,6 @@
import { once } from '../../../utils/once'
import { disposables } from '../../../utils/disposables'
import { match } from '../../../utils/match'
function addClasses(node: HTMLElement, ...classes: string[]) {
node && classes.length > 0 && node.classList.add(...classes)
@@ -10,7 +11,10 @@ function removeClasses(node: HTMLElement, ...classes: string[]) {
}
export enum Reason {
Finished = 'finished',
// Transition succesfully ended
Ended = 'ended',
// Transition was cancelled
Cancelled = 'cancelled',
}
@@ -22,7 +26,7 @@ function waitForTransition(node: HTMLElement, done: (reason: Reason) => void) {
// Safari returns a comma separated list of values, so let's sort them and take the highest value.
let { transitionDuration, transitionDelay } = getComputedStyle(node)
let [durationMs, delaysMs] = [transitionDuration, transitionDelay].map((value) => {
let [durationMs, delayMs] = [transitionDuration, transitionDelay].map((value) => {
let [resolvedValue = 0] = value
.split(',')
// Remove falsy we can't work with
@@ -34,22 +38,60 @@ function waitForTransition(node: HTMLElement, done: (reason: Reason) => void) {
return resolvedValue
})
// Waiting for the transition to end. We could use the `transitionend` event, however when no
// actual transition/duration is defined then the `transitionend` event is not fired.
//
// TODO: Downside is, when you slow down transitions via devtools this timeout is still using the
// full 100% speed instead of the 25% or 10%.
if (durationMs !== 0) {
d.setTimeout(() => {
done(Reason.Finished)
}, durationMs + delaysMs)
let totalDuration = durationMs + delayMs
if (totalDuration !== 0) {
let listeners: (() => void)[] = []
if (process.env.NODE_ENV === 'test') {
listeners.push(
d.setTimeout(() => {
done(Reason.Ended)
listeners.splice(0).forEach((dispose) => dispose())
}, totalDuration)
)
} else {
listeners.push(
d.addEventListener(
node,
'transitionrun',
() => {
// Cleanup "old" listeners
listeners.splice(0).forEach((dispose) => dispose())
// Register new listeners
listeners.push(
d.addEventListener(
node,
'transitionend',
() => {
done(Reason.Ended)
listeners.splice(0).forEach((dispose) => dispose())
},
{ once: true }
),
d.addEventListener(
node,
'transitioncancel',
() => {
done(Reason.Cancelled)
listeners.splice(0).forEach((dispose) => dispose())
},
{ once: true }
)
)
},
{ once: true }
)
)
}
} else {
// No transition is happening, so we should cleanup already. Otherwise we have to wait until we
// get disposed.
done(Reason.Finished)
done(Reason.Ended)
}
// If we get disposed before the timeout runs we should cleanup anyway
// If we get disposed before the transition finishes, we should cleanup anyway.
d.add(() => done(Reason.Cancelled))
return d.dispose
@@ -57,39 +99,60 @@ function waitForTransition(node: HTMLElement, done: (reason: Reason) => void) {
export function transition(
node: HTMLElement,
base: string[],
from: string[],
to: string[],
entered: string[],
classes: {
enter: string[]
enterFrom: string[]
enterTo: string[]
leave: string[]
leaveFrom: string[]
leaveTo: string[]
entered: string[]
},
show: boolean,
done?: (reason: Reason) => void
) {
let direction = show ? 'enter' : 'leave'
let d = disposables()
let _done = done !== undefined ? once(done) : () => {}
removeClasses(node, ...entered)
let base = match(direction, {
enter: () => classes.enter,
leave: () => classes.leave,
})
let to = match(direction, {
enter: () => classes.enterTo,
leave: () => classes.leaveTo,
})
let from = match(direction, {
enter: () => classes.enterFrom,
leave: () => classes.leaveFrom,
})
removeClasses(
node,
...classes.enter,
...classes.enterTo,
...classes.enterFrom,
...classes.leave,
...classes.leaveFrom,
...classes.leaveTo,
...classes.entered
)
addClasses(node, ...base, ...from)
d.nextFrame(() => {
removeClasses(node, ...from)
addClasses(node, ...to)
d.add(
waitForTransition(node, (reason) => {
removeClasses(node, ...to, ...base)
addClasses(node, ...entered)
return _done(reason)
})
)
waitForTransition(node, (reason) => {
if (reason === Reason.Ended) {
removeClasses(node, ...base)
addClasses(node, ...classes.entered)
}
return _done(reason)
})
})
// Once we get disposed, we should ensure that we cleanup after ourselves. In case of an unmount,
// the node itself will be nullified and will be a no-op. In case of a full transition the classes
// are already removed which is also a no-op. However if you go from enter -> leave mid-transition
// then we have some leftovers that should be cleaned.
d.add(() => removeClasses(node, ...base, ...from, ...to, ...entered))
// When we get disposed early, than we should also call the done method but switch the reason.
d.add(() => _done(Reason.Cancelled))
return d.dispose
}
+13 -9
View File
@@ -1,4 +1,4 @@
import { useState } from 'react'
import React from 'react'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
import { useServerHandoffComplete } from './use-server-handoff-complete'
@@ -13,13 +13,17 @@ function generateId() {
return ++id
}
export function useId() {
let ready = useServerHandoffComplete()
let [id, setId] = useState(ready ? generateId : null)
export let useId =
// Prefer React's `useId` if it's available.
// @ts-expect-error - `useId` doesn't exist in React < 18.
React.useId ??
function useId() {
let ready = useServerHandoffComplete()
let [id, setId] = React.useState(ready ? generateId : null)
useIsoMorphicEffect(() => {
if (id === null) setId(generateId())
}, [id])
useIsoMorphicEffect(() => {
if (id === null) setId(generateId())
}, [id])
return id != null ? '' + id : undefined
}
return id != null ? '' + id : undefined
}
@@ -5,6 +5,10 @@ export function useIsInitialRender() {
useEffect(() => {
initial.current = false
return () => {
initial.current = true
}
}, [])
return initial.current
@@ -1,9 +1,10 @@
import { useRef, useEffect } from 'react'
import { useRef } from 'react'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
export function useIsMounted() {
let mounted = useRef(false)
useEffect(() => {
useIsoMorphicEffect(() => {
mounted.current = true
return () => {
@@ -1,9 +1,10 @@
import { useRef, useEffect } from 'react'
import { useRef } from 'react'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
export function useLatestValue<T>(value: T) {
let cache = useRef(value)
useEffect(() => {
useIsoMorphicEffect(() => {
cache.current = value
}, [value])
@@ -1,22 +1,8 @@
import { MutableRefObject, useMemo, useRef } from 'react'
import { microTask } from '../utils/micro-task'
import { useLatestValue } from './use-latest-value'
import { useWindowEvent } from './use-window-event'
// Polyfill
function microTask(cb: () => void) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(cb)
} else {
Promise.resolve()
.then(cb)
.catch((e) =>
setTimeout(() => {
throw e
})
)
}
}
type Container = MutableRefObject<HTMLElement | null> | HTMLElement | null
type ContainerCollection = Container[] | Set<Container>
type ContainerInput = Container | ContainerCollection
@@ -0,0 +1,96 @@
import { MutableRefObject, useRef } from 'react'
import { Reason, transition } from '../components/transitions/utils/transition'
import { disposables } from '../utils/disposables'
import { match } from '../utils/match'
import { useDisposables } from './use-disposables'
import { useIsMounted } from './use-is-mounted'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
import { useLatestValue } from './use-latest-value'
interface TransitionArgs {
container: MutableRefObject<HTMLElement | null>
classes: MutableRefObject<{
enter: string[]
enterFrom: string[]
enterTo: string[]
leave: string[]
leaveFrom: string[]
leaveTo: string[]
entered: string[]
}>
events: MutableRefObject<{
beforeEnter: () => void
afterEnter: () => void
beforeLeave: () => void
afterLeave: () => void
}>
direction: 'enter' | 'leave' | 'idle'
onStart: MutableRefObject<(direction: TransitionArgs['direction']) => void>
onStop: MutableRefObject<(direction: TransitionArgs['direction']) => void>
}
export function useTransition({
container,
direction,
classes,
events,
onStart,
onStop,
}: TransitionArgs) {
let mounted = useIsMounted()
let d = useDisposables()
let latestDirection = useLatestValue(direction)
let beforeEvent = useLatestValue(() => {
return match(latestDirection.current, {
enter: () => events.current.beforeEnter(),
leave: () => events.current.beforeLeave(),
idle: () => {},
})
})
let afterEvent = useLatestValue(() => {
return match(latestDirection.current, {
enter: () => events.current.afterEnter(),
leave: () => events.current.afterLeave(),
idle: () => {},
})
})
useIsoMorphicEffect(() => {
let dd = disposables()
d.add(dd.dispose)
let node = container.current
if (!node) return // We don't have a DOM node (yet)
if (latestDirection.current === 'idle') return // We don't need to transition
if (!mounted.current) return
dd.dispose()
beforeEvent.current()
onStart.current(latestDirection.current)
dd.add(
transition(node, classes.current, latestDirection.current === 'enter', (reason) => {
dd.dispose()
match(reason, {
[Reason.Ended]() {
afterEvent.current()
onStop.current(latestDirection.current)
},
[Reason.Cancelled]: () => {},
})
})
)
return dd.dispose
}, [direction])
}
@@ -47,8 +47,8 @@ describe('Keyboard', () => {
Keys.Tab,
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'after'),
event('focusout', 'trigger'),
event('focusin', 'after'),
event('keyup', 'after'),
],
new Set(),
@@ -57,8 +57,8 @@ describe('Keyboard', () => {
shift(Keys.Tab),
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'before'),
event('focusout', 'trigger'),
event('focusin', 'before'),
event('keyup', 'before'),
],
new Set(),
@@ -79,8 +79,8 @@ describe('Keyboard', () => {
Keys.Tab,
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'after'),
event('focusout', 'trigger'),
event('focusin', 'after'),
event('keyup', 'after'),
],
new Set<Events>(['onKeyPress']),
@@ -89,8 +89,8 @@ describe('Keyboard', () => {
shift(Keys.Tab),
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'before'),
event('focusout', 'trigger'),
event('focusin', 'before'),
event('keyup', 'before'),
],
new Set<Events>(['onKeyPress']),
@@ -104,8 +104,8 @@ describe('Keyboard', () => {
Keys.Tab,
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'after'),
event('focusout', 'trigger'),
event('focusin', 'after'),
event('keyup', 'after'),
],
new Set<Events>(['onKeyUp']),
@@ -114,8 +114,8 @@ describe('Keyboard', () => {
shift(Keys.Tab),
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'before'),
event('focusout', 'trigger'),
event('focusin', 'before'),
event('keyup', 'before'),
],
new Set<Events>(['onKeyUp']),
@@ -126,8 +126,8 @@ describe('Keyboard', () => {
Keys.Tab,
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'after'),
event('focusout', 'trigger'),
event('focusin', 'after'),
event('keyup', 'after'),
],
new Set<Events>(['onBlur']),
@@ -136,8 +136,8 @@ describe('Keyboard', () => {
shift(Keys.Tab),
[
event('keydown', 'trigger'),
event('blur', 'trigger'),
event('focus', 'before'),
event('focusout', 'trigger'),
event('focusin', 'before'),
event('keyup', 'before'),
],
new Set<Events>(['onBlur']),
@@ -4,11 +4,13 @@ import { disposables } from '../utils/disposables'
let d = disposables()
function nextFrame(cb: Function): void {
setImmediate(() =>
setImmediate(() => {
setImmediate(() => {
cb()
setImmediate(() => {
cb()
})
})
)
})
}
export let Keys: Record<string, Partial<KeyboardEvent>> = {
@@ -282,7 +284,11 @@ export async function focus(element: Document | Element | Window | Node | null)
try {
if (element === null) return expect(element).not.toBe(null)
fireEvent.focus(element)
if (element instanceof HTMLElement) {
element.focus()
} else {
fireEvent.focus(element)
}
await new Promise(nextFrame)
} catch (err) {
@@ -7,24 +7,41 @@ export function disposables() {
queue.push(fn)
},
addEventListener<TEventName extends keyof WindowEventMap>(
element: HTMLElement,
name: TEventName,
listener: (event: WindowEventMap[TEventName]) => any,
options?: boolean | AddEventListenerOptions
) {
element.addEventListener(name, listener as any, options)
return api.add(() => element.removeEventListener(name, listener as any, options))
},
requestAnimationFrame(...args: Parameters<typeof requestAnimationFrame>) {
let raf = requestAnimationFrame(...args)
api.add(() => cancelAnimationFrame(raf))
return api.add(() => cancelAnimationFrame(raf))
},
nextFrame(...args: Parameters<typeof requestAnimationFrame>) {
api.requestAnimationFrame(() => {
api.requestAnimationFrame(...args)
return api.requestAnimationFrame(() => {
return api.requestAnimationFrame(...args)
})
},
setTimeout(...args: Parameters<typeof setTimeout>) {
let timer = setTimeout(...args)
api.add(() => clearTimeout(timer))
return api.add(() => clearTimeout(timer))
},
add(cb: () => void) {
disposables.push(cb)
return () => {
let idx = disposables.indexOf(cb)
if (idx >= 0) {
let [dispose] = disposables.splice(idx, 1)
dispose()
}
}
},
dispose() {
@@ -0,0 +1,14 @@
// Polyfill
export function microTask(cb: () => void) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(cb)
} else {
Promise.resolve()
.then(cb)
.catch((e) =>
setTimeout(() => {
throw e
})
)
}
}
+1
View File
@@ -1,4 +1,5 @@
module.exports = {
reactStrictMode: true,
devIndicators: {
autoPrerender: false,
},
+3 -3
View File
@@ -15,9 +15,9 @@
"@headlessui/react": "*",
"@popperjs/core": "^2.6.0",
"framer-motion": "^6.0.0",
"next": "^12.0.8",
"react": "16.14.0",
"react-dom": "16.14.0",
"next": "^12.1.4",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-flatpickr": "^3.10.9"
}
}
@@ -71,7 +71,20 @@ export default function Home() {
<button onClick={() => setNested(true)}>Show nested</button>
{nested && <Nested onClose={() => setNested(false)} />}
<Transition show={isOpen} as={Fragment} afterLeave={() => console.log('done')}>
<div
data-preload
className="translate-y-4 translate-y-0 translate-y-0 translate-y-4 scale-95 scale-100 scale-100 scale-95 transform transform transform transform transform transform opacity-0 opacity-75 opacity-75 opacity-0 opacity-75 opacity-0 opacity-100 opacity-100 opacity-0 opacity-0 opacity-100 opacity-100 opacity-0 transition transition duration-1000 duration-300 duration-200 duration-300 duration-200 duration-300 duration-75 ease-out ease-in ease-out ease-in ease-out ease-out sm:translate-y-0 sm:translate-y-0 sm:scale-95 sm:scale-100 sm:scale-100 sm:scale-95"
/>
<Transition
data-debug="Dialog"
show={isOpen}
as={Fragment}
beforeEnter={() => console.log('[Transition] Before enter')}
afterEnter={() => console.log('[Transition] After enter')}
beforeLeave={() => console.log('[Transition] Before leave')}
afterLeave={() => console.log('[Transition] After leave')}
>
<Dialog onClose={setIsOpen}>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-screen items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
@@ -84,6 +97,10 @@ export default function Home() {
leaveFrom="opacity-75"
leaveTo="opacity-0"
entered="opacity-75"
beforeEnter={() => console.log('[Transition.Child] [Overlay] Before enter')}
afterEnter={() => console.log('[Transition.Child] [Overlay] After enter')}
beforeLeave={() => console.log('[Transition.Child] [Overlay] Before leave')}
afterLeave={() => console.log('[Transition.Child] [Overlay] After leave')}
>
<Dialog.Overlay className="fixed inset-0 bg-gray-500 transition-opacity" />
</Transition.Child>
@@ -95,6 +112,10 @@ export default function Home() {
leave="ease-in transform duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
beforeEnter={() => console.log('[Transition.Child] [Panel] Before enter')}
afterEnter={() => console.log('[Transition.Child] [Panel] After enter')}
beforeLeave={() => console.log('[Transition.Child] [Panel] Before leave')}
afterLeave={() => console.log('[Transition.Child] [Panel] After leave')}
>
{/* This element is to trick the browser into centering the modal contents. */}
<span
@@ -14,6 +14,10 @@ export default function Home() {
return (
<div className="flex h-full w-screen justify-center bg-gray-50 p-12">
<div className="relative inline-block text-left">
<div
data-preload
className="scale-95 scale-100 scale-100 scale-95 transform transform transform transform opacity-0 opacity-100 opacity-100 opacity-0 transition transition duration-1000 duration-1000 ease-out ease-out"
/>
<Menu>
<span className="rounded-md shadow-sm">
<Menu.Button className="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800">
@@ -35,6 +39,10 @@ export default function Home() {
leave="transition duration-1000 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
beforeEnter={() => console.log('Before enter')}
afterEnter={() => console.log('After enter')}
beforeLeave={() => console.log('Before leave')}
afterLeave={() => console.log('After leave')}
>
<Menu.Items className="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none">
<div className="px-4 py-3">
@@ -17,6 +17,10 @@ export default function Home() {
return (
<div>
<div
data-preload
className="translate-y-4 translate-y-0 translate-y-0 translate-y-4 opacity-0 opacity-100 opacity-100 opacity-0 opacity-0 opacity-100 opacity-100 opacity-0 duration-300 duration-200 duration-300 duration-200 ease-out ease-in ease-out ease-in sm:translate-y-0 sm:translate-y-0 sm:scale-95 sm:scale-100 sm:scale-100 sm:scale-95"
/>
<div className="flex space-x-4 p-12">
<div className="inline-block p-12">
<span className="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
@@ -44,21 +48,17 @@ export default function Home() {
show={isOpen}
className="fixed inset-0 z-10 overflow-y-auto"
beforeEnter={() => {
addEvent('Before enter')
addEvent('[Root] Before enter')
}}
afterEnter={() => {
inputRef.current.focus()
addEvent('After enter')
inputRef.current?.focus()
addEvent('[Root] After enter')
}}
beforeLeave={() => {
addEvent('Before leave (before confirm)')
window.confirm('Are you sure?')
addEvent('Before leave (after confirm)')
addEvent('[Root] Before leave')
}}
afterLeave={() => {
addEvent('After leave (before alert)')
window.alert('Consider it done!')
addEvent('After leave (after alert)')
addEvent('[Root] After leave')
setEmail('')
}}
>
@@ -70,6 +70,10 @@ export default function Home() {
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
beforeEnter={() => addEvent('[Overlay] Before enter')}
afterEnter={() => addEvent('[Overlay] After enter')}
beforeLeave={() => addEvent('[Overlay] Before leave')}
afterLeave={() => addEvent('[Overlay] After leave')}
>
<div className="fixed inset-0 transition-opacity">
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
@@ -88,6 +92,10 @@ export default function Home() {
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
beforeEnter={() => addEvent('[Panel] Before enter')}
afterEnter={() => addEvent('[Panel] After enter')}
beforeLeave={() => addEvent('[Panel] Before leave')}
afterLeave={() => addEvent('[Panel] After leave')}
>
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
@@ -131,6 +139,7 @@ export default function Home() {
ref={inputRef}
value={email}
onChange={(event) => setEmail(event.target.value)}
type="email"
id="email"
className="form-input block w-full px-3 sm:text-sm sm:leading-5"
placeholder="name@example.com"
@@ -18,16 +18,27 @@ export default function Home() {
</button>
</span>
<div
hidden
data-preload
className="transform transform transform transform rounded-md rounded-md bg-red-500 bg-blue-500 bg-red-500 bg-green-500 bg-green-500 bg-blue-500 bg-white p-4 p-4 opacity-0 opacity-100 opacity-100 opacity-0 shadow shadow transition-colors transition-colors transition-colors transition transition duration-[5s] duration-1000 duration-1000 duration-1000 duration-1000 ease-out ease-in ease-out ease-in"
/>
<Transition
show={isOpen}
unmount={false}
enter="transition ease-out duration-300"
enterFrom="transform opacity-0"
enterTo="transform opacity-100"
leave="transition ease-in duration-300"
leaveFrom="transform opacity-100"
leaveTo="transform opacity-0"
className="rounded-md bg-white p-4 shadow"
appear={false}
beforeEnter={() => console.log('beforeEnter')}
afterEnter={() => console.log('afterEnter')}
beforeLeave={() => console.log('beforeLeave')}
afterLeave={() => console.log('afterLeave')}
enter="transition-colors ease-out duration-[5s]"
enterFrom="transform bg-red-500"
enterTo="transform bg-blue-500"
leave="transition-colors ease-in duration-[5s]"
leaveFrom="transform bg-blue-500"
leaveTo="transform bg-red-500"
entered="bg-blue-500"
className="h-64 rounded-md p-4 shadow"
>
Contents to show and hide
</Transition>
+125 -117
View File
@@ -531,65 +531,70 @@
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@next/env@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314"
integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ==
"@next/env@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.4.tgz#5af629b43075281ecd7f87938802b7cf5b67e94b"
integrity sha512-7gQwotJDKnfMxxXd8xJ2vsX5AzyDxO3zou0+QOXX8/unypA6icw5+wf6A62yKZ6qQ4UZHHxS68pb6UV+wNneXg==
"@next/swc-android-arm64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39"
integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA==
"@next/swc-android-arm-eabi@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.1.4.tgz#c3dae178b7c15ad627d2e9b8dfb38caecb5c4ac7"
integrity sha512-FJg/6a3s2YrUaqZ+/DJZzeZqfxbbWrynQMT1C5wlIEq9aDLXCFpPM/PiOyJh0ahxc0XPmi6uo38Poq+GJTuKWw==
"@next/swc-darwin-arm64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135"
integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg==
"@next/swc-android-arm64@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.4.tgz#f320d60639e19ecffa1f9034829f2d95502a9a51"
integrity sha512-LXraazvQQFBgxIg3Htny6G5V5he9EK7oS4jWtMdTGIikmD/OGByOv8ZjLuVLZLtVm3UIvaAiGtlQSLecxJoJDw==
"@next/swc-darwin-x64@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd"
integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug==
"@next/swc-darwin-arm64@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.4.tgz#fd578278312613eddcf3aee26910100509941b63"
integrity sha512-SSST/dBymecllZxcqTCcSTCu5o1NKk9I+xcvhn/O9nH6GWjgvGgGkNqLbCarCa0jJ1ukvlBA138FagyrmZ/4rQ==
"@next/swc-linux-arm-gnueabihf@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7"
integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog==
"@next/swc-darwin-x64@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.4.tgz#ace5f80d8c8348efe194f6d7074c6213c52b3944"
integrity sha512-p1lwdX0TVjaoDXQVuAkjtxVBbCL/urgxiMCBwuPDO7TikpXtSRivi+mIzBj5q7ypgICFmIAOW3TyupXeoPRAnA==
"@next/swc-linux-arm64-gnu@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093"
integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q==
"@next/swc-linux-arm-gnueabihf@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.4.tgz#2bf2c83863635f19c71c226a2df936e001cce29c"
integrity sha512-67PZlgkCn3TDxacdVft0xqDCL7Io1/C4xbAs0+oSQ0xzp6OzN2RNpuKjHJrJgKd0DsE1XZ9sCP27Qv0591yfyg==
"@next/swc-linux-arm64-musl@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566"
integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA==
"@next/swc-linux-arm64-gnu@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.4.tgz#d577190f641c9b4b463719dd6b8953b6ba9be8d9"
integrity sha512-OnOWixhhw7aU22TQdQLYrgpgFq0oA1wGgnjAiHJ+St7MLj82KTDyM9UcymAMbGYy6nG/TFOOHdTmRMtCRNOw0g==
"@next/swc-linux-x64-gnu@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e"
integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A==
"@next/swc-linux-arm64-musl@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.4.tgz#e70ffe70393d8f9242deecdb282ce5a8fd588b14"
integrity sha512-UoRMzPZnsAavdWtVylYxH8DNC7Uy0i6RrvNwT4PyQVdfANBn2omsUkcH5lgS2O7oaz0nAYLk1vqyZDO7+tJotA==
"@next/swc-linux-x64-musl@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31"
integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw==
"@next/swc-linux-x64-gnu@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.4.tgz#91498a130387fb1961902f2bee55863f8e910cff"
integrity sha512-nM+MA/frxlTLUKLJKorctdI20/ugfHRjVEEkcLp/58LGG7slNaP1E5d5dRA1yX6ISjPcQAkywas5VlGCg+uTvA==
"@next/swc-win32-arm64-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283"
integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw==
"@next/swc-linux-x64-musl@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.4.tgz#78057b03c148c121553d41521ad38f6c732762ff"
integrity sha512-GoRHxkuW4u4yKw734B9SzxJwVdyEJosaZ62P7ifOwcujTxhgBt3y76V2nNUrsSuopcKI2ZTDjaa+2wd5zyeXbA==
"@next/swc-win32-ia32-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1"
integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q==
"@next/swc-win32-arm64-msvc@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.4.tgz#05bbaabacac23b8edf6caa99eb86b17550a09051"
integrity sha512-6TQkQze0ievXwHJcVUrIULwCYVe3ccX6T0JgZ1SiMeXpHxISN7VJF/O8uSCw1JvXZYZ6ud0CJ7nfC5HXivgfPg==
"@next/swc-win32-x64-msvc@12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064"
integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg==
"@next/swc-win32-ia32-msvc@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.4.tgz#8fd2fb48f04a2802e51fc320878bf6b411c1c866"
integrity sha512-CsbX/IXuZ5VSmWCpSetG2HD6VO5FTsO39WNp2IR2Ut/uom9XtLDJAZqjQEnbUTLGHuwDKFjrIO3LkhtROXLE/g==
"@next/swc-win32-x64-msvc@12.1.4":
version "12.1.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.4.tgz#a72ed44c9b1f850986a30fe36c59e01f8a79b5f3"
integrity sha512-JtYuWzKXKLDMgE/xTcFtCm1MiCIRaAc5XYZfYX3n/ZWSI1SJS/GMm+Su0SAHJgRFavJh6U/p998YwO/iGTIgqQ==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -722,7 +727,7 @@
dependencies:
"@jest/create-cache-key-function" "^27.4.2"
"@testing-library/dom@^7.26.6", "@testing-library/dom@^7.28.1":
"@testing-library/dom@^7.26.6":
version "7.31.2"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a"
integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==
@@ -736,10 +741,24 @@
lz-string "^1.4.4"
pretty-format "^26.6.2"
"@testing-library/jest-dom@^5.11.9":
version "5.16.2"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.2.tgz#f329b36b44aa6149cd6ced9adf567f8b6aa1c959"
integrity sha512-6ewxs1MXWwsBFZXIk4nKKskWANelkdUehchEOokHsN8X7c2eKXGw+77aRV63UU8f/DTSVUPLaGxdrj4lN7D/ug==
"@testing-library/dom@^8.5.0":
version "8.12.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.12.0.tgz#fef5e545533fb084175dda6509ee71d7d2f72e23"
integrity sha512-rBrJk5WjI02X1edtiUcZhgyhgBhiut96r5Jp8J5qktKdcvLcZpKDW8i2hkGMMItxrghjXuQ5AM6aE0imnFawaw==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
"@types/aria-query" "^4.2.0"
aria-query "^5.0.0"
chalk "^4.1.0"
dom-accessibility-api "^0.5.9"
lz-string "^1.4.4"
pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.16.4":
version "5.16.4"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd"
integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
@@ -751,13 +770,14 @@
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react@^11.2.3":
version "11.2.7"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818"
integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==
"@testing-library/react@^13.0.0":
version "13.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.0.0.tgz#8cdaf4667c6c2b082eb0513731551e9db784e8bc"
integrity sha512-p0lYA1M7uoEmk2LnCbZLGmHJHyH59sAaZVXChTXlyhV/PRW9LoIh4mdf7tiXsO8BoNG+vN8UnFJff1hbZeXv+w==
dependencies:
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^7.28.1"
"@testing-library/dom" "^8.5.0"
"@types/react-dom" "*"
"@testing-library/vue@^5.8.2":
version "5.8.2"
@@ -875,17 +895,17 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
"@types/react-dom@^16.9.0":
version "16.9.14"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.14.tgz#674b8f116645fe5266b40b525777fc6bb8eb3bcd"
integrity sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==
"@types/react-dom@*", "@types/react-dom@^17.0.14":
version "17.0.14"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f"
integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ==
dependencies:
"@types/react" "^16"
"@types/react" "*"
"@types/react@16.14.21", "@types/react@^16":
version "16.14.21"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.21.tgz#35199b21a278355ec7a3c40003bd6a334bd4ae4a"
integrity sha512-rY4DzPKK/4aohyWiDRHS2fotN5rhBSK6/rz1X37KzNna9HJyqtaGAbq9fVttrEPWF5ywpfIP1ITL8Xi2QZn6Eg==
"@types/react@*", "@types/react@^17.0.43":
version "17.0.43"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.43.tgz#4adc142887dd4a2601ce730bc56c3436fdb07a55"
integrity sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@@ -1768,7 +1788,7 @@ diff-sequences@^27.5.1:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
dom-accessibility-api@^0.5.6:
dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9:
version "0.5.13"
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz#102ee5f25eacce09bdf1cfa5a298f86da473be4b"
integrity sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==
@@ -3668,28 +3688,28 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
next@^12.0.8:
version "12.1.0"
resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d"
integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q==
next@^12.1.4:
version "12.1.4"
resolved "https://registry.yarnpkg.com/next/-/next-12.1.4.tgz#597a9bdec7aec778b442c4f6d41afd2c64a54b23"
integrity sha512-DA4g97BM4Z0nKtDvCTm58RxdvoQyYzeg0AeVbh0N4Y/D8ELrNu47lQeEgRGF8hV4eQ+Sal90zxrJQQG/mPQ8CQ==
dependencies:
"@next/env" "12.1.0"
"@next/env" "12.1.4"
caniuse-lite "^1.0.30001283"
postcss "8.4.5"
styled-jsx "5.0.0"
use-subscription "1.5.1"
styled-jsx "5.0.1"
optionalDependencies:
"@next/swc-android-arm64" "12.1.0"
"@next/swc-darwin-arm64" "12.1.0"
"@next/swc-darwin-x64" "12.1.0"
"@next/swc-linux-arm-gnueabihf" "12.1.0"
"@next/swc-linux-arm64-gnu" "12.1.0"
"@next/swc-linux-arm64-musl" "12.1.0"
"@next/swc-linux-x64-gnu" "12.1.0"
"@next/swc-linux-x64-musl" "12.1.0"
"@next/swc-win32-arm64-msvc" "12.1.0"
"@next/swc-win32-ia32-msvc" "12.1.0"
"@next/swc-win32-x64-msvc" "12.1.0"
"@next/swc-android-arm-eabi" "12.1.4"
"@next/swc-android-arm64" "12.1.4"
"@next/swc-darwin-arm64" "12.1.4"
"@next/swc-darwin-x64" "12.1.4"
"@next/swc-linux-arm-gnueabihf" "12.1.4"
"@next/swc-linux-arm64-gnu" "12.1.4"
"@next/swc-linux-arm64-musl" "12.1.4"
"@next/swc-linux-x64-gnu" "12.1.4"
"@next/swc-linux-x64-musl" "12.1.4"
"@next/swc-win32-arm64-msvc" "12.1.4"
"@next/swc-win32-ia32-msvc" "12.1.4"
"@next/swc-win32-x64-msvc" "12.1.4"
nice-try@^1.0.4:
version "1.0.5"
@@ -4086,7 +4106,7 @@ pretty-format@^26.1.0, pretty-format@^26.6.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
pretty-format@^27.0.0, pretty-format@^27.5.1:
pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
@@ -4112,7 +4132,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.5.10, prop-types@^15.6.2:
prop-types@^15.5.10:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -4154,15 +4174,13 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
react-dom@16.14.0, react-dom@^16.14.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
react-dom@^18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0.tgz#26b88534f8f1dbb80853e1eabe752f24100d8023"
integrity sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.19.1"
scheduler "^0.21.0"
react-flatpickr@^3.10.9:
version "3.10.9"
@@ -4182,14 +4200,12 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react@16.14.0, react@^16.14.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
react@^18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96"
integrity sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
read-pkg-up@^7.0.1:
version "7.0.1"
@@ -4400,13 +4416,12 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
scheduler@^0.21.0:
version "0.21.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820"
integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
semver-compare@^1.0.0:
version "1.0.0"
@@ -4798,10 +4813,10 @@ style-value-types@5.0.0:
hey-listen "^1.0.8"
tslib "^2.1.0"
styled-jsx@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77"
integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA==
styled-jsx@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80"
integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw==
supports-color@^5.3.0:
version "5.5.0"
@@ -5007,13 +5022,6 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
use-subscription@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==
dependencies:
object-assign "^4.1.1"
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"