-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prevent crash in environments where
Element.prototype.getAnimations
…
… is not available (#3473) Recently we made improvements to the `Transition` component and internal `useTransition` hook. We now use the `Element.prototype.getAnimations` API to know whether or not all transitions are done. This API has been available in browsers since 2020, however jsdom doesn't have support for this. This results in a lot of failing tests where users rely on jsdom (e.g. inside of Jest or Vitest). In a perfect world, jsdom is not used because it's not a real browser and there is a lot you need to workaround to even mimic a real browser. I understand that just switching to real browser tests (using Playwright for example) is not an easy task that can be done easily. Even our tests still rely on jsdom… So to make the development experience better, we polyfill the `Element.prototype.getAnimations` API only in tests (`process.env.NODE_ENV === 'test'`) and show a warning in the console on how to proceed. The polyfill we ship simply returns an empty array for `node.getAnimations()`. This means that it will be _enough_ for most tests to pass. The exception is if you are actually relying on `transition-duration` and `transition-delay` CSS properties. The warning you will get looks like this: `````` Headless UI has polyfilled `Element.prototype.getAnimations` for your tests. Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings. Example usage: ```js import { mockAnimationsApi } from 'jsdom-testing-mocks' mockAnimationsApi() ``` `````` Fixes: #3470 Fixes: #3469 Fixes: #3468
- Loading branch information
1 parent
5b365f5
commit 4737c6d
Showing
6 changed files
with
66 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,46 +1 @@ | ||
globalThis.IS_REACT_ACT_ENVIRONMENT = true | ||
|
||
// These are not 1:1 perfect polyfills, but they implement the parts we need for | ||
// testing. The implementation of the `getAnimations` uses the `setTimeout` | ||
// approach we used in the past. | ||
// | ||
// This is only necessary because JSDOM does not implement `getAnimations` or | ||
// `CSSTransition` yet. This is a temporary solution until JSDOM implements | ||
// these features. Or, until we use proper browser tests using Puppeteer or | ||
// Playwright. | ||
{ | ||
if (typeof CSSTransition === 'undefined') { | ||
globalThis.CSSTransition = class CSSTransition { | ||
constructor(duration) { | ||
this.duration = duration | ||
} | ||
|
||
finished = new Promise((resolve) => { | ||
setTimeout(resolve, this.duration) | ||
}) | ||
} | ||
} | ||
|
||
if (typeof Element.prototype.getAnimations !== 'function') { | ||
Element.prototype.getAnimations = function () { | ||
let { transitionDuration, transitionDelay } = getComputedStyle(this) | ||
|
||
let [durationMs, delayMs] = [transitionDuration, transitionDelay].map((value) => { | ||
let [resolvedValue = 0] = value | ||
.split(',') | ||
// Remove falsy we can't work with | ||
.filter(Boolean) | ||
// Values are returned as `0.3s` or `75ms` | ||
.map((v) => (v.includes('ms') ? parseFloat(v) : parseFloat(v) * 1000)) | ||
.sort((a, z) => z - a) | ||
|
||
return resolvedValue | ||
}) | ||
|
||
let totalDuration = durationMs + delayMs | ||
if (totalDuration === 0) return [] | ||
|
||
return [new CSSTransition(totalDuration)] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters