diff --git a/package.json b/package.json index 74d3235..afc4b3d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,8 @@ "@glimmer/component": "^1.0.4", "@glimmer/tracking": "^1.0.4", "@tailwindcss/ui": "^0.6.0", + "@testing-library/dom": "^8.1.0", + "@testing-library/user-event": "^13.1.9", "babel-eslint": "^10.1.0", "broccoli-asset-rev": "^3.0.0", "dotenv-cli": "^4.0.0", diff --git a/tests/accessibility-assertions.js b/tests/accessibility-assertions.js index be48ec3..58a4f64 100644 --- a/tests/accessibility-assertions.js +++ b/tests/accessibility-assertions.js @@ -4,6 +4,10 @@ function getDialog() { return document.querySelector('[role="dialog"]'); } +function getDialogs() { + return Array.from(document.querySelectorAll('[role="dialog"]')); +} + function getDialogOverlay() { return document.querySelector('[id$="-headlessui-dialog-overlay"]'); } @@ -16,6 +20,12 @@ function getDialogDescription() { return document.querySelector('[id$="-headlessui-dialog-description"]'); } +function getDialogOverlays() { + return Array.from( + document.querySelectorAll('[id$="-headlessui-dialog-overlay"]') + ); +} + const DialogState = { /** The dialog is visible to the user. */ Visible: 'Visible', @@ -31,14 +41,15 @@ function assertNever(x) { throw new Error('Unexpected object: ' + x); } -function assertActiveElement(element) { +function assertActiveElement(element, comment) { try { if (element === null) { Qunit.assert.notEqual(element, null); return; } - - Qunit.assert.equal(document.activeElement, element); + return Qunit.assert.waitFor(() => { + Qunit.assert.equal(document.activeElement, element, comment); + }); } catch (err) { Error.captureStackTrace(err, assertActiveElement); throw err; @@ -523,7 +534,9 @@ export { assertDialogOverlay, assertDialogTitle, getDialog, + getDialogs, getDialogOverlay, + getDialogOverlays, assertActiveElement, getByText, }; diff --git a/tests/integration/components/dialog-test.js b/tests/integration/components/dialog-test.js index df953f4..780eb8f 100644 --- a/tests/integration/components/dialog-test.js +++ b/tests/integration/components/dialog-test.js @@ -1,8 +1,13 @@ import { module, test, todo } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { click, render, setupOnerror } from '@ember/test-helpers'; -import triggerKeyEvent from '@ember/test-helpers/dom/trigger-key-event'; +import { + click, + render, + setupOnerror, + triggerKeyEvent, +} from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; +import userEvent from '@testing-library/user-event'; import { Keys } from 'ember-headlessui/utils/keyboard'; @@ -13,7 +18,9 @@ import { assertDialogOverlay, assertDialogTitle, getDialog, + getDialogs, getDialogOverlay, + getDialogOverlays, assertActiveElement, getByText, } from '../../accessibility-assertions'; @@ -484,19 +491,274 @@ module('Integration | Component | ', function (hooks) { }); module('Nesting', function () { - todo( - 'it should be possible to open nested Dialog components and close them with `Escape`', - async function () {} - ); + test.each( + 'it should be possible to open nested Dialog components and close them with `$strategy`', + [ + { + strategy: 'outside click', + action: () => click(document.body), + }, + { + strategy: 'ESCAPE click', + action: () => + triggerKeyEvent(document.activeElement, 'keyup', Keys.Escape), + }, + { + strategy: 'click on Dialog.Overlay', + action: () => click(getDialogOverlays().pop()), + }, + ], + async function (assert, { action }) { + await render(hbs` + {{#let (hash value=false) as |state|}} + + + {{/let}} + `); - todo( - 'it should be possible to open nested Dialog components and close them with `Outside Click`', - async function () {} - ); + assert.equal(getDialogs(), 0, 'Verify we have no open dialogs'); - todo( - 'it should be possible to open nested Dialog components and close them with `Click on Dialog.Overlay`', - async function () {} + await click(getByText('Open 1'), 'Open Dialog 1'); + + assert.equal( + getDialogs().length, + 1, + 'Verify that we have 1 open dialog' + ); + + await assertActiveElement( + getByText('Open 2 a'), + 'Verify that the `Open 2 a` has focus' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 b'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 a'), + 'Verify that we can tab around' + ); + + // Open Dialog 2 via the second button + await click(getByText('Open 2 b')); + + assert.equal( + getDialogs().length, + 2, + 'Verify that we have 2 open dialogs' + ); + + await assertActiveElement( + getByText('Open 3 a'), + 'Verify that the `Open 3 a` has focus' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 b'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 a'), + 'Verify that we can tab around' + ); + + //close the top most dialog with ${strategy} + await action(); + + assert.equal( + getDialogs().length, + 1, + 'Verify that we have 1 open dialog' + ); + + await assertActiveElement( + getByText('Open 2 b'), + 'Verify that the `Open 2 b` button got focused again' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 a'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 b'), + 'Verify that we can tab around' + ); + + // Open Dialog 2 via the second button + await click(getByText('Open 2 b')); + + await assertActiveElement( + getByText('Open 3 a'), + 'Verify that the `Open 3 a` has focus' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 b'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 a'), + 'Verify that we can tab around' + ); + + assert.equal( + getDialogs().length, + 2, + 'Verify that we have 2 open dialogs' + ); + + // Open Dialog 3 via button c + await click(getByText('Open 3 c')); + + await assertActiveElement( + getByText('Open 4 a'), + 'Verify that the `Open 4 a` has focus' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 4 b'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 4 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 4 a'), + 'Verify that we can tab around' + ); + + assert.equal( + getDialogs().length, + 3, + 'Verify that we have 3 open dialogs' + ); + + //close the top most dialog with ${strategy} + await action(); + + await assertActiveElement( + getByText('Open 3 c'), + 'Verify that the `Open 3 c` button got focused again' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 a'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 b'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 3 c'), + 'Verify that we can tab around' + ); + + assert.equal( + getDialogs().length, + 2, + 'Verify that we have 2 open dialogs' + ); + + //close the top most dialog with ${strategy} + await action(); + + assert.equal( + getDialogs().length, + 1, + 'Verify that we have 1 open dialog' + ); + + await assertActiveElement( + getByText('Open 2 b'), + 'Verify that the `Open 2 b` button got focused again' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 c'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 a'), + 'Verify that we can tab around' + ); + + userEvent.tab(); + await assertActiveElement( + getByText('Open 2 b'), + 'Verify that we can tab around' + ); + + //close the top most dialog with ${strategy} + await action(); + + assert.equal(getDialogs(), 0, 'Verify that we have 0 open dialogs'); + + await assertActiveElement( + getByText('Open 1'), + 'Verify that we have 1 open dialog' + ); + } ); }); diff --git a/yarn.lock b/yarn.lock index cdb0ce8..7bb9685 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,7 +9,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== @@ -921,6 +921,14 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/runtime-corejs3@^7.10.2": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz#0ef292bbce40ca00f874c9724ef175a12476465c" + integrity sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA== + dependencies: + core-js-pure "^3.15.0" + regenerator-runtime "^0.13.4" + "@babel/runtime@7.12.18": version "7.12.18" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.18.tgz#af137bd7e7d9705a412b3caaf991fe6aaa97831b" @@ -928,6 +936,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.2": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.5.tgz#665450911c6031af38f81db530f387ec04cd9a98" @@ -1343,6 +1358,17 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@jest/types@^27.0.6": + version "27.0.6" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.6.tgz#9a992bc517e0c49f035938b8549719c2de40706b" + integrity sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1523,11 +1549,37 @@ hex-rgb "^4.1.0" postcss-selector-parser "^6.0.2" +"@testing-library/dom@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.1.0.tgz#f8358b1883844ea569ba76b7e94582168df5370d" + integrity sha512-kmW9alndr19qd6DABzQ978zKQ+J65gU2Rzkl8hriIetPnwpesRaK4//jEQyYh8fEALmGhomD/LBQqt+o+DL95Q== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^4.2.2" + chalk "^4.1.0" + dom-accessibility-api "^0.5.6" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/user-event@^13.1.9": + version "13.1.9" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.1.9.tgz#29e49a42659ac3c1023565ff56819e0153a82e99" + integrity sha512-NZr0zL2TMOs2qk+dNlqrAdbaRW5dAmYwd1yuQ4r7HpkVEOj0MWuUjDWwKhcLd/atdBy8ZSMHSKp+kXSQe47ezg== + dependencies: + "@babel/runtime" "^7.12.5" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/aria-query@^4.2.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" + integrity sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg== + "@types/body-parser@*": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -1639,6 +1691,25 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + "@types/json-schema@*": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" @@ -1726,6 +1797,18 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/yargs-parser@*": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + +"@types/yargs@^16.0.0": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.3.tgz#4b6d35bb8e680510a7dc2308518a80ee1ef27e01" + integrity sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ== + dependencies: + "@types/yargs-parser" "*" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -2095,6 +2178,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-to-html@^0.6.6: version "0.6.15" resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" @@ -2153,6 +2241,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -4641,6 +4737,11 @@ core-js-compat@^3.14.0, core-js-compat@^3.9.1: browserslist "^4.16.6" semver "7.0.0" +core-js-pure@^3.15.0: + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.15.2.tgz#c8e0874822705f3385d3197af9348f7c9ae2e3ce" + integrity sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA== + core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.5: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" @@ -5032,6 +5133,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" + integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -9046,6 +9152,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" + integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= + macos-release@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2" @@ -10546,6 +10657,16 @@ prettier@^2.2.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== +pretty-format@^27.0.2: + version "27.0.6" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f" + integrity sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ== + dependencies: + "@jest/types" "^27.0.6" + ansi-regex "^5.0.0" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -10842,6 +10963,11 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"