[
  {
    "path": ".eslintignore",
    "content": "/dist\n/packages/**/dist\n/node_modules\n/packages/**/node_modules"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\nThanks for your interest in contributing to Headless UI! Please take a moment to review this document **before submitting a pull request**.\n\n- [Pull requests](#pull-requests)\n- [Monorepo](#monorepo)\n- [Installation](#installation)\n- [Coding standards](#coding-standards)\n- [Running tests](#running-tests)\n- [Running playgrounds](#running-playgrounds)\n- [Scripts summary](#scripts-summary)\n\n## Pull requests\n\n**Please ask first before starting work on any significant new features.**\n\nIt's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create [an issue](https://github.com/tailwindlabs/headlessui/issues) to first discuss any significant new features. This includes things like adding new components, exposing internal information, etc.\nAlso make sure that you are making changes to both the `React` and `Vue` versions so that we can ensure feature parity.\n\n## Monorepo\n\nThe Headless UI repo is a monorepo using `npm` workspaces.\n\n## Installation\n\nYou only require a `npm install` in the root directory to install everything you need.\n\n```sh\nnpm install\n```\n\n## Coding standards\n\nWe use `prettier` for making sure that the codebase is formatted consistently.\nTo automatically fix any style violations in your code, you can run:\n\n```sh\nnpm lint\n```\n\n**Note**: Whenever you commit, the lint check will run on all staged files.\n**Note**: In CI, we will only check your code, and not write with the formatted files. If you want to just check, then you can either run `npm run lint-check` or `CI=true npm run lint`\n\n## Running tests\n\nYou can run the test suite using the following commands:\n\n```sh\nnpm run test\n```\n\nYou can also run them for React or Vue individually:\n\n```sh\nnpm run react test\n\n# or\n\nnpm run vue test\n```\n\nPlease ensure that the tests are passing when submitting a pull request. If you're adding new features to Headless UI, please include tests.\n\n## Running playgrounds\n\nCurrently the `React` playground (located in `packages/playground-react`) is a Next.js app that contains some examples which you can find in the `pages` directory. The `Vue` playground (located in `packages/playground-vue`) is a Vite app that contains some examples which you can find in the `src/components` directory.\n\nYou can launch them by running:\n\n```sh\nnpm run react playground\n\n# or\n\nnpm run vue playground\n```\n\nThis will also start the necessary watchers so that you don't have to care about them.\n\n## Scripts summary\n\nGlobal scripts, and some aliases:\n\n- `npm install`: install all dependencies for all packages\n- `npm run clean`: this will call all `npm run {package} clean` commands\n- `npm run build`: this will call all `npm run {package} build` commands\n- `npm run lint`: this will `lint` all packages\n- `npm run test`: this will `test` all packages\n  - `npm run test`: run all jest tests\n  - `npm run test --watch`: run all jest tests in interactive mode\n  - `npm run test tabs`: run all jest tests filtered by `tabs`\n  - `npm run test tabs --watch`: run all jest tests in interactive mode filtered by `tabs`\n\nScripts per package:\n\n- `npm run react`: Prefix to run anything in the `@headlessui/react` package\n  - `npm run react test`: run all jest tests\n  - `npm run react test --watch`: run all jest tests in interactive mode\n  - `npm run react test tabs`: run all jest tests filtered by `tabs`\n  - `npm run react test tabs --watch`: run all jest tests in interactive mode filtered by `tabs`\n  - `npm run react build`: build the final artefacts\n  - `npm run react lint`: validate and fix the react codebase using prettier\n  - `npm run react watch`: start a watcher for the react esm build\n    - **Note**: this will be executed for you when using the `npm run react playground`\n    - **Note**: this is not required for jest. You will probably never need this\n  - `npm run react playground`: (alias) start a development server in the `playground-react` package\n    - **Note**: this will also run `npm run react watch` for you, which means that you only need to execute `npm run react playground`\n  - `npm run react clean`: this will remove `dist` files\n- `npm run vue`: Prefix to run anything in the `@headlessui/vue` package\n  - `npm run vue test`: run all jest tests\n  - `npm run vue test --watch`: run all jest tests in interactive mode\n  - `npm run vue test tabs`: run all jest tests filtered by `tabs`\n  - `npm run vue test tabs --watch`: run all jest tests in interactive mode filtered by `tabs`\n  - `npm run vue build`: build the final artefacts\n  - `npm run vue lint`: validate and fix the vue codebase using prettier\n  - `npm run vue watch`: start a watcher for the vue esm build\n    - **Note**: this will be executed for you when using the `npm run vue playground`\n    - **Note**: this is not required for jest. You will probably never need this\n  - `npm run vue playground`: (alias) start a development server in the `playground-vue` package\n    - **Note**: this will also run `npm run vue watch` for you, which means that you only need to execute `npm run react playground`\n  - `npm run vue clean`: this will remove `dist` files\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "custom: ['https://tailwindcss.com/sponsor']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug report\nabout: If you've already asked for help with a problem and confirmed something is broken with Headless UI itself, create a bug report.\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n<!-- Please provide all of the information requested below. We're a small team and without all of this information it's not possible for us to help and your bug report will be closed. -->\n\n**What package within Headless UI are you using?**\n\nFor example: @headlessui/react\n\n**What version of that package are you using?**\n\nFor example: v0.3.1\n\n**What browser are you using?**\n\nFor example: Chrome, Safari, or N/A\n\n**Reproduction URL**\n\nA public GitHub repo that includes a minimal reproduction of the bug. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn't matter if your real project is private/confidential, since we want a link to a separate, isolated reproduction anyways. Unfortunately we can't provide support without a reproduction, and your issue will be closed with no comment if this is not provided.\n\nYou can use one of the starting projects on CodeSandbox:\n\n- With React and Tailwind CSS: https://codesandbox.io/s/github/tailwindlabs/reproduction-headlessui-react\n- With Vue and Tailwind CSS: https://codesandbox.io/s/github/tailwindlabs/reproduction-headlessui-vue\n\n**Describe your issue**\n\nDescribe the problem you're seeing, any important steps to reproduce and what behavior you expect instead.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Get Help\n    url: https://github.com/tailwindlabs/headlessui/discussions/new?category=help\n    about: If you can't get something to work the way you expect, open a question in our discussion forums.\n  - name: Feature Request\n    url: https://github.com/tailwindlabs/headlessui/discussions/new?category=ideas\n    about: 'Suggest any ideas you have using our discussion forums.'\n  - name: Documentation Issue\n    url: https://github.com/tailwindlabs/headlessui/issues/new?title=%5BDOCS%5D:%20\n    about: 'For documentation issues, suggest changes on our documentation repository.'\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [main]\n  pull_request:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\nenv:\n  NODE_VERSION: 18.x\n\njobs:\n  install:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Begin CI...\n        uses: actions/checkout@v4\n      - name: Use Node ${{ env.NODE_VERSION }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ env.NODE_VERSION }}\n      - uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n      - name: Install dependencies\n        run: npm ci\n        env:\n          CI: true\n\n  lint:\n    runs-on: ubuntu-latest\n    needs: [install]\n\n    steps:\n      - name: Begin CI...\n        uses: actions/checkout@v4\n      - uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n      - name: Lint\n        run: npm run lint\n        env:\n          CI: true\n\n  test:\n    runs-on: ubuntu-latest\n    needs: [install]\n\n    steps:\n      - name: Begin CI...\n        uses: actions/checkout@v4\n      - uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n      - name: Test\n        run: |\n          npm run test || npm run test || npm run test || exit 1\n        env:\n          CI: true\n\n  build:\n    runs-on: ubuntu-latest\n    needs: [install]\n\n    steps:\n      - name: Begin CI...\n        uses: actions/checkout@v4\n      - uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n      - name: Build\n        run: npm run build\n        env:\n          CI: true\n\n  check-types:\n    runs-on: ubuntu-latest\n    needs: [build]\n\n    steps:\n      - name: Begin CI...\n        uses: actions/checkout@v4\n      - uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n      - name: Check Types\n        run: npm run lint-types\n        env:\n          CI: true\n"
  },
  {
    "path": ".github/workflows/prepare-release.yml",
    "content": "name: Prepare Release\n\non:\n  workflow_dispatch:\n  push:\n    tags:\n      - '**'\n\nenv:\n  CI: true\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    permissions:\n      contents: write # for softprops/action-gh-release to create GitHub release\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [18]\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - run: git fetch --tags -f\n\n      - name: Resolve version\n        id: vars\n        run: |\n          echo \"TAG_NAME=${{ github.ref_name }}\" >> $GITHUB_ENV\n\n      - name: Get release notes\n        run: |\n          RELEASE_NOTES=$(npm run release-notes $TAG_NAME --silent)\n          echo \"RELEASE_NOTES<<EOF\" >> $GITHUB_ENV\n          echo \"$RELEASE_NOTES\" >> $GITHUB_ENV\n          echo \"EOF\" >> $GITHUB_ENV\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Release\n        uses: softprops/action-gh-release@v2\n        with:\n          draft: true\n          tag_name: ${{ env.TAG_NAME }}\n          body: |\n            ${{ env.RELEASE_NOTES }}\n"
  },
  {
    "path": ".github/workflows/release-insiders.yml",
    "content": "name: Release Insiders\n\non:\n  push:\n    branches: [main]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n  # https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions\n  id-token: write\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [20]\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Use cached node_modules\n        id: cache\n        uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n          restore-keys: |\n            nodeModules-\n\n      - name: Install dependencies\n        run: npm ci\n        env:\n          CI: true\n\n      - name: Test\n        run: |\n          npm run test || npm run test || npm run test || exit 1\n        env:\n          CI: true\n\n      - name: Resolve version\n        id: vars\n        run: echo \"::set-output name=sha_short::$(git rev-parse --short HEAD)\"\n\n      - name: 'Version based on commit: 0.0.0-insiders.${{ steps.vars.outputs.sha_short }}'\n        run: npm version -w packages 0.0.0-insiders.${{ steps.vars.outputs.sha_short }} --force --no-git-tag-version\n\n      - name: Publish\n        run: npm publish -w packages --provenance --tag insiders\n        env:\n          CI: true\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  release:\n    types: [published]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions:\n  contents: read\n  # https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions\n  id-token: write\n\nenv:\n  CI: true\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [18]\n\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Use cached node_modules\n        id: cache\n        uses: actions/cache@v4\n        with:\n          path: '**/node_modules'\n          key: ${{ runner.os }}-${{ env.NODE_VERSION }}-modules-${{ hashFiles('**/package-lock.json') }}\n          restore-keys: |\n            nodeModules-\n\n      - name: Install dependencies\n        run: npm ci\n        env:\n          CI: true\n\n      - name: Test\n        run: |\n          npm run test || npm run test || npm run test || exit 1\n        env:\n          CI: true\n\n      - name: Calculate environment variables\n        run: |\n          echo \"TAG_NAME=${{ github.event.tag_name }}\" >> $GITHUB_ENV\n          echo \"RELEASE_CHANNEL=$(npm run release-channel $TAG_NAME --silent)\" >> $GITHUB_ENV\n          echo \"PACKAGE_PATH=$(npm run package-path $TAG_NAME --silent)\" >> $GITHUB_ENV\n\n      - name: Publish\n        run: npm publish ${{ env.PACKAGE_PATH }} --provenance --tag ${{ env.RELEASE_CHANNEL }}\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/packages/**/node_modules\n/playgrounds/**/node_modules\n\n# testing\n/coverage\n/packages/**/coverage\n/playgrounds/**/coverage\n\n# logs\n*.log\n/packages/**/*.log\n/playgrounds/**/*.log\n\n# next.js\n/.next/\n/playgrounds/**/.next/\n/out/\n/packages/**/out/\n/playgrounds/**/out/\n\n# production\n/dist\n/packages/**/dist\n/playgrounds/**/dist\n\n# misc\n.DS_Store\n*.pem\n.cache\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n"
  },
  {
    "path": ".prettierignore",
    "content": "dist/\nnode_modules/\ncoverage/\n.next/\n"
  },
  {
    "path": ".swcrc",
    "content": "{\n  \"minify\": false,\n  \"jsc\": {\n    \"parser\": {\n      \"syntax\": \"typescript\",\n      \"tsx\": true,\n      \"decorators\": false,\n      \"dynamicImport\": false\n    }\n  }\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nEach package has its own changelog.\n\n- [@headlessui/react](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/CHANGELOG.md)\n- [@headlessui/vue](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-vue/CHANGELOG.md)\n- [@headlessui/tailwindcss](https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-tailwindcss/CHANGELOG.md)\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Tailwind Labs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<h3 align=\"center\">\n  Headless UI\n</h3>\n\n<p align=\"center\">\n  A set of completely unstyled, fully accessible UI components, designed to integrate\n  beautifully with Tailwind CSS.\n</p>\n\n---\n\n## Documentation\n\nFor full documentation, visit [headlessui.com](https://headlessui.com).\n\n### Installing the latest version\n\nYou can install the latest version by using:\n\n- `npm install @headlessui/react@latest`\n- `npm install @headlessui/vue@latest`\n\n### Installing the insiders version\n\nYou can install the insiders version (which points to whatever the latest commit on the `main` branch is) by using:\n\n- `npm install @headlessui/react@insiders`\n- `npm install @headlessui/vue@insiders`\n\n**Note:** The insiders build doesn't follow semver and therefore doesn't guarantee that the APIs will be the same once they are released.\n\n## Packages\n\n| Name                                                                                                                 |                                                              Version                                                              |                                                              Downloads                                                               |\n| :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: |\n| [`@headlessui/react`](https://github.com/tailwindlabs/headlessui/tree/main/packages/%40headlessui-react)             |       [![npm version](https://img.shields.io/npm/v/@headlessui/react.svg)](https://www.npmjs.com/package/@headlessui/react)       |       [![npm downloads](https://img.shields.io/npm/dt/@headlessui/react.svg)](https://www.npmjs.com/package/@headlessui/react)       |\n| [`@headlessui/vue`](https://github.com/tailwindlabs/headlessui/tree/main/packages/%40headlessui-vue)                 |         [![npm version](https://img.shields.io/npm/v/@headlessui/vue.svg)](https://www.npmjs.com/package/@headlessui/vue)         |         [![npm downloads](https://img.shields.io/npm/dt/@headlessui/vue.svg)](https://www.npmjs.com/package/@headlessui/vue)         |\n| [`@headlessui/tailwindcss`](https://github.com/tailwindlabs/headlessui/tree/main/packages/%40headlessui-tailwindcss) | [![npm version](https://img.shields.io/npm/v/@headlessui/tailwindcss.svg)](https://www.npmjs.com/package/@headlessui/tailwindcss) | [![npm downloads](https://img.shields.io/npm/dt/@headlessui/tailwindcss.svg)](https://www.npmjs.com/package/@headlessui/tailwindcss) |\n\n## Community\n\nFor help, discussion about best practices, or feature ideas:\n\n[Discuss Headless UI on GitHub](https://github.com/tailwindlabs/headlessui/discussions)\n\n## Contributing\n\nIf you're interested in contributing to Headless UI, please read our [contributing docs](https://github.com/tailwindlabs/headlessui/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**.\n"
  },
  {
    "path": "jest/create-jest-config.cjs",
    "content": "module.exports = function createJestConfig(root, options) {\n  let { setupFilesAfterEnv = [], transform = {}, ...rest } = options\n  return Object.assign(\n    {\n      rootDir: root,\n      setupFilesAfterEnv: [\n        '<rootDir>../../jest/custom-matchers.ts',\n        '<rootDir>../../jest/polyfills.ts',\n        ...setupFilesAfterEnv,\n      ],\n      transform: {\n        '^.+\\\\.(t|j)sx?$': '@swc/jest',\n        ...transform,\n      },\n      globals: {\n        __DEV__: true,\n      },\n    },\n    rest\n  )\n}\n"
  },
  {
    "path": "jest/custom-matchers.ts",
    "content": "import '@testing-library/jest-dom/extend-expect'\n\n// Assuming requestAnimationFrame is roughly 60 frames per second\nlet frame = 1000 / 60\nlet amountOfFrames = 2\n\nlet formatter = new Intl.NumberFormat('en')\n\nexpect.extend({\n  toBeWithinRenderFrame(actual, expected) {\n    let min = expected - frame * amountOfFrames\n    let max = expected + frame * amountOfFrames\n\n    let pass = actual >= min && actual <= max\n\n    return {\n      message: pass\n        ? () => {\n            return `expected ${actual} not to be within range of a frame ${formatter.format(\n              min\n            )} - ${formatter.format(max)}`\n          }\n        : () => {\n            return `expected ${actual} not to be within range of a frame ${formatter.format(\n              min\n            )} - ${formatter.format(max)}`\n          },\n      pass,\n    }\n  },\n})\n"
  },
  {
    "path": "jest/polyfills.ts",
    "content": "import { mockAnimationsApi, mockResizeObserver } from 'jsdom-testing-mocks'\n\nmockAnimationsApi() // `Element.prototype.getAnimations` and `CSSTransition` polyfill\nmockResizeObserver() // `ResizeObserver` polyfill\n\n// JSDOM Doesn't implement innerText yet: https://github.com/jsdom/jsdom/issues/1245\n// So this is a hacky way of implementing it using `textContent`.\n// Real implementation doesn't use textContent because:\n// > textContent gets the content of all elements, including <script> and <style> elements. In\n// > contrast, innerText only shows \"human-readable\" elements.\n// >\n// > — https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext\nObject.defineProperty(HTMLElement.prototype, 'innerText', {\n  get() {\n    return this.textContent\n  },\n  set(value) {\n    this.textContent = value\n  },\n})\n\n// Source: https://github.com/testing-library/react-testing-library/issues/838#issuecomment-735259406\n//\n// Polyfill the PointerEvent class for JSDOM\nclass PointerEvent extends Event {\n  constructor(type, props) {\n    super(type, props)\n    if (props.button != null) {\n      // @ts-expect-error JSDOM doesn't support `button` yet...\n      this.button = props.button\n    }\n\n    if (props.pointerType != null) {\n      // @ts-expect-error JSDOM doesn't support `pointerType` yet...\n      this.pointerType = props.pointerType\n    }\n\n    // @ts-expect-error JSDOM doesn't support `pointerType` yet...\n    if (this.pointerType === undefined) {\n      // Fallback to `pointerType` of `'mouse'` if not provided.\n      // @ts-expect-error JSDOM doesn't support `pointerType` yet...\n      this.pointerType = 'mouse'\n    }\n  }\n}\n// @ts-expect-error JSDOM doesn't support `PointerEvent` yet...\nwindow.PointerEvent = PointerEvent\n"
  },
  {
    "path": "jest.config.cjs",
    "content": "module.exports = {\n  projects: ['<rootDir>/packages/*/jest.config.cjs'],\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"headlessui\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Headless UI components for various libraries like React and Vue\",\n  \"main\": \"index.js\",\n  \"repository\": \"https://github.com/tailwindlabs/headlessui\",\n  \"license\": \"MIT\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\",\n    \"playgrounds/*\"\n  ],\n  \"scripts\": {\n    \"react\": \"npm run --workspace=@headlessui/react\",\n    \"react-playground\": \"npm run --workspace=playground-react dev\",\n    \"playground-react\": \"npm run --workspace=playground-react dev\",\n    \"vue\": \"npm run --workspace=@headlessui/vue\",\n    \"playground-vue\": \"npm run --workspace=playground-vue dev\",\n    \"vue-playground\": \"npm run --workspace=playground-vue dev\",\n    \"clean\": \"npm run clean --workspaces --if-present\",\n    \"build\": \"npm-run-all -p 'react build' 'vue build'\",\n    \"test\": \"./scripts/test.sh\",\n    \"lint\": \"./scripts/lint.sh\",\n    \"lint-check\": \"CI=true ./scripts/lint.sh\",\n    \"lint-types\": \"CI=true npm run lint-types --workspaces --if-present\",\n    \"release-channel\": \"node ./scripts/release-channel.js\",\n    \"release-notes\": \"node ./scripts/release-notes.js\",\n    \"package-path\": \"node ./scripts/package-path.js\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\"\n    }\n  },\n  \"lint-staged\": {\n    \"*\": \"npm run lint\"\n  },\n  \"prettier\": {\n    \"printWidth\": 100,\n    \"semi\": false,\n    \"singleQuote\": true,\n    \"trailingComma\": \"es5\",\n    \"plugins\": [\n      \"prettier-plugin-organize-imports\",\n      \"prettier-plugin-tailwindcss\"\n    ],\n    \"overrides\": [\n      {\n        \"files\": [\n          \"./playgrounds/react/**/*\"\n        ],\n        \"options\": {\n          \"tailwindStylesheet\": \"./playgrounds/react/pages/styles.css\"\n        }\n      },\n      {\n        \"files\": [\n          \"./playgrounds/vue/**/*\"\n        ],\n        \"options\": {\n          \"tailwindStylesheet\": \"./playgrounds/vue/src/styles.css\"\n        }\n      }\n    ]\n  },\n  \"devDependencies\": {\n    \"@arethetypeswrong/cli\": \"^0.13.3\",\n    \"@swc-node/register\": \"^1.6.8\",\n    \"@swc/core\": \"^1.2.131\",\n    \"@swc/jest\": \"^0.2.17\",\n    \"@testing-library/jest-dom\": \"^5.16.4\",\n    \"@types/node\": \"^14.14.22\",\n    \"esbuild\": \"^0.17.8\",\n    \"fast-glob\": \"^3.2.11\",\n    \"husky\": \"^4.3.8\",\n    \"jest\": \"26\",\n    \"lint-staged\": \"^12.2.1\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^3.1.0\",\n    \"prettier-plugin-organize-imports\": \"^3.2.4\",\n    \"prettier-plugin-tailwindcss\": \"^0.6.11\",\n    \"resize-observer-polyfill\": \"^1.5.1\",\n    \"rimraf\": \"^3.0.2\",\n    \"tslib\": \"^2.3.1\",\n    \"typescript\": \"^5.4.3\"\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Fixed\n\n- Don’t render `<Portal>` while hydrating ([#3825](https://github.com/tailwindlabs/headlessui/pull/3825))\n\n## [2.2.9] - 2025-09-25\n\n### Fixed\n\n- Improve focus management in shadow DOM roots ([#3794](https://github.com/tailwindlabs/headlessui/pull/3794))\n- Don't accidentally open the `Combobox` when touching the `ComboboxButton` while dragging on mobile ([#3795](https://github.com/tailwindlabs/headlessui/pull/3795))\n- Ensure sibling `Dialog` components are scrollable on mobile ([#3796](https://github.com/tailwindlabs/headlessui/pull/3796))\n- Infer `Combobox` type based on `onChange` handler ([#3798](https://github.com/tailwindlabs/headlessui/pull/3798))\n- Allow home/end key default behavior inside `ComboboxInput` when `Combobox` is closed ([#3798](https://github.com/tailwindlabs/headlessui/pull/3798))\n- Ensure interacting with a `Dialog` on iOS works after interacting with a disallowed area ([#3801](https://github.com/tailwindlabs/headlessui/pull/3801))\n- Freeze Listbox values as soon as a value is selected ([#3802](https://github.com/tailwindlabs/headlessui/pull/3802))\n- Ensure refs are forwarded when freezing data ([#3390](https://github.com/tailwindlabs/headlessui/pull/3390))\n- Do not serialize React components into form fields ([49e9e8e](https://github.com/tailwindlabs/headlessui/commit/49e9e8e54d71b50971af7bc064a62827190e8b36))\n\n## [2.2.8] - 2025-09-12\n\n### Fixed\n\n- Ensure we are not freezing data when the `static` prop is used ([#3779](https://github.com/tailwindlabs/headlessui/pull/3779))\n- Ensure `onChange` types are contravariant instead of bivariant ([#3781](https://github.com/tailwindlabs/headlessui/pull/3781))\n- Support `<summary>` as a focusable element inside `<details>` ([#3389](https://github.com/tailwindlabs/headlessui/pull/3389))\n- Fix `Maximum update depth exceeded` crash when using `transition` prop ([#3782](https://github.com/tailwindlabs/headlessui/pull/3782))\n- Ensure pressing `Tab` in the `ComboboxInput`, correctly syncs the input value ([#3785](https://github.com/tailwindlabs/headlessui/pull/3785))\n- Ensure `--button-width` and `--input-width` have the latest value ([#3786](https://github.com/tailwindlabs/headlessui/pull/3786))\n- Fix 'Invalid prop `data-headlessui-state` supplied to `React.Fragment`' warning ([#3788](https://github.com/tailwindlabs/headlessui/pull/3788))\n- Ensure `element` in `ref` callback is always connected when rendering in a `Portal` ([#3789](https://github.com/tailwindlabs/headlessui/pull/3789))\n- Ensure form state is up to date when using uncontrolled components ([#3790](https://github.com/tailwindlabs/headlessui/pull/3790))\n- Ensure `data-open` on `ComboboxInput` is up to date ([#3791](https://github.com/tailwindlabs/headlessui/pull/3791))\n- Ensure changing the `immediate` prop value on the `Combobox` component works as expected ([#3792](https://github.com/tailwindlabs/headlessui/pull/3792))\n\n## [2.2.7] - 2025-07-30\n\n### Fixed\n\n- Fix incorrect double invocation of menu items, listbox options and combobox options ([#3766](https://github.com/tailwindlabs/headlessui/pull/3766))\n- Fix memory leak in SSR environment ([#3767](https://github.com/tailwindlabs/headlessui/pull/3767))\n- Ensure programmatic `.click()` on `MenuButton` ref works ([#3768](https://github.com/tailwindlabs/headlessui/pull/3768))\n- Don't activate hovered items while using the keyboard ([#3769](https://github.com/tailwindlabs/headlessui/pull/3769))\n\n## [2.2.6] - 2025-07-24\n\n### Fixed\n\n- Fix immediately closing Listbox by requiring some cursor movement ([#3762](https://github.com/tailwindlabs/headlessui/pull/3762))\n\n## [2.2.5] - 2025-07-23\n\n### Fixed\n\n- Fix listbox closing immediately after opening on touch devices ([#3755](https://github.com/tailwindlabs/headlessui/pull/3755))\n\n## [2.2.4] - 2025-05-20\n\n### Fixed\n\n- Fix `Combobox` error (unexpected undefined) when using virtual mode ([#3734](https://github.com/tailwindlabs/headlessui/pull/3734))\n\n## [2.2.3] - 2025-05-12\n\n### Added\n\n- Add a quick trigger action to the `Menu`, `Listbox` and `Combobox` components ([#3700](https://github.com/tailwindlabs/headlessui/pull/3700))\n\n### Fixed\n\n- Fix clicking `Label` component should open `<Input type=\"file\">` ([#3707](https://github.com/tailwindlabs/headlessui/pull/3707))\n- Ensure clicking on interactive elements inside `Label` component works ([#3709](https://github.com/tailwindlabs/headlessui/pull/3709))\n- Fix focus not returned to SVG Element ([#3704](https://github.com/tailwindlabs/headlessui/pull/3704))\n- Fix `Listbox` not focusing first or last option on ArrowUp / ArrowDown ([#3721](https://github.com/tailwindlabs/headlessui/pull/3721))\n- Performance improvement: only re-render top-level component when nesting components e.g.: `Menu` inside a `Dialog` ([#3722](https://github.com/tailwindlabs/headlessui/pull/3722))\n- Fix closing `Menu` when other `Menu` is opened ([#3726](https://github.com/tailwindlabs/headlessui/pull/3726))\n\n## [2.2.2] - 2025-04-17\n\n### Fixed\n\n- Improve `Menu` component performance ([#3685](https://github.com/tailwindlabs/headlessui/pull/3685))\n- Improve `Listbox` component performance ([#3688](https://github.com/tailwindlabs/headlessui/pull/3688))\n- Open `Menu` and `Listbox` on `mousedown` ([#3689](https://github.com/tailwindlabs/headlessui/pull/3689))\n- Fix `Transition` component from incorrectly exposing the `Closing` state ([#3696](https://github.com/tailwindlabs/headlessui/pull/3696))\n- Improve `Combobox` component performance ([#3697](https://github.com/tailwindlabs/headlessui/pull/3697))\n\n## [2.2.1] - 2025-04-04\n\n### Added\n\n- Accept `tabIndex` on the `Checkbox` component ([#3645](https://github.com/tailwindlabs/headlessui/pull/3645))\n- Accept `tabIndex` on the `RadioGroup` component ([#3646](https://github.com/tailwindlabs/headlessui/pull/3646))\n\n### Fixed\n\n- Use correct `ownerDocument` when using internal `Portal` component ([#3594](https://github.com/tailwindlabs/headlessui/pull/3594))\n- Bump `@tanstack/react-virtual` to fix warnings in React 19 projects ([#3588](https://github.com/tailwindlabs/headlessui/pull/3588))\n- Fix `aria-invalid` attributes to have a valid `'true'` value ([#3639](https://github.com/tailwindlabs/headlessui/pull/3639))\n- Add missing `invalid` prop to `Combobox` component ([#3677](https://github.com/tailwindlabs/headlessui/pull/3677))\n- Fix `Unexpected undefined` crash in `Combobox` component with `virtual` mode ([#3678](https://github.com/tailwindlabs/headlessui/pull/3678))\n\n## [2.2.0] - 2024-10-25\n\n### Added\n\n- Add React 19 support ([#3543](https://github.com/tailwindlabs/headlessui/pull/3543))\n\n## [2.1.10] - 2024-10-10\n\n### Fixed\n\n- Use `React.JSX` instead of deprecated global `JSX` ([#3511](https://github.com/tailwindlabs/headlessui/pull/3511))\n- Fix crash in `ListboxOptions` when using `as={Fragment}` ([#3513](https://github.com/tailwindlabs/headlessui/pull/3513))\n\n## [2.1.9] - 2024-10-03\n\n### Fixed\n\n- Ensure `Element` is available before polyfilling to prevent crashes in non-browser environments ([#3493](https://github.com/tailwindlabs/headlessui/pull/3493))\n- Fix crash when using `instanceof HTMLElement` in some environments ([#3494](https://github.com/tailwindlabs/headlessui/pull/3494))\n- Cleanup `process` in Combobox component when using virtualization ([#3495](https://github.com/tailwindlabs/headlessui/pull/3495))\n\n## [2.1.8] - 2024-09-12\n\n### Fixed\n\n- Fix crash when using `as={Fragment}` on `MenuButton`, `ListboxButton`, `DisclosureButton` or `Button` components ([#3478](https://github.com/tailwindlabs/headlessui/pull/3478))\n\n## [2.1.7] - 2024-09-11\n\n### Fixed\n\n- Prevent crash in environments where `Element.prototype.getAnimations` is not available ([#3473](https://github.com/tailwindlabs/headlessui/pull/3473))\n\n## [2.1.6] - 2024-09-09\n\n### Fixed\n\n- Fix `ListboxOptions` being incorrectly marked as `inert` ([#3466](https://github.com/tailwindlabs/headlessui/pull/3466))\n- Fix crash when using `DisclosureButton` inside of a `DisclosurePanel` when the `Disclosure` is open by default ([#3465](https://github.com/tailwindlabs/headlessui/pull/3465))\n\n## [2.1.5] - 2024-09-04\n\n### Fixed\n\n- Fix transition bug on Firefox, triggered by clicking the `PopoverButton` in rapid succession ([#3452](https://github.com/tailwindlabs/headlessui/pull/3452))\n\n## [2.1.4] - 2024-09-03\n\n### Fixed\n\n- Fix components not closing properly when using the `transition` prop ([#3448](https://github.com/tailwindlabs/headlessui/pull/3448))\n\n## [2.1.3] - 2024-08-23\n\n### Fixed\n\n- Ensure `Transition` component state doesn't change when it becomes hidden ([#3372](https://github.com/tailwindlabs/headlessui/pull/3372))\n- Fix closing components using the `transition` prop, and after scrolling the page ([#3407](https://github.com/tailwindlabs/headlessui/pull/3407))\n- Ensure all client components are marked correctly to avoid a crash with React 19 and Turbopack ([#3429](https://github.com/tailwindlabs/headlessui/pull/3429))\n\n## [2.1.2] - 2024-07-05\n\n### Fixed\n\n- Fix prematurely added anchoring styles on `ListboxOptions` ([#3337](https://github.com/tailwindlabs/headlessui/pull/3337))\n- Ensure `unmount` on `Dialog` works in combination with the `transition` prop on `DialogBackdrop` and `DialogPanel` components ([#3352](https://github.com/tailwindlabs/headlessui/pull/3352))\n- Fix crash in `Combobox` component when in `virtual` mode when options are empty ([#3356](https://github.com/tailwindlabs/headlessui/pull/3356))\n- Fix hanging tests when using `anchor` prop ([#3357](https://github.com/tailwindlabs/headlessui/pull/3357))\n- Fix `transition` and `focus` prop combination for `PopoverPanel` component ([#3361](https://github.com/tailwindlabs/headlessui/pull/3361))\n- Fix outside click in nested portalled `Popover` components ([#3362](https://github.com/tailwindlabs/headlessui/pull/3362))\n- Fix restoring focus to correct element when closing `Dialog` component ([#3365](https://github.com/tailwindlabs/headlessui/pull/3365))\n- Fix `flushSync` warning for `Combobox` component with `immediate` prop enabled ([#3366](https://github.com/tailwindlabs/headlessui/pull/3366))\n\n## [2.1.1] - 2024-06-26\n\n### Fixed\n\n- Fix issues spreading omitted props onto components ([#3313](https://github.com/tailwindlabs/headlessui/pull/3313))\n- Fix initial `anchor=\"selection\"` positioning ([#3324](https://github.com/tailwindlabs/headlessui/pull/3324))\n- Fix render prop in `ComboboxOptions` to use `any` instead of `unknown` ([#3327](https://github.com/tailwindlabs/headlessui/pull/3327))\n- Fix incorrect `Transition` boundary for `Dialog` component ([#3331](https://github.com/tailwindlabs/headlessui/pull/3331))\n\n## [2.1.0] - 2024-06-21\n\n### Added\n\n- Add ability to render multiple `Dialog` components at once (without nesting them) ([#3242](https://github.com/tailwindlabs/headlessui/pull/3242))\n- Add new data-attribute-based transition API ([#3273](https://github.com/tailwindlabs/headlessui/pull/3273), [#3285](https://github.com/tailwindlabs/headlessui/pull/3285), [#3307](https://github.com/tailwindlabs/headlessui/pull/3307), [#3309](https://github.com/tailwindlabs/headlessui/pull/3309), [#3312](https://github.com/tailwindlabs/headlessui/pull/3312))\n- Add `DialogBackdrop` component ([#3307](https://github.com/tailwindlabs/headlessui/pull/3307), [#3310](https://github.com/tailwindlabs/headlessui/pull/3310))\n- Add `PopoverBackdrop` component to replace `PopoverOverlay` ([#3308](https://github.com/tailwindlabs/headlessui/pull/3308))\n\n### Fixed\n\n- Keep `Combobox` open when clicking scrollbar in `ComboboxOptions` ([#3249](https://github.com/tailwindlabs/headlessui/pull/3249))\n- Ensure `ComboboxInput` does not sync with current value while typing ([#3259](https://github.com/tailwindlabs/headlessui/pull/3259))\n- Fix visual jitter in `Combobox` component when using native scrollbar ([#3190](https://github.com/tailwindlabs/headlessui/pull/3190))\n- Improve UX by freezing `ComboboxOptions` while closing ([#3304](https://github.com/tailwindlabs/headlessui/pull/3304))\n- Merge incoming `style` prop on `ComboboxOptions`, `ListboxOptions`, `MenuItems`, and `PopoverPanel` components ([#3250](https://github.com/tailwindlabs/headlessui/pull/3250))\n- Prevent focus on `Checkbox` when it is `disabled` ([#3251](https://github.com/tailwindlabs/headlessui/pull/3251))\n- Use `useId` instead of React internals (for React 19 compatibility) ([#3254](https://github.com/tailwindlabs/headlessui/pull/3254))\n- Cancel outside click behavior on touch devices when scrolling ([#3266](https://github.com/tailwindlabs/headlessui/pull/3266))\n- Correctly apply conditional classes when using `Transition` and `TransitionChild` components ([#3303](https://github.com/tailwindlabs/headlessui/pull/3303))\n\n### Changed\n\n- Allow using the `Tab` and `Shift+Tab` keys when the `Listbox` component is open ([#3284](https://github.com/tailwindlabs/headlessui/pull/3284))\n\n## [2.0.4] - 2024-05-25\n\n### Fixed\n\n- [internal] Don’t set a focus fallback for Dialog’s in demo mode ([#3194](https://github.com/tailwindlabs/headlessui/pull/3194))\n- Ensure page doesn't scroll down when pressing `Escape` to close the `Dialog` component ([#3218](https://github.com/tailwindlabs/headlessui/pull/3218))\n- Fix crash when toggling between `virtual` and non-virtual mode in `Combobox` component ([#3236](https://github.com/tailwindlabs/headlessui/pull/3236))\n- Ensure tabbing to a portalled `PopoverPanel` component moves focus inside (without using `PortalGroup`) ([#3239](https://github.com/tailwindlabs/headlessui/pull/3239))\n- Only handle form reset when `defaultValue` is used ([#3240](https://github.com/tailwindlabs/headlessui/pull/3240))\n\n### Deprecated\n\n- Mark `SwitchGroup` as deprecated, prefer `Field` instead ([#3232](https://github.com/tailwindlabs/headlessui/pull/3232))\n\n### Changed\n\n- Use native `fieldset` instead of `div` by default for `Fieldset` component ([#3237](https://github.com/tailwindlabs/headlessui/pull/3237))\n\n## [2.0.3] - 2024-05-07\n\n### Fixed\n\n- Make sure disabling demo mode on `Combobox` works ([#3182](hhttps://github.com/tailwindlabs/headlessui/pull/3182))\n\n## [2.0.2] - 2024-05-07\n\n### Fixed\n\n- Improve performance of internal `useInertOthers` hook ([#3181](https://github.com/tailwindlabs/headlessui/pull/3181))\n\n## [2.0.1] - 2024-05-06\n\n### Fixed\n\n- Remove accidental deprecation comments on `DialogPanel` and `DialogTitle` ([#3176](https://github.com/tailwindlabs/headlessui/pull/3176))\n\n## [2.0.0] - 2024-05-06\n\n### Added\n\n- Add new `Checkbox` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#2962](https://github.com/tailwindlabs/headlessui/pull/2962))\n- Add new `Button` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `Input` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#2902](https://github.com/tailwindlabs/headlessui/pull/2902), [#2940](https://github.com/tailwindlabs/headlessui/pull/2940))\n- Add new `Textarea` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#2902](https://github.com/tailwindlabs/headlessui/pull/2902), [#2940](https://github.com/tailwindlabs/headlessui/pull/2940))\n- Add new `Select` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#2902](https://github.com/tailwindlabs/headlessui/pull/2902))\n- Add new `Fieldset` and `Legend` components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `Field`, `Label`, and `Description` components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#2941](https://github.com/tailwindlabs/headlessui/pull/2941))\n- Add new `MenuSection`, `MenuHeading`, and `MenuSeparator` components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `ListboxSelectedOption` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `DataInteractive` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `CloseButton` component and `useClose` hook ([#3096](https://github.com/tailwindlabs/headlessui/pull/3096))\n- Add new `anchor`, `modal`, and `portal` props to `Combobox`, `Listbox`, `Menu` and `Popover` components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3075](https://github.com/tailwindlabs/headlessui/pull/3075), [#3097](https://github.com/tailwindlabs/headlessui/pull/3097), [#3111](https://github.com/tailwindlabs/headlessui/pull/3111), [#3115](https://github.com/tailwindlabs/headlessui/pull/3115), [#3121](https://github.com/tailwindlabs/headlessui/pull/3121), [#3124](https://github.com/tailwindlabs/headlessui/pull/3124), [#3133](https://github.com/tailwindlabs/headlessui/pull/3133), [#3138](https://github.com/tailwindlabs/headlessui/pull/3138), [#3148](https://github.com/tailwindlabs/headlessui/pull/3148), [#3152](https://github.com/tailwindlabs/headlessui/pull/3152), [#3154](https://github.com/tailwindlabs/headlessui/pull/3154), [#3157](https://github.com/tailwindlabs/headlessui/pull/3157))\n- Add new `autoFocus` prop to focusable components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Add new `virtual` prop to `Combobox` component ([#2779](https://github.com/tailwindlabs/headlessui/pull/2779), [#3128](https://github.com/tailwindlabs/headlessui/pull/3128), [#3160](https://github.com/tailwindlabs/headlessui/pull/3160), [#3161](https://github.com/tailwindlabs/headlessui/pull/3161), [#3163](https://github.com/tailwindlabs/headlessui/pull/3163))\n- Add new `onClose` prop to `Combobox` component ([#3122](https://github.com/tailwindlabs/headlessui/pull/3122))\n- Add new `immediate` prop to `Combobox` for immediately opening the Combobox when the `input` receives focus ([#2686](https://github.com/tailwindlabs/headlessui/pull/2686))\n- Add new `--button-width` CSS variable on the `ListboxOptions`, `MenuItems`, and `PopoverPanel` components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3058](https://github.com/tailwindlabs/headlessui/pull/3058))\n- Add new `--input-width` and `--button-width` CSS variables on the `ComboboxOptions` component ([#3057](https://github.com/tailwindlabs/headlessui/pull/3057))\n- Add new `data-*` attributes as an alternative to the existing `data-headlessui-state` attribute ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3035](https://github.com/tailwindlabs/headlessui/pull/3035), [#3061](https://github.com/tailwindlabs/headlessui/pull/3061))\n\n### Fixed\n\n- Fix scroll-locking on iOS ([#2891](https://github.com/tailwindlabs/headlessui/pull/2891))\n- Fix cancellation of events when using `disabled` or `aria-disabled` attributes ([#2890](https://github.com/tailwindlabs/headlessui/pull/2890))\n- Fix unnecessary execution of the `displayValue` callback in `ComboboxInput` component ([#3048](https://github.com/tailwindlabs/headlessui/pull/3048))\n- Fix types for `multiple` prop in `Combobox` component ([#3099](https://github.com/tailwindlabs/headlessui/pull/3099))\n- Fix focus handling in `ComboboxInput` component ([#3065](https://github.com/tailwindlabs/headlessui/pull/3065), [#3073](https://github.com/tailwindlabs/headlessui/pull/3073))\n- Fix enter transitions in `Transition` component ([#3074](https://github.com/tailwindlabs/headlessui/pull/3074))\n- Fix focus handling in `ListboxOptions` and `MenuItems` components ([#3112](https://github.com/tailwindlabs/headlessui/pull/3112))\n- Fix horizontal scrolling inside the `Dialog` component ([#2889](https://github.com/tailwindlabs/headlessui/pull/2889))\n- Don’t cancel `touchmove` on `input` elements inside a dialog ([#3166](https://github.com/tailwindlabs/headlessui/pull/3166))\n\n### Changed\n\n- Require React 18 ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3092](https://github.com/tailwindlabs/headlessui/pull/3092), [#3131](https://github.com/tailwindlabs/headlessui/pull/3131))\n- Always render hidden form input fields for `Checkbox`, `Switch`, and `RadioGroup` components ([#3095](https://github.com/tailwindlabs/headlessui/pull/3095))\n- Deprecate the `RadioGroup.Option` component in favor of new `Radio` component ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Deprecate the `active` prop in favor of new `focus` prop ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Dialog is now focused by default instead of the first focusable element ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887))\n- Change default tags for `ListboxOptions`, `ListboxOption`, `ComboboxOptions`, `ComboboxOption`, and `TabGroup` components ([#3109](https://github.com/tailwindlabs/headlessui/pull/3109))\n- Change default tag from `div` to `Fragment` on `Transition` components ([#3110](https://github.com/tailwindlabs/headlessui/pull/3110), [#3147](https://github.com/tailwindlabs/headlessui/pull/3147))\n- Allow `Combobox` component to have a `null` value ([#3064](https://github.com/tailwindlabs/headlessui/pull/3064), [#3100](https://github.com/tailwindlabs/headlessui/pull/3100))\n- Attempt form submission when pressing enter on the `ListboxButton` component ([#2972](https://github.com/tailwindlabs/headlessui/pull/2972))\n- Deprecate the `entered` prop on the `Transition` component ([#3089](https://github.com/tailwindlabs/headlessui/pull/3089))\n- Deprecate dot notation for components ([#2887](https://github.com/tailwindlabs/headlessui/pull/2887), [#3170](https://github.com/tailwindlabs/headlessui/pull/3170))\n- Add frozen value to `ComboboxOptions` component ([#3126](https://github.com/tailwindlabs/headlessui/pull/3126))\n- Remove deprecated `DialogBackdrop` and `DialogOverlay` components ([#3171](https://github.com/tailwindlabs/headlessui/pull/3171))\n\n## [1.7.19] - 2024-04-15\n\n### Fixed\n\n- Make sure panels re-register when IDs are calculated in React < 18 ([#2883](https://github.com/tailwindlabs/headlessui/pull/2883))\n- Expose `disabled` state on `Tab` component ([#2918](https://github.com/tailwindlabs/headlessui/pull/2918))\n- Prevent default behavior when clicking outside of a `Dialog.Panel` ([#2919](https://github.com/tailwindlabs/headlessui/pull/2919))\n- Add `hidden` attribute to internal `Hidden` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))\n- Allow setting custom `tabIndex` on the `Switch` component ([#2966](https://github.com/tailwindlabs/headlessui/pull/2966))\n- Forward `disabled` state to hidden inputs in form-like components ([#3004](https://github.com/tailwindlabs/headlessui/pull/3004))\n- Respect `selectedIndex` for controlled `Tab` components ([#3037](https://github.com/tailwindlabs/headlessui/pull/3037))\n\n## [2.0.0-alpha.4] - 2024-01-03\n\n### Fixed\n\n- Ensure `Input`, `Select` and `Textarea` expose `Ref` related types ([#2902](https://github.com/tailwindlabs/headlessui/pull/2902))\n\n## [2.0.0-alpha.3] - 2023-12-20\n\n### Fixed\n\n- Further fine tune scroll locking on iOS ([#2891](https://github.com/tailwindlabs/headlessui/pull/2891))\n\n## [2.0.0-alpha.2] - 2023-12-20\n\n### Fixed\n\n- Allow horizontal scrolling inside the `Dialog` component ([#2889](https://github.com/tailwindlabs/headlessui/pull/2889))\n- Improve cancellation of events when using `disabled` or `aria-disabled` attributes ([#2890](https://github.com/tailwindlabs/headlessui/pull/2890))\n\n## [2.0.0-alpha.1] - 2023-12-20\n\n### Added\n\n- Add `immediate` prop to `Combobox` for immediately opening the Combobox when the `input` receives focus ([#2686](https://github.com/tailwindlabs/headlessui/pull/2686))\n- Add `virtual` prop to `Combobox` component ([#2779](https://github.com/tailwindlabs/headlessui/pull/2779))\n- Add new `Checkbox` component\n- Add new `Radio` component as an alternative to the existing `RadioGroup.Option` component\n- Add new `Button` component\n- Add new `Input` component\n- Add new `Textarea` component\n- Add new `Select` component\n- Add new `Field`, `Label`, `Description`, `Fieldset` and `Legend` components\n- Add new `DataInteractive` component\n- Add new `anchor` and `modal` prop to `ComboboxOptions`, `ListboxOptions`, `MenuItems` and `PopoverPanel` components\n- Add new `ListboxSelectedOption` component\n- Add new `MenuSection`, `MenuHeading`, and `MenuSeparator` components\n- Add new simplified `data-*` attributes as an alternative to the existing `data-headlessui-state=\"...\"` attribute\n- Add `autoFocus` prop on focusable components (which maps to `data-autofocus`)\n\n### Changed\n\n- Bumped to React and React DOM 18\n- Dialog is focused by default instead of the first focusable element (unless an element exists with a `data-autofocus` in the dialog)\n\n## [1.7.18] - 2024-01-08\n\n### Fixed\n\n- Don't call `Dialog`'s `onClose` twice on mobile devices ([#2690](https://github.com/tailwindlabs/headlessui/pull/2690))\n- Lazily resolve default containers in `Dialog` ([#2697](https://github.com/tailwindlabs/headlessui/pull/2697))\n- Ensure hidden `Tab.Panel` components are hidden from the accessibility tree ([#2708](https://github.com/tailwindlabs/headlessui/pull/2708))\n- Add support for `role=\"alertdialog\"` to `Dialog` component ([#2709](https://github.com/tailwindlabs/headlessui/pull/2709))\n- Ensure blurring the `Combobox.Input` component closes the `Combobox` ([#2712](https://github.com/tailwindlabs/headlessui/pull/2712))\n- Allow changes to the `className` prop when the `Transition` component is currently not transitioning ([#2722](https://github.com/tailwindlabs/headlessui/pull/2722))\n- Export (internal-only) component interfaces for TypeScript compiler ([#2313](https://github.com/tailwindlabs/headlessui/pull/2313))\n- Fix infinite render-loop for `Disclosure.Panel` and `Popover.Panel` when `as={Fragment}` ([#2760](https://github.com/tailwindlabs/headlessui/pull/2760))\n- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))\n- Fix outside click detection when component is mounted in the Shadow DOM ([#2866](https://github.com/tailwindlabs/headlessui/pull/2866))\n- Fix CJS types ([#2880](https://github.com/tailwindlabs/headlessui/pull/2880))\n- Fix error when transition classes contain new lines ([#2871](https://github.com/tailwindlabs/headlessui/pull/2871))\n- Improve iOS locking ([7721aca](https://github.com/tailwindlabs/headlessui/commit/7721acaecea2008c2d7e8ab29cc8d45b70bb021e))\n\n## [1.7.17] - 2023-08-17\n\n### Fixed\n\n- Use correct value when resetting `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))\n- Render `MainTreeNode` in `Popover.Group` component only ([#2634](https://github.com/tailwindlabs/headlessui/pull/2634))\n- Disable smooth scrolling when opening/closing `Dialog` components on iOS ([#2635](https://github.com/tailwindlabs/headlessui/pull/2635))\n- Don't assume `Tab` components are available when setting the next index ([#2642](https://github.com/tailwindlabs/headlessui/pull/2642))\n- Fix incorrectly focused `Combobox.Input` component on page load ([#2654](https://github.com/tailwindlabs/headlessui/pull/2654))\n- Ensure `appear` works using the `Transition` component (even when used with SSR) ([#2646](https://github.com/tailwindlabs/headlessui/pull/2646))\n- Improve resetting values when using the `nullable` prop on the `Combobox` component ([#2660](https://github.com/tailwindlabs/headlessui/pull/2660))\n- Fix hydration of components inside `Suspense` ([#2663](https://github.com/tailwindlabs/headlessui/pull/2663))\n- Prevent scrolling when focusing a tab ([#2674](https://github.com/tailwindlabs/headlessui/pull/2674))\n\n## [1.7.16] - 2023-07-27\n\n### Fixed\n\n- Ensure the caret is in a consistent position when syncing the `Combobox.Input` value ([#2568](https://github.com/tailwindlabs/headlessui/pull/2568))\n- Improve \"outside click\" behavior in combination with 3rd party libraries ([#2572](https://github.com/tailwindlabs/headlessui/pull/2572))\n- Ensure IME works on Android devices ([#2580](https://github.com/tailwindlabs/headlessui/pull/2580))\n- Calculate `aria-expanded` purely based on the open/closed state ([#2610](https://github.com/tailwindlabs/headlessui/pull/2610))\n- Submit form on `Enter` even if no submit-like button was found ([#2613](https://github.com/tailwindlabs/headlessui/pull/2613))\n\n## [1.7.15] - 2023-06-01\n\n### Added\n\n- [internal] add demo mode to `Menu` and `Popover` components ([#2448](https://github.com/tailwindlabs/headlessui/pull/2448))\n\n### Fixed\n\n- Ensure `FocusTrap` is only active when the given `enabled` value is `true` ([#2456](https://github.com/tailwindlabs/headlessui/pull/2456))\n- Stop `<Transition appear>` from overwriting classes on re-render ([#2457](https://github.com/tailwindlabs/headlessui/pull/2457))\n- Improve control over `Menu` and `Listbox` options while searching ([#2471](https://github.com/tailwindlabs/headlessui/pull/2471))\n- Consider clicks inside iframes to be \"outside\" ([#2485](https://github.com/tailwindlabs/headlessui/pull/2485))\n- Ensure moving focus within a `Portal` component, does not close the `Popover` component ([#2492](https://github.com/tailwindlabs/headlessui/pull/2492))\n\n### Changed\n\n- Move `types` condition to the front ([#2469](https://github.com/tailwindlabs/headlessui/pull/2469))\n\n## [1.7.14] - 2023-04-12\n\n### Fixed\n\n- Fix focus styles showing up when using the mouse ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347))\n- Fix \"Can't perform a React state update on an unmounted component.\" when using the `Transition` component ([#2374](https://github.com/tailwindlabs/headlessui/pull/2374))\n- Add `FocusTrap` event listeners once document has loaded ([#2389](https://github.com/tailwindlabs/headlessui/pull/2389))\n- Fix `className` hydration for `<Transition appear>` ([#2390](https://github.com/tailwindlabs/headlessui/pull/2390))\n- Improve `Combobox` types to improve false positives ([#2411](https://github.com/tailwindlabs/headlessui/pull/2411))\n- Merge `className` correctly when it’s a function ([#2412](https://github.com/tailwindlabs/headlessui/pull/2412))\n- Correctly handle IME composition in `Combobox.Input` ([#2426](https://github.com/tailwindlabs/headlessui/pull/2426))\n\n### Added\n\n- Add `form` prop to form-like components such as `RadioGroup`, `Switch`, `Listbox`, and `Combobox` ([#2356](https://github.com/tailwindlabs/headlessui/pull/2356))\n\n## [1.7.13] - 2023-03-03\n\n### Fixed\n\n- Ensure `Transition` component completes if nothing is transitioning ([#2318](https://github.com/tailwindlabs/headlessui/pull/2318))\n- Enable native label behavior for `Switch` where possible ([#2265](https://github.com/tailwindlabs/headlessui/pull/2265))\n- Allow root containers from the `Dialog` component in the `FocusTrap` component ([#2322](https://github.com/tailwindlabs/headlessui/pull/2322))\n- Fix `XYZPropsWeControl` and cleanup internal TypeScript types ([#2329](https://github.com/tailwindlabs/headlessui/pull/2329))\n- Fix invalid warning when using multiple `Popover.Button` components inside a `Popover.Panel` ([#2333](https://github.com/tailwindlabs/headlessui/pull/2333))\n- Fix restore focus to buttons in Safari, when `Dialog` component closes ([#2326](https://github.com/tailwindlabs/headlessui/pull/2326))\n\n## [1.7.12] - 2023-02-24\n\n### Added\n\n- Add explicit props types for every component ([#2282](https://github.com/tailwindlabs/headlessui/pull/2282))\n\n### Fixed\n\n- Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290))\n- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293))\n- Make React types more compatible with other libraries ([#2282](https://github.com/tailwindlabs/headlessui/pull/2282))\n- Fix `Dialog` cleanup when the `Dialog` becomes hidden ([#2303](https://github.com/tailwindlabs/headlessui/pull/2303))\n\n## [1.7.11] - 2023-02-15\n\n### Fixed\n\n- Ensure we handle `null` values for the `dataRef` correctly ([#2258](https://github.com/tailwindlabs/headlessui/pull/2258))\n- Move `aria-multiselectable` to `[role=listbox]` in the `Combobox` component ([#2271](https://github.com/tailwindlabs/headlessui/pull/2271))\n- Re-focus `Combobox.Input` when a `Combobox.Option` is selected ([#2272](https://github.com/tailwindlabs/headlessui/pull/2272))\n- Ensure we reset the `activeOptionIndex` if the active option is unmounted ([#2274](https://github.com/tailwindlabs/headlessui/pull/2274))\n- Improve `Ref` type for forwarded `Switch`'s ref ([#2277](https://github.com/tailwindlabs/headlessui/pull/2277))\n- Start cleanup phase of the `Dialog` component when going into the `Closing` state ([#2264](https://github.com/tailwindlabs/headlessui/pull/2264))\n\n## [1.7.10] - 2023-02-06\n\n### Fixed\n\n- Revert \"Use the `import * as React from 'react'` pattern ([#2242](https://github.com/tailwindlabs/headlessui/pull/2242))\n\n## [1.7.9] - 2023-02-03\n\n### Fixed\n\n- Fix SSR tab hydration when using Strict Mode in development ([#2231](https://github.com/tailwindlabs/headlessui/pull/2231))\n- Don't break overflow when multiple dialogs are open at the same time ([#2215](https://github.com/tailwindlabs/headlessui/pull/2215))\n- Fix \"This `Suspense` boundary received an update before it finished hydrating\" error in the `Disclosure` component ([#2238](https://github.com/tailwindlabs/headlessui/pull/2238))\n- Use the `import * as React from 'react'` pattern ([#2242](https://github.com/tailwindlabs/headlessui/pull/2242))\n\n## [1.7.8] - 2023-01-27\n\n### Fixed\n\n- Fix SSR tab rendering on React 17 ([#2102](https://github.com/tailwindlabs/headlessui/pull/2102))\n- Fix arrow key handling in `Tab` (after DOM order changes) ([#2145](https://github.com/tailwindlabs/headlessui/pull/2145))\n- Fix false positive warning about using multiple `Popover.Button` components ([#2146](https://github.com/tailwindlabs/headlessui/pull/2146))\n- Fix `Tab` key with non focusable elements in `Popover.Panel` ([#2147](https://github.com/tailwindlabs/headlessui/pull/2147))\n- Fix false positive warning when using `Popover.Button` in React 17 ([#2163](https://github.com/tailwindlabs/headlessui/pull/2163))\n- Fix `failed to removeChild on Node` bug ([#2164](https://github.com/tailwindlabs/headlessui/pull/2164))\n- Don’t overwrite classes during SSR when rendering fragments ([#2173](https://github.com/tailwindlabs/headlessui/pull/2173))\n- Improve `Combobox` accessibility ([#2153](https://github.com/tailwindlabs/headlessui/pull/2153))\n- Fix crash when reading `headlessuiFocusGuard` of `relatedTarget` in the `FocusTrap` component ([#2203](https://github.com/tailwindlabs/headlessui/pull/2203))\n- Fix `FocusTrap` in `Dialog` when there is only 1 focusable element ([#2172](https://github.com/tailwindlabs/headlessui/pull/2172))\n- Improve `Tabs` wrapping around when controlling the component and overflowing the `selectedIndex` ([#2213](https://github.com/tailwindlabs/headlessui/pull/2213))\n- Fix `shadow-root` bug closing `Dialog` containers ([#2217](https://github.com/tailwindlabs/headlessui/pull/2217))\n\n### Added\n\n- Allow setting `tabIndex` on the `Tab.Panel` ([#2214](https://github.com/tailwindlabs/headlessui/pull/2214))\n\n## [1.7.7] - 2022-12-16\n\n### Fixed\n\n- Improve scroll restoration after `Dialog` closes ([b20e48dd](https://github.com/tailwindlabs/headlessui/commit/b20e48dde3c37867f3900be3d475f9ac2058b587))\n\n## [1.7.6] - 2022-12-15\n\n### Fixed\n\n- Fix regression where `displayValue` crashes ([#2087](https://github.com/tailwindlabs/headlessui/pull/2087))\n- Fix `displayValue` syncing when `Combobox.Input` is unmounted and re-mounted in different trees ([#2090](https://github.com/tailwindlabs/headlessui/pull/2090))\n- Fix FocusTrap escape due to strange tabindex values ([#2093](https://github.com/tailwindlabs/headlessui/pull/2093))\n- Improve scroll locking on iOS ([#2100](https://github.com/tailwindlabs/headlessui/pull/2100), [28234b0e](https://github.com/tailwindlabs/headlessui/commit/28234b0e37633c0e2ec62ac8bd12320207a5d02b))\n\n## [1.7.5] - 2022-12-08\n\n### Fixed\n\n- Reset form-like components when the parent `form` resets ([#2004](https://github.com/tailwindlabs/headlessui/pull/2004))\n- Add warning when using `Popover.Button` multiple times ([#2007](https://github.com/tailwindlabs/headlessui/pull/2007))\n- Ensure Popover doesn't crash when `focus` is going to `window` ([#2019](https://github.com/tailwindlabs/headlessui/pull/2019))\n- Ensure `shift+home` and `shift+end` works as expected in the `Combobox.Input` component ([#2024](https://github.com/tailwindlabs/headlessui/pull/2024))\n- Improve syncing of the `Combobox.Input` value ([#2042](https://github.com/tailwindlabs/headlessui/pull/2042))\n- Fix crash when using `multiple` mode without `value` prop (uncontrolled) for `Listbox` and `Combobox` components ([#2058](https://github.com/tailwindlabs/headlessui/pull/2058))\n- Apply `enter` and `enterFrom` classes in SSR for `Transition` component ([#2059](https://github.com/tailwindlabs/headlessui/pull/2059))\n- Allow passing in your own `id` prop ([#2060](https://github.com/tailwindlabs/headlessui/pull/2060))\n- Fix `Dialog` unmounting problem due to incorrect `transitioncancel` event in the `Transition` component on Android ([#2071](https://github.com/tailwindlabs/headlessui/pull/2071))\n- Ignore pointer events in Listbox, Menu, and Combobox when cursor hasn't moved ([#2069](https://github.com/tailwindlabs/headlessui/pull/2069))\n- Allow clicks inside dialog panel when target is inside shadow root ([#2079](https://github.com/tailwindlabs/headlessui/pull/2079))\n\n## [1.7.4] - 2022-11-03\n\n### Fixed\n\n- Fix `<Popover.Button as={Fragment} />` crash ([#1889](https://github.com/tailwindlabs/headlessui/pull/1889))\n- Expose `close` function for `Menu` and `Menu.Item` components ([#1897](https://github.com/tailwindlabs/headlessui/pull/1897))\n- Fix `useOutsideClick`, add improvements for ShadowDOM ([#1914](https://github.com/tailwindlabs/headlessui/pull/1914))\n- Fire `Combobox.Input`'s `onChange` handler when changing the value internally ([#1916](https://github.com/tailwindlabs/headlessui/pull/1916))\n- Add `client-only` to mark everything as client components ([#1981](https://github.com/tailwindlabs/headlessui/pull/1981))\n\n### Added\n\n- Warn when changing components between controlled and uncontrolled ([#1878](https://github.com/tailwindlabs/headlessui/issues/1878))\n\n## [1.7.3] - 2022-09-30\n\n### Fixed\n\n- Improve `Portal` detection for `Popover` components ([#1842](https://github.com/tailwindlabs/headlessui/pull/1842))\n- Fix `useOutsideClick` swallowing events inside ShadowDOM ([#1876](https://github.com/tailwindlabs/headlessui/pull/1876))\n- Fix `Tab` incorrectly activating on `focus` event ([#1887](https://github.com/tailwindlabs/headlessui/pull/1887))\n\n## [1.7.2] - 2022-09-15\n\n### Fixed\n\n- Prevent option selection in `Combobox.Input` while composing ([#1850](https://github.com/tailwindlabs/headlessui/issues/1850))\n- Ensure we handle the `static` prop in `Tab.Panel` components correctly ([#1856](https://github.com/tailwindlabs/headlessui/pull/1856))\n\n## [1.7.1] - 2022-09-12\n\n### Fixed\n\n- Improve iOS scroll locking ([#1830](https://github.com/tailwindlabs/headlessui/pull/1830))\n- Add `<fieldset disabled>` check to radio group options in React ([#1835](https://github.com/tailwindlabs/headlessui/pull/1835))\n- Ensure `Tab` order stays consistent, and the currently active `Tab` stays active ([#1837](https://github.com/tailwindlabs/headlessui/pull/1837))\n- Ensure `Combobox.Label` is properly linked when rendered after `Combobox.Button` and `Combobox.Input` components ([#1838](https://github.com/tailwindlabs/headlessui/pull/1838))\n- Remove `forceRerender` from `Tab` component ([#1846](https://github.com/tailwindlabs/headlessui/pull/1846))\n\n## [1.7.0] - 2022-09-06\n\n### Added\n\n- Add `by` prop for `Listbox`, `Combobox` and `RadioGroup` ([#1482](https://github.com/tailwindlabs/headlessui/pull/1482), [#1717](https://github.com/tailwindlabs/headlessui/pull/1717), [#1814](https://github.com/tailwindlabs/headlessui/pull/1814), [#1815](https://github.com/tailwindlabs/headlessui/pull/1815))\n- Make form components uncontrollable ([#1683](https://github.com/tailwindlabs/headlessui/pull/1683))\n- Add `@headlessui/tailwindcss` plugin ([#1487](https://github.com/tailwindlabs/headlessui/pull/1487))\n\n### Fixed\n\n- Fixed SSR support on Deno ([#1671](https://github.com/tailwindlabs/headlessui/pull/1671))\n- Don’t close dialog when opened during mouse up event ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Don’t close dialog when drag ends outside dialog ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Fix outside clicks to close dialog when nested, unopened dialogs are present ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Close `Menu` component when using `tab` key ([#1673](https://github.com/tailwindlabs/headlessui/pull/1673))\n- Resync input when display value changes ([#1679](https://github.com/tailwindlabs/headlessui/pull/1679), [#1755](https://github.com/tailwindlabs/headlessui/pull/1755))\n- Ensure controlled `Tabs` don't change automagically ([#1680](https://github.com/tailwindlabs/headlessui/pull/1680))\n- Don't scroll lock when a Transition + Dialog is mounted but hidden ([#1681](https://github.com/tailwindlabs/headlessui/pull/1681))\n- Allow `Popover` `close` to be passed directly to `onClick` handlers ([#1696](https://github.com/tailwindlabs/headlessui/pull/1696))\n- Improve outside click on Safari iOS ([#1712](https://github.com/tailwindlabs/headlessui/pull/1712))\n- Improve event handler merging ([#1715](https://github.com/tailwindlabs/headlessui/pull/1715))\n- Fix incorrect scrolling to the bottom when opening a `Dialog` ([#1716](https://github.com/tailwindlabs/headlessui/pull/1716))\n- Improve `Combobox` re-opening keyboard issue on mobile ([#1732](https://github.com/tailwindlabs/headlessui/pull/1732))\n- Ensure `Disclosure.Panel` is properly linked ([#1747](https://github.com/tailwindlabs/headlessui/pull/1747))\n- Only select the active option when using \"singular\" mode when pressing `tab` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750))\n- Improve the types of the `Combobox` component ([#1761](https://github.com/tailwindlabs/headlessui/pull/1761))\n- Only restore focus to the `Menu.Button` if necessary when activating a `Menu.Option` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782))\n- Don't scroll when wrapping around in focus trap ([#1789](https://github.com/tailwindlabs/headlessui/pull/1789))\n- Fix `Transition` component's incorrect cleanup and order of events ([#1803](https://github.com/tailwindlabs/headlessui/pull/1803))\n- Ensure enter transitions work when using `unmount={false}` ([#1811](https://github.com/tailwindlabs/headlessui/pull/1811))\n- Improve accessibility when announcing `Listbox.Option` and `Combobox.Option` components ([#1812](https://github.com/tailwindlabs/headlessui/pull/1812))\n- Fix `ref` stealing from children ([#1820](https://github.com/tailwindlabs/headlessui/pull/1820))\n- Expose the `value` from the `Combobox` and `Listbox` components render prop ([#1822](https://github.com/tailwindlabs/headlessui/pull/1822))\n- Improve `scroll lock` on iOS ([#1824](https://github.com/tailwindlabs/headlessui/pull/1824))\n- Fix maximum call stack size exceeded error on `Tab` component when using `as={Fragment}` ([#1826](https://github.com/tailwindlabs/headlessui/pull/1826))\n- Fix \"blank\" screen on initial load of `Transition` component ([#1823](https://github.com/tailwindlabs/headlessui/pull/1823))\n\n## [1.6.6] - 2022-07-07\n\n### Fixed\n\n- Ensure `CMD`+`Backspace` works in nullable mode for `Combobox` component ([#1617](https://github.com/tailwindlabs/headlessui/pull/1617))\n\n## [1.6.5] - 2022-06-20\n\n### Fixed\n\n- Fix incorrect transitionend/transitioncancel events for the Transition component ([#1537](https://github.com/tailwindlabs/headlessui/pull/1537))\n- Improve outside click of `Dialog` component ([#1546](https://github.com/tailwindlabs/headlessui/pull/1546))\n- Detect outside clicks from within `iframe` elements ([#1552](https://github.com/tailwindlabs/headlessui/pull/1552))\n- Improve Combobox input cursor position ([#1574](https://github.com/tailwindlabs/headlessui/pull/1574))\n- Fix scrolling issue in `Tab` component when using arrow keys ([#1584](https://github.com/tailwindlabs/headlessui/pull/1584))\n\n## [1.6.4] - 2022-05-29\n\n### Fixed\n\n- Ensure `Escape` propagates correctly in `Combobox` component ([#1511](https://github.com/tailwindlabs/headlessui/pull/1511))\n- Remove leftover code in Combobox component ([#1514](https://github.com/tailwindlabs/headlessui/pull/1514))\n- Fix event handlers with arity > 1 ([#1515](https://github.com/tailwindlabs/headlessui/pull/1515))\n- Fix transition `enter` bug ([#1519](https://github.com/tailwindlabs/headlessui/pull/1519))\n- Fix render prop data in `RadioGroup` component ([#1522](https://github.com/tailwindlabs/headlessui/pull/1522))\n\n## [1.6.3] - 2022-05-25\n\n### Fixed\n\n- Allow to override the `type` on the `Combobox.Input` ([#1476](https://github.com/tailwindlabs/headlessui/pull/1476))\n- Ensure the the `<Popover.Panel focus>` closes correctly ([#1477](https://github.com/tailwindlabs/headlessui/pull/1477))\n- Only render the `FocusSentinel` if required in the `Tabs` component ([#1493](https://github.com/tailwindlabs/headlessui/pull/1493))\n- Ensure the Transition stops once DOM Nodes are hidden ([#1500](https://github.com/tailwindlabs/headlessui/pull/1500))\n\n## [1.6.2] - 2022-05-19\n\n### Fixed\n\n- Fix closing of `Popover.Panel` in React 18 ([#1409](https://github.com/tailwindlabs/headlessui/pull/1409))\n- Ignore `Escape` when event got prevented in `Dialog` component ([#1424](https://github.com/tailwindlabs/headlessui/pull/1424))\n- Improve `FocusTrap` behavior ([#1432](https://github.com/tailwindlabs/headlessui/pull/1432))\n- Simplify `Popover` Tab logic by using sentinel nodes instead of keydown event interception ([#1440](https://github.com/tailwindlabs/headlessui/pull/1440))\n- Ensure the `Popover.Panel` is clickable without closing the `Popover` ([#1443](https://github.com/tailwindlabs/headlessui/pull/1443))\n- Improve \"Scroll lock\" scrollbar width for `Dialog` component ([#1457](https://github.com/tailwindlabs/headlessui/pull/1457))\n- Make the `ref` optional in the `Popover` component ([#1465](https://github.com/tailwindlabs/headlessui/pull/1465))\n- Ensure the `ref` is forwarded on the `Transition.Child` component ([#1473](https://github.com/tailwindlabs/headlessui/pull/1473))\n\n## [1.6.1] - 2022-05-03\n\n### Fixed\n\n- Fix hydration issue with `Tab` component ([#1393](https://github.com/tailwindlabs/headlessui/pull/1393))\n\n## [1.6.0] - 2022-04-25\n\n### Fixed\n\n- Ensure that you can add the `ref` prop to all components ([#1116](https://github.com/tailwindlabs/headlessui/pull/1116))\n- Ensure links are triggered inside `Popover.Panel` components ([#1153](https://github.com/tailwindlabs/headlessui/pull/1153))\n- Improve SSR for `Tab` component ([#1155](https://github.com/tailwindlabs/headlessui/pull/1155))\n- Fix `hover` scroll issue in `Listbox`, `Combobox` and `Menu` components ([#1161](https://github.com/tailwindlabs/headlessui/pull/1161))\n- Guarantee DOM sort order when performing `Listbox`, `Combobox` and `Menu` actions ([#1168](https://github.com/tailwindlabs/headlessui/pull/1168))\n- Fix `Transition` flickering issue ([#1118](https://github.com/tailwindlabs/headlessui/pull/1118))\n- Improve outside click support ([#1175](https://github.com/tailwindlabs/headlessui/pull/1175))\n- Ensure that `appear` prop on the `Transition` component works regardless of multiple rerenders ([#1179](https://github.com/tailwindlabs/headlessui/pull/1179))\n- Reset `Combobox.Input` when the value gets reset ([#1181](https://github.com/tailwindlabs/headlessui/pull/1181))\n- Fix double `beforeEnter` callback on the `Transition` component caused by SSR ([#1183](https://github.com/tailwindlabs/headlessui/pull/1183))\n- Adjust active `item`/`option` index on `Listbox`, `Combobox` and `Menu` components ([#1184](https://github.com/tailwindlabs/headlessui/pull/1184))\n- Only activate the `Tab` on mouseup ([#1192](https://github.com/tailwindlabs/headlessui/pull/1192))\n- Ignore \"outside click\" on removed elements ([#1193](https://github.com/tailwindlabs/headlessui/pull/1193))\n- Remove `focus()` from `Listbox.Option` ([#1218](https://github.com/tailwindlabs/headlessui/pull/1218))\n- Improve some internal code ([#1221](https://github.com/tailwindlabs/headlessui/pull/1221))\n- Use `ownerDocument` instead of `document` ([#1158](https://github.com/tailwindlabs/headlessui/pull/1158))\n- Ensure focus trapping plays well with the `Tab` and `Dialog` components ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231))\n- Improve syncing of `Combobox.Input` value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248))\n- Fix tree-shaking support ([#1247](https://github.com/tailwindlabs/headlessui/pull/1247))\n- Stop propagation on the `Popover.Button` ([#1263](https://github.com/tailwindlabs/headlessui/pull/1263))\n- Fix incorrect `active` option in the `Listbox` and `Combobox` components ([#1264](https://github.com/tailwindlabs/headlessui/pull/1264))\n- Properly merge incoming props ([#1265](https://github.com/tailwindlabs/headlessui/pull/1265))\n- Fix incorrect closing while interacting with third party libraries in `Dialog` component ([#1268](https://github.com/tailwindlabs/headlessui/pull/1268))\n- Mimic browser select on focus when navigating the `Tab` component ([#1272](https://github.com/tailwindlabs/headlessui/pull/1272))\n- 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))\n- Support classic form submissions in `RadioGroup`, `Switch` and `Combobox` components ([#1285](https://github.com/tailwindlabs/headlessui/pull/1285))\n- Add React 18 compatibility ([#1326](https://github.com/tailwindlabs/headlessui/pull/1326))\n- Fix open/closed state issue in `Dialog` ([#1360](https://github.com/tailwindlabs/headlessui/pull/1360))\n\n### Added\n\n- Add classic form submission compatibility via new hidden inputs ([#1214](https://github.com/tailwindlabs/headlessui/pull/1214))\n- Add multiple value support to `Listbox` and `Combobox` components ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243), [#1355](https://github.com/tailwindlabs/headlessui/pull/1355))\n- Add support for clearing the value of a `Combobox` ([#1295](https://github.com/tailwindlabs/headlessui/pull/1295))\n- Add `Dialog.Backdrop` and `Dialog.Panel` components ([#1333](https://github.com/tailwindlabs/headlessui/pull/1333))\n\n## [1.5.0] - 2022-02-17\n\n### Fixed\n\n- Ensure correct order when conditionally rendering `Menu.Item`, `Listbox.Option` and `RadioGroup.Option` ([#1045](https://github.com/tailwindlabs/headlessui/pull/1045))\n- Improve controlled Tabs behavior ([#1050](https://github.com/tailwindlabs/headlessui/pull/1050))\n- Improve typeahead search logic ([#1051](https://github.com/tailwindlabs/headlessui/pull/1051))\n- Improve overal codebase, use modern tech like `esbuild` and TypeScript 4! ([#1055](https://github.com/tailwindlabs/headlessui/pull/1055))\n- Improve build files ([#1078](https://github.com/tailwindlabs/headlessui/pull/1078))\n- Ensure typeahead stays on same item if it still matches ([#1098](https://github.com/tailwindlabs/headlessui/pull/1098))\n- Fix off-by-one frame issue causing flicker ([#1111](https://github.com/tailwindlabs/headlessui/pull/1111))\n- Trigger scrollIntoView effect when position changes ([#1113](https://github.com/tailwindlabs/headlessui/pull/1113))\n\n### Added\n\n- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104), [#1109](https://github.com/tailwindlabs/headlessui/pull/1109))\n\n## [1.4.3] - 2022-01-14\n\n### Fixes\n\n- Ensure portal root exists in the DOM ([#950](https://github.com/tailwindlabs/headlessui/pull/950))\n- Ensure correct DOM node order when performing focus actions ([#1038](https://github.com/tailwindlabs/headlessui/pull/1038))\n\n### Added\n\n- Allow for `Tab.Group` to be controllable ([#909](https://github.com/tailwindlabs/headlessui/pull/909), [#970](https://github.com/tailwindlabs/headlessui/pull/970))\n\n## [1.4.2] - 2021-11-08\n\n### Fixes\n\n- Stop the event from propagating in the `Popover` component ([#798](https://github.com/tailwindlabs/headlessui/pull/798))\n- Allow clicking on elements inside a `Dialog.Overlay` ([#816](https://github.com/tailwindlabs/headlessui/pull/816))\n- Ensure interactability with `Popover.Panel` contents when using the `static` prop ([#857](https://github.com/tailwindlabs/headlessui/pull/857))\n- Fix initial transition in `Transition` component ([#882](https://github.com/tailwindlabs/headlessui/pull/882))\n\n## [1.4.1] - 2021-08-30\n\n### Fixes\n\n- Only add `type=button` to real buttons ([#709](https://github.com/tailwindlabs/headlessui/pull/709))\n- Fix `escape` bug not closing Dialog after clicking in Dialog ([#754](https://github.com/tailwindlabs/headlessui/pull/754))\n- Use `console.warn` instead of throwing an error when there are no focusable elements ([#775](https://github.com/tailwindlabs/headlessui/pull/775))\n\n## [1.4.0] - 2021-07-29\n\n### Added\n\n- Add new `Tabs` component ([#674](https://github.com/tailwindlabs/headlessui/pull/674), [#698](https://github.com/tailwindlabs/headlessui/pull/698))\n- Make `Disclosure.Button` close the disclosure inside a `Disclosure.Panel` ([#682](https://github.com/tailwindlabs/headlessui/pull/682))\n- Add `aria-orientation` to `Listbox`, which swaps Up/Down with Left/Right keys ([#683](https://github.com/tailwindlabs/headlessui/pull/683))\n- Expose `close` function from the render prop for `Disclosure`, `Disclosure.Panel`, `Popover` and `Popover.Panel` ([#697](https://github.com/tailwindlabs/headlessui/pull/697))\n\n## [1.3.0] - 2021-06-21\n\n### Added\n\n- Ensure that you can use `Transition.Child` when using implicit Transitions ([#503](https://github.com/tailwindlabs/headlessui/pull/503))\n- Add new `entered` prop for `Transition` and `Transition.Child` components ([#504](https://github.com/tailwindlabs/headlessui/pull/504))\n\n### Fixes\n\n- Add `aria-disabled` on disabled `RadioGroup.Option` components ([#543](https://github.com/tailwindlabs/headlessui/pull/543))\n- Improve `disabled` and `tabindex` prop handling ([#512](https://github.com/tailwindlabs/headlessui/pull/512))\n- Improve React peer dependency version range ([#544](https://github.com/tailwindlabs/headlessui/pull/544))\n- Improve types for the `open` prop in the `Dialog` component ([#550](https://github.com/tailwindlabs/headlessui/pull/550))\n- Improve `aria-expanded` logic ([#592](https://github.com/tailwindlabs/headlessui/pull/592))\n- Remove undocumented `:className` prop ([#607](https://github.com/tailwindlabs/headlessui/pull/607))\n- Improve types for `Listbox` component ([#576](https://github.com/tailwindlabs/headlessui/pull/576))\n- Remove explicit `:class` prop ([#608](https://github.com/tailwindlabs/headlessui/pull/608))\n- Improve tree shaking ([#602](https://github.com/tailwindlabs/headlessui/pull/602))\n- Improve peer dependencies for `react-dom`, and for the future version `18` ([#622](https://github.com/tailwindlabs/headlessui/pull/622))\n\n## [1.2.0] - 2021-05-10\n\n### Added\n\n- Introduce Open/Closed state, to simplify component communication ([#466](https://github.com/tailwindlabs/headlessui/pull/466))\n\n### Fixes\n\n- Improve SSR for `Dialog` ([#477](https://github.com/tailwindlabs/headlessui/pull/477))\n- Delay focus trap initialization ([#477](https://github.com/tailwindlabs/headlessui/pull/477))\n- Improve incorrect behavior for nesting `Dialog` components ([#560](https://github.com/tailwindlabs/headlessui/pull/560))\n\n## [1.1.1] - 2021-04-28\n\n### Fixes\n\n- Fix form submission within Dialog ([#460](https://github.com/tailwindlabs/headlessui/pull/460))\n\n## [1.1.0] - 2021-04-26\n\n### Fixes\n\n- Improve search, make searching case insensitive ([#385](https://github.com/tailwindlabs/headlessui/pull/385))\n- Fix unreachable `RadioGroup` ([#401](https://github.com/tailwindlabs/headlessui/pull/401))\n- Fix closing nested `Dialog` components when pressing `Escape` ([#430](https://github.com/tailwindlabs/headlessui/pull/430))\n\n### Added\n\n- Add `disabled` prop to `RadioGroup` and `RadioGroup.Option` ([#401](https://github.com/tailwindlabs/headlessui/pull/401))\n- Add `defaultOpen` prop to the `Disclosure` component ([#447](https://github.com/tailwindlabs/headlessui/pull/447))\n\n## [1.0.0] - 2021-04-14\n\n### Fixes\n\n- Fixed `outside click` not re-focusing the `Menu.Button` ([#220](https://github.com/tailwindlabs/headlessui/pull/220), [#256](https://github.com/tailwindlabs/headlessui/pull/256))\n- Fixed `outside click` not re-focusing the `Listbox.Button` ([#220](https://github.com/tailwindlabs/headlessui/pull/220), [#256](https://github.com/tailwindlabs/headlessui/pull/256))\n- Force focus in `Menu.Items` and `Listbox.Options` from within the component itself ([#261](https://github.com/tailwindlabs/headlessui/pull/261))\n- Stop propagating keyboard/mouse events ([#261](https://github.com/tailwindlabs/headlessui/pull/261))\n\n### Added\n\n- Add `Disclosure`, `Disclosure.Button` and `Disclosure.Panel` components ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `Dialog`, `Dialog.Overlay`, `Dialog.Tile` and `Dialog.Description` components ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `Portal` and `Portal.Group` component ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `Switch.Description` component, which adds the `aria-describedby` to the actual Switch ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `FocusTrap` component ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `Popover`, `Popover.Button`, `Popover.Overlay`, `Popover.Panel` and `Popover.Group` components ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- All components that accept a `className`, can now also receive a function with the renderProp argument ([#257](https://github.com/tailwindlabs/headlessui/pull/257))\n- Add `RadioGroup`, `RadioGroup.Option`, `RadioGroup.Label` and `RadioGroup.Description` components ([#274](https://github.com/tailwindlabs/headlessui/pull/274))\n\n## [0.3.2] - 2021-04-02\n\n### Fixes\n\n- Fix incorrect type error `unique symbol` ([#248](https://github.com/tailwindlabs/headlessui/pull/248), [#240](https://github.com/tailwindlabs/headlessui/issues/240))\n\n## [0.3.1] - 2021-02-11\n\n### Fixes\n\n- Fix incorrect `types` path ([d557d50](https://github.com/tailwindlabs/headlessui/commit/d557d5013968f7bb9877f190b682318704043905))\n- Fix TypeScript render related types ([bb68793](https://github.com/tailwindlabs/headlessui/commit/bb68793f08a57833095a38519b639a744076dc69))\n\n## [0.3.0] - 2021-02-06\n\n### Fixes\n\n- Ensure that you can't use Enter to invoke the Switch\n- Fix outside click refocus bug ([#114](https://github.com/tailwindlabs/headlessui/pull/114))\n- Prevent scrolling when refocusing items\n- Ensure `Switch` has `type=\"button\"` ([#192](https://github.com/tailwindlabs/headlessui/pull/192))\n- Fix `useId()` hook returning `undefined` on the client\n- Fix `disabled` not working when inside a disabled fieldset ([#202](https://github.com/tailwindlabs/headlessui/pull/202))\n- Trigger \"outside click\" behavior on mousedown ([#212](https://github.com/tailwindlabs/headlessui/pull/212))\n- Ensure the `active` MenuItem is scrolled into view\n- Ensure valid Menu accessibility tree ([#228](https://github.com/tailwindlabs/headlessui/pull/228))\n\n### Added\n\n- Add Transition events (`beforeEnter`, `afterEnter`, `beforeLeave` and `afterLeave`) ([#57](https://github.com/tailwindlabs/headlessui/pull/57))\n- Add render features + render strategy (`static` and `unmount={true | false}`) ([#106](https://github.com/tailwindlabs/headlessui/pull/106))\n- Add displayName to all contexts ([#175](https://github.com/tailwindlabs/headlessui/pull/175))\n- Add `disabled` prop to `Listbox` itself, instead of the `Listbox.Button` ([#229](https://github.com/tailwindlabs/headlessui/pull/229))\n\n### Changes\n\n- Changes the API of the Transition component.\n  - We will now always render a `div` by default (unless you change this using the `as={...}` prop).\n  - The render function prop doesn't expose a `ref` anymore.\n  - Adds `unmount` prop to the `Transition` and `Transition.Child` components.\n\n## [0.2.0] - 2020-10-06\n\n### Added\n\n- Add `Listbox` component\n- Add `Switch` component\n\n## [0.1.3] - 2020-09-29\n\n### Fixes\n\n- Fix outside click behavior. If you had multiple menu's, when menu 1 is open, menu 2 is closed and you click on menu button 2 it will open both menu's. This is now fixed.\n- Ensure when using keyboard navigation we prevent the default behavior.\n\n## [0.1.2] - 2020-09-25\n\n### Added\n\n- Add tests for `onClick` handling that wasn't working properly in @headlessui/vue to ensure behavior stays the same in this library\n\n### Fixes\n\n- Don't pass `disabled` prop through to children, only add `aria-disabled`\n\n## [0.1.1] - 2020-09-24\n\n### Added\n\n- Everything!\n\n[unreleased]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.9...HEAD\n[2.2.9]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.8...@headlessui/react@v2.2.9\n[2.2.8]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.7...@headlessui/react@v2.2.8\n[2.2.7]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.6...@headlessui/react@v2.2.7\n[2.2.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.5...@headlessui/react@v2.2.6\n[2.2.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.4...@headlessui/react@v2.2.5\n[2.2.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.3...@headlessui/react@v2.2.4\n[2.2.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.2...@headlessui/react@v2.2.3\n[2.2.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.1...@headlessui/react@v2.2.2\n[2.2.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.2.0...@headlessui/react@v2.2.1\n[2.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.10...@headlessui/react@v2.2.0\n[2.1.10]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.9...@headlessui/react@v2.1.10\n[2.1.9]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.8...@headlessui/react@v2.1.9\n[2.1.8]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.7...@headlessui/react@v2.1.8\n[2.1.7]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.6...@headlessui/react@v2.1.7\n[2.1.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.5...@headlessui/react@v2.1.6\n[2.1.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.4...@headlessui/react@v2.1.5\n[2.1.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.3...@headlessui/react@v2.1.4\n[2.1.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.2...@headlessui/react@v2.1.3\n[2.1.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.1...@headlessui/react@v2.1.2\n[2.1.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.1.0...@headlessui/react@v2.1.1\n[2.1.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.4...@headlessui/react@v2.1.0\n[2.0.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.3...@headlessui/react@v2.0.4\n[2.0.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.2...@headlessui/react@v2.0.3\n[2.0.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.1...@headlessui/react@v2.0.2\n[2.0.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.0...@headlessui/react@v2.0.1\n[2.0.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.0-alpha.4...@headlessui/react@v2.0.0\n[1.7.19]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.18...v1.7.19\n[2.0.0-alpha.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.0-alpha.3...v2.0.0-alpha.4\n[2.0.0-alpha.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.0-alpha.2...v2.0.0-alpha.3\n[2.0.0-alpha.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v2.0.0-alpha.1...v2.0.0-alpha.2\n[2.0.0-alpha.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.18...v2.0.0-alpha.1\n[1.7.18]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.17...v1.7.18\n[1.7.17]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.16...v1.7.17\n[1.7.16]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.15...@headlessui/react@v1.7.16\n[1.7.15]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.14...@headlessui/react@v1.7.15\n[1.7.14]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.13...@headlessui/react@v1.7.14\n[1.7.13]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.12...@headlessui/react@v1.7.13\n[1.7.12]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.11...@headlessui/react@v1.7.12\n[1.7.11]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.10...@headlessui/react@v1.7.11\n[1.7.10]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.9...@headlessui/react@v1.7.10\n[1.7.9]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.8...@headlessui/react@v1.7.9\n[1.7.8]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.7...@headlessui/react@v1.7.8\n[1.7.7]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.6...@headlessui/react@v1.7.7\n[1.7.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.5...@headlessui/react@v1.7.6\n[1.7.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.4...@headlessui/react@v1.7.5\n[1.7.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.3...@headlessui/react@v1.7.4\n[1.7.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.2...@headlessui/react@v1.7.3\n[1.7.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.1...@headlessui/react@v1.7.2\n[1.7.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.0...@headlessui/react@v1.7.1\n[1.7.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.6...@headlessui/react@v1.7.0\n[1.6.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.5...@headlessui/react@v1.6.6\n[1.6.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.4...@headlessui/react@v1.6.5\n[1.6.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.3...@headlessui/react@v1.6.4\n[1.6.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.2...@headlessui/react@v1.6.3\n[1.6.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.1...@headlessui/react@v1.6.2\n[1.6.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.6.0...@headlessui/react@v1.6.1\n[1.6.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.5.0...@headlessui/react@v1.6.0\n[1.5.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.4.3...@headlessui/react@v1.5.0\n[1.4.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.4.2...@headlessui/react@v1.4.3\n[1.4.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.4.1...@headlessui/react@v1.4.2\n[1.4.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.4.0...@headlessui/react@v1.4.1\n[1.4.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.3.0...@headlessui/react@v1.4.0\n[1.3.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.2.0...@headlessui/react@v1.3.0\n[1.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.1.1...@headlessui/react@v1.2.0\n[1.1.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.1.0...@headlessui/react@v1.1.1\n[1.1.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.0.0...@headlessui/react@v1.1.0\n[1.0.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.3.2...@headlessui/react@v1.0.0\n[0.3.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.3.1...@headlessui/react@v0.3.2\n[0.3.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.3.0...@headlessui/react@v0.3.1\n[0.3.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.2.0...@headlessui/react@v0.3.0\n[0.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.1.3...@headlessui/react@v0.2.0\n[0.1.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.1.2...@headlessui/react@v0.1.3\n[0.1.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v0.1.1...@headlessui/react@v0.1.2\n[0.1.1]: https://github.com/tailwindlabs/headlessui/releases/tag/@headlessui/react@v0.1.1\n"
  },
  {
    "path": "packages/@headlessui-react/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Tailwind Labs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "packages/@headlessui-react/README.md",
    "content": "<h3 align=\"center\">\n  @headlessui/react\n</h3>\n\n<p align=\"center\">\n  A set of completely unstyled, fully accessible UI components for React, designed to integrate\n  beautifully with Tailwind CSS.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@headlessui/react\"><img src=\"https://img.shields.io/npm/dt/@headlessui/react.svg\" alt=\"Total Downloads\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/releases\"><img src=\"https://img.shields.io/npm/v/@headlessui/react.svg\" alt=\"Latest Release\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/blob/main/LICENSE\"><img src=\"https://img.shields.io/npm/l/@headlessui/react.svg\" alt=\"License\"></a>\n</p>\n\n## Installation\n\n```sh\nnpm install @headlessui/react\n```\n\n## Documentation\n\nFor full documentation, visit [headlessui.dev](https://headlessui.dev/react/menu).\n\n## Community\n\nFor help, discussion about best practices, or feature ideas:\n\n[Discuss Headless UI on GitHub](https://github.com/tailwindlabs/headlessui/discussions)\n"
  },
  {
    "path": "packages/@headlessui-react/build/index.cjs",
    "content": "'use strict'\n\nif (process.env.NODE_ENV === 'production') {\n  module.exports = require('./headlessui.prod.cjs')\n} else {\n  module.exports = require('./headlessui.dev.cjs')\n}\n"
  },
  {
    "path": "packages/@headlessui-react/jest.config.cjs",
    "content": "let create = require('../../jest/create-jest-config.cjs')\nmodule.exports = create(__dirname, {\n  displayName: 'React',\n  setupFilesAfterEnv: ['./jest.setup.js'],\n})\n"
  },
  {
    "path": "packages/@headlessui-react/jest.setup.js",
    "content": "globalThis.IS_REACT_ACT_ENVIRONMENT = true\n"
  },
  {
    "path": "packages/@headlessui-react/package.json",
    "content": "{\n  \"name\": \"@headlessui/react\",\n  \"version\": \"2.2.9\",\n  \"description\": \"A set of completely unstyled, fully accessible UI components for React, designed to integrate beautifully with Tailwind CSS.\",\n  \"main\": \"dist/index.cjs\",\n  \"typings\": \"dist/index.d.ts\",\n  \"module\": \"dist/headlessui.esm.js\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"README.md\",\n    \"dist\"\n  ],\n  \"exports\": {\n    \"types\": {\n      \"import\": \"./dist/index.d.ts\",\n      \"require\": \"./dist/index.d.cts\"\n    },\n    \"import\": \"./dist/headlessui.esm.js\",\n    \"require\": \"./dist/index.cjs\"\n  },\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"engines\": {\n    \"node\": \">=10\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/tailwindlabs/headlessui.git\",\n    \"directory\": \"packages/@headlessui-react\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"prepublishOnly\": \"npm run build\",\n    \"build\": \"../../scripts/build.sh --external:react --external:react-dom --external:use-sync-external-store\",\n    \"watch\": \"../../scripts/watch.sh --external:react --external:react-dom --external:use-sync-external-store\",\n    \"test\": \"../../scripts/test.sh\",\n    \"lint\": \"../../scripts/lint.sh\",\n    \"lint-types\": \"npm run attw -P --workspaces --if-present\",\n    \"playground\": \"npm run dev --workspace=playground-react\",\n    \"clean\": \"rimraf ./dist\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^18 || ^19 || ^19.0.0-rc\",\n    \"react-dom\": \"^18 || ^19 || ^19.0.0-rc\"\n  },\n  \"devDependencies\": {\n    \"@testing-library/react\": \"^15.0.7\",\n    \"@types/react\": \"^18.3.3\",\n    \"@types/react-dom\": \"^18.3.0\",\n    \"@types/use-sync-external-store\": \"^1.5.0\",\n    \"jsdom-testing-mocks\": \"^1.13.1\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"snapshot-diff\": \"^0.10.0\"\n  },\n  \"dependencies\": {\n    \"@floating-ui/react\": \"^0.26.16\",\n    \"@react-aria/focus\": \"^3.20.2\",\n    \"@react-aria/interactions\": \"^3.25.0\",\n    \"@tanstack/react-virtual\": \"^3.13.9\",\n    \"use-sync-external-store\": \"^1.5.0\"\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/button/button.test.tsx",
    "content": "import { render, screen } from '@testing-library/react'\nimport React, { Fragment } from 'react'\nimport { Button } from './button'\n\ndescribe('Rendering', () => {\n  describe('Button', () => {\n    it('should render a button', async () => {\n      render(<Button>My Button</Button>)\n\n      expect(screen.getByRole('button')).toBeInTheDocument()\n    })\n\n    it('should default to `type=\"button\"`', async () => {\n      render(<Button>My Button</Button>)\n\n      expect(screen.getByRole('button')).toHaveAttribute('type', 'button')\n    })\n\n    it('should render a button using a render prop', () => {\n      render(<Button>{(slot) => <>{JSON.stringify(slot)}</>}</Button>)\n\n      expect(screen.getByRole('button').textContent).toEqual(\n        JSON.stringify({\n          disabled: false,\n          hover: false,\n          focus: false,\n          active: false,\n          autofocus: false,\n        })\n      )\n    })\n\n    it('should map the `autoFocus` prop to a `data-autofocus` attribute', () => {\n      render(<Button autoFocus>My Button</Button>)\n\n      expect(screen.getByRole('button')).toHaveAttribute('data-autofocus')\n    })\n\n    it('should be possible to render a Button using as={Fragment}', async () => {\n      render(\n        <Button as={Fragment}>\n          <button>Toggle</button>\n        </Button>\n      )\n\n      expect(screen.getByRole('button')).toHaveAttribute('type')\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/button/button.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { type ElementType, type Ref } from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useDisabled } from '../../internal/disabled'\nimport type { Props } from '../../types'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\n\ntype ButtonRenderPropArg = {\n  disabled: boolean\n  hover: boolean\n  focus: boolean\n  active: boolean\n  autofocus: boolean\n}\ntype ButtonPropsWeControl = never\n\nexport type ButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    disabled?: boolean\n    autoFocus?: boolean\n    type?: 'button' | 'submit' | 'reset'\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: ButtonProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let providedDisabled = useDisabled()\n  let { disabled = providedDisabled || false, autoFocus = false, ...theirProps } = props\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let ourProps = mergeProps(\n    {\n      ref,\n      type: theirProps.type ?? 'button',\n      disabled: disabled || undefined,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let slot = useSlot<ButtonRenderPropArg>({ disabled, hover, focus, active, autofocus: autoFocus })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BUTTON_TAG,\n    name: 'Button',\n  })\n}\n\nexport interface _internal_ComponentButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: ButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport let Button = forwardRefWithAs(ButtonFn) as _internal_ComponentButton\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/checkbox/checkbox.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { useState } from 'react'\nimport {\n  CheckboxState,\n  assertCheckbox,\n  getCheckbox,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, click, focus, press } from '../../test-utils/interactions'\nimport {\n  commonControlScenarios,\n  commonFormScenarios,\n  commonRenderingScenarios,\n} from '../../test-utils/scenarios'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Checkbox, type CheckboxProps } from './checkbox'\n\ncommonRenderingScenarios(Checkbox, { getElement: getCheckbox })\ncommonControlScenarios(Checkbox)\ncommonFormScenarios((props) => <Checkbox defaultChecked {...props} />, {\n  async performUserInteraction(control) {\n    await click(control)\n  },\n})\n\ndescribe('Rendering', () => {\n  it(\n    'should be possible to put the checkbox in an indeterminate state',\n    suppressConsoleLogs(async () => {\n      render(<Checkbox indeterminate />)\n\n      assertCheckbox({ state: CheckboxState.Indeterminate })\n    })\n  )\n\n  it(\n    'should be possible to put the checkbox in an default checked state',\n    suppressConsoleLogs(async () => {\n      render(<Checkbox defaultChecked />)\n\n      assertCheckbox({ state: CheckboxState.Checked })\n    })\n  )\n\n  it(\n    'should render a checkbox in an unchecked state',\n    suppressConsoleLogs(async () => {\n      render(<Checkbox />)\n\n      assertCheckbox({ state: CheckboxState.Unchecked })\n    })\n  )\n})\n\ndescribe.each([\n  [\n    'Uncontrolled',\n    function Example(props: CheckboxProps) {\n      return <Checkbox {...props} />\n    },\n  ],\n  [\n    'Controlled',\n    function Example(props: CheckboxProps) {\n      let [checked, setChecked] = useState(false)\n      return <Checkbox checked={checked} onChange={setChecked} {...props} />\n    },\n  ],\n])('Keyboard interactions (%s)', (_, Example) => {\n  describe('`Space` key', () => {\n    it(\n      'should be possible to toggle a checkbox',\n      suppressConsoleLogs(async () => {\n        render(<Example />)\n\n        assertCheckbox({ state: CheckboxState.Unchecked })\n\n        await focus(getCheckbox())\n        await press(Keys.Space)\n\n        assertCheckbox({ state: CheckboxState.Checked })\n\n        await press(Keys.Space)\n\n        assertCheckbox({ state: CheckboxState.Unchecked })\n      })\n    )\n  })\n})\n\ndescribe.each([\n  [\n    'Uncontrolled',\n    function Example(props: CheckboxProps) {\n      return <Checkbox {...props} />\n    },\n  ],\n  [\n    'Controlled',\n    function Example(props: CheckboxProps) {\n      let [checked, setChecked] = useState(false)\n      return <Checkbox checked={checked} onChange={setChecked} {...props} />\n    },\n  ],\n])('Mouse interactions (%s)', (_, Example) => {\n  it(\n    'should be possible to toggle a checkbox by clicking it',\n    suppressConsoleLogs(async () => {\n      render(<Example />)\n\n      assertCheckbox({ state: CheckboxState.Unchecked })\n\n      await click(getCheckbox())\n\n      assertCheckbox({ state: CheckboxState.Checked })\n\n      await click(getCheckbox())\n\n      assertCheckbox({ state: CheckboxState.Unchecked })\n    })\n  )\n})\n\ndescribe('Form submissions', () => {\n  it('should be possible to use in an uncontrolled way', async () => {\n    let handleSubmission = jest.fn()\n\n    render(\n      <form\n        onSubmit={(e) => {\n          e.preventDefault()\n          handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n        }}\n      >\n        <Checkbox name=\"notifications\" />\n      </form>\n    )\n\n    let checkbox = document.querySelector('[id^=\"headlessui-checkbox-\"]') as HTMLInputElement\n\n    // Focus the checkbox\n    await focus(checkbox)\n\n    // Submit\n    await press(Keys.Enter)\n\n    // No values\n    expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n    // Toggle\n    await click(checkbox)\n\n    // Submit\n    await press(Keys.Enter)\n\n    // Notifications should be on\n    expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })\n\n    // Toggle\n    await click(checkbox)\n\n    // Submit\n    await press(Keys.Enter)\n\n    // Notifications should be off (or in this case, non-existent)\n    expect(handleSubmission).toHaveBeenLastCalledWith({})\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/checkbox/checkbox.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  useCallback,\n  useState,\n  type ElementType,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useDefaultValue } from '../../hooks/use-default-value'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useDisabled } from '../../internal/disabled'\nimport { FormFields } from '../../internal/form-fields'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport { isDisabledReactIssue7711 } from '../../utils/bugs'\nimport { attemptSubmit } from '../../utils/form'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { Keys } from '../keyboard'\nimport { useLabelledBy } from '../label/label'\n\nlet DEFAULT_CHECKBOX_TAG = 'span' as const\ntype CheckboxRenderPropArg = {\n  checked: boolean\n  changing: boolean\n  focus: boolean\n  active: boolean\n  hover: boolean\n  autofocus: boolean\n  disabled: boolean\n  indeterminate: boolean\n}\ntype CheckboxPropsWeControl =\n  | 'aria-checked'\n  | 'aria-describedby'\n  | 'aria-disabled'\n  | 'aria-labelledby'\n  | 'role'\n\nexport type CheckboxProps<\n  TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG,\n  TType = string,\n> = Props<\n  TTag,\n  CheckboxRenderPropArg,\n  CheckboxPropsWeControl,\n  {\n    value?: TType\n    disabled?: boolean\n    indeterminate?: boolean\n\n    checked?: boolean\n    defaultChecked?: boolean\n    autoFocus?: boolean\n    form?: string\n    name?: string\n    onChange?: (checked: boolean) => void\n    tabIndex?: number\n  }\n>\n\nfunction CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TType = any>(\n  props: CheckboxProps<TTag, TType>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-checkbox-${internalId}`,\n    disabled = providedDisabled || false,\n    autoFocus = false,\n    checked: controlledChecked,\n    defaultChecked: _defaultChecked,\n    onChange: controlledOnChange,\n    name,\n    value,\n    form,\n    indeterminate = false,\n    tabIndex = 0,\n    ...theirProps\n  } = props\n\n  let defaultChecked = useDefaultValue(_defaultChecked)\n  let [checked, onChange] = useControllable(\n    controlledChecked,\n    controlledOnChange,\n    defaultChecked ?? false\n  )\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let d = useDisposables()\n  let [changing, setChanging] = useState(false)\n  let toggle = useEvent(() => {\n    setChanging(true)\n    onChange?.(!checked)\n\n    d.nextFrame(() => {\n      setChanging(false)\n    })\n  })\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()\n    event.preventDefault()\n    toggle()\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    if (event.key === Keys.Space) {\n      event.preventDefault()\n      toggle()\n    } else if (event.key === Keys.Enter) {\n      attemptSubmit(event.currentTarget)\n    }\n  })\n\n  // This is needed so that we can \"cancel\" the click event when we use the `Enter` key on a button.\n  let handleKeyPress = useEvent((event: ReactKeyboardEvent<HTMLElement>) => event.preventDefault())\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let ourProps = mergeProps(\n    {\n      ref,\n      id,\n      role: 'checkbox',\n      'aria-checked': indeterminate ? 'mixed' : checked ? 'true' : 'false',\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      'aria-disabled': disabled ? true : undefined,\n      indeterminate: indeterminate ? 'true' : undefined,\n      tabIndex: disabled ? undefined : tabIndex,\n      onKeyUp: disabled ? undefined : handleKeyUp,\n      onKeyPress: disabled ? undefined : handleKeyPress,\n      onClick: disabled ? undefined : handleClick,\n    },\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let slot = useSlot<CheckboxRenderPropArg>({\n    checked,\n    disabled,\n    hover,\n    focus,\n    active,\n    indeterminate,\n    changing,\n    autofocus: autoFocus,\n  })\n\n  let reset = useCallback(() => {\n    if (defaultChecked === undefined) return\n    return onChange?.(defaultChecked)\n  }, [onChange, defaultChecked])\n\n  let render = useRender()\n\n  return (\n    <>\n      {name != null && (\n        <FormFields\n          disabled={disabled}\n          data={{ [name]: value || 'on' }}\n          overrides={{ type: 'checkbox', checked }}\n          form={form}\n          onReset={reset}\n        />\n      )}\n      {render({\n        ourProps,\n        theirProps,\n        slot,\n        defaultTag: DEFAULT_CHECKBOX_TAG,\n        name: 'Checkbox',\n      })}\n    </>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentCheckbox extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TType = string>(\n    props: CheckboxProps<TTag, TType> & RefProp<typeof CheckboxFn>\n  ): React.JSX.Element\n}\n\nexport let Checkbox = forwardRefWithAs(CheckboxFn) as _internal_ComponentCheckbox\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/close-button/close-button.tsx",
    "content": "'use client'\n\nimport React, { type ElementType, type Ref } from 'react'\nimport { useClose } from '../../internal/close-provider'\nimport { forwardRefWithAs, mergeProps } from '../../utils/render'\nimport { Button, type ButtonProps, type _internal_ComponentButton } from '../button/button'\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\n\nexport type CloseButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> =\n  ButtonProps<TTag>\n\nfunction CloseButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: ButtonProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let close = useClose()\n  return <Button ref={ref} {...mergeProps({ onClick: close }, props)} />\n}\n\nexport let CloseButton = forwardRefWithAs(CloseButtonFn) as _internal_ComponentButton\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox/combobox-machine-glue.tsx",
    "content": "import { createContext, useContext, useMemo } from 'react'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { ComboboxMachine } from './combobox-machine'\n\nexport const ComboboxContext = createContext<ComboboxMachine<unknown> | null>(null)\nexport function useComboboxMachineContext<T>(component: string) {\n  let context = useContext(ComboboxContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Combobox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useComboboxMachine)\n    throw err\n  }\n  return context as ComboboxMachine<T>\n}\n\nexport function useComboboxMachine({\n  id,\n  virtual = null,\n  __demoMode = false,\n}: Parameters<typeof ComboboxMachine.new>[0]) {\n  let machine = useMemo(() => ComboboxMachine.new({ id, virtual, __demoMode }), [])\n  useOnUnmount(() => machine.dispose())\n  return machine\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox/combobox-machine.ts",
    "content": "import { Machine } from '../../machine'\nimport { ActionTypes as StackActionTypes, stackMachines } from '../../machines/stack-machine'\nimport type { EnsureArray } from '../../types'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport {\n  ElementPositionState,\n  computeVisualPosition,\n  detectMovement,\n} from '../../utils/element-movement'\nimport { sortByDomNode } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\n\ninterface MutableRefObject<T> {\n  current: T\n}\n\nexport enum ComboboxState {\n  Open,\n  Closed,\n}\n\nexport enum ValueMode {\n  Single,\n  Multi,\n}\n\nexport enum ActivationTrigger {\n  Pointer,\n  Focus,\n  Other,\n}\n\nexport type ComboboxOptionDataRef<T> = MutableRefObject<{\n  disabled: boolean\n  value: T\n  domRef: MutableRefObject<HTMLElement | null>\n  order: number | null\n}>\n\nexport interface State<T> {\n  id: string\n\n  dataRef: MutableRefObject<{\n    value: unknown\n    defaultValue: unknown\n    disabled: boolean\n    invalid: boolean\n    mode: ValueMode\n    immediate: boolean\n    onChange: (value: T) => void\n    onClose?: () => void\n    compare(a: unknown, z: unknown): boolean\n    isSelected(value: unknown): boolean\n\n    virtual: { options: T[]; disabled: (value: T) => boolean } | null\n    calculateIndex(value: unknown): number\n\n    __demoMode: boolean\n\n    optionsPropsRef: MutableRefObject<{\n      static: boolean\n      hold: boolean\n    }>\n  }>\n\n  virtual: { options: T[]; disabled: (value: unknown) => boolean } | null\n\n  comboboxState: ComboboxState\n\n  defaultToFirstOption: boolean\n\n  options: { id: string; dataRef: ComboboxOptionDataRef<T> }[]\n  activeOptionIndex: number | null\n  activationTrigger: ActivationTrigger\n\n  isTyping: boolean\n\n  inputElement: HTMLInputElement | null\n  buttonElement: HTMLButtonElement | null\n  optionsElement: HTMLElement | null\n\n  // Track input to determine if it moved\n  inputPositionState: ElementPositionState\n\n  __demoMode: boolean\n}\n\nexport enum ActionTypes {\n  OpenCombobox,\n  CloseCombobox,\n\n  GoToOption,\n  SetTyping,\n\n  RegisterOption,\n  UnregisterOption,\n\n  DefaultToFirstOption,\n\n  SetActivationTrigger,\n\n  UpdateVirtualConfiguration,\n\n  SetInputElement,\n  SetButtonElement,\n  SetOptionsElement,\n\n  MarkInputAsMoved,\n}\n\nfunction adjustOrderedState<T>(\n  state: State<T>,\n  adjustment: (options: State<T>['options']) => State<T>['options'] = (i) => i\n) {\n  let currentActiveOption =\n    state.activeOptionIndex !== null ? state.options[state.activeOptionIndex] : null\n\n  let list = adjustment(state.options.slice())\n  let sortedOptions =\n    list.length > 0 && list[0].dataRef.current.order !== null\n      ? // Prefer sorting based on the `order`\n        list.sort((a, z) => a.dataRef.current.order! - z.dataRef.current.order!)\n      : // Fallback to much slower DOM order\n        sortByDomNode(list, (option) => option.dataRef.current.domRef.current)\n\n  // If we inserted an option before the current active option then the active option index\n  // would be wrong. To fix this, we will re-lookup the correct index.\n  let adjustedActiveOptionIndex = currentActiveOption\n    ? sortedOptions.indexOf(currentActiveOption)\n    : null\n\n  // Reset to `null` in case the currentActiveOption was removed.\n  if (adjustedActiveOptionIndex === -1) {\n    adjustedActiveOptionIndex = null\n  }\n\n  return {\n    options: sortedOptions,\n    activeOptionIndex: adjustedActiveOptionIndex,\n  }\n}\n\ntype Actions<T> =\n  | { type: ActionTypes.CloseCombobox }\n  | { type: ActionTypes.OpenCombobox }\n  | {\n      type: ActionTypes.GoToOption\n      focus: Focus.Specific\n      idx: number\n      trigger?: ActivationTrigger\n    }\n  | { type: ActionTypes.SetTyping; isTyping: boolean }\n  | {\n      type: ActionTypes.GoToOption\n      focus: Exclude<Focus, Focus.Specific>\n      trigger?: ActivationTrigger\n    }\n  | {\n      type: ActionTypes.RegisterOption\n      payload: { id: string; dataRef: ComboboxOptionDataRef<T> }\n    }\n  | { type: ActionTypes.UnregisterOption; id: string }\n  | { type: ActionTypes.DefaultToFirstOption; value: boolean }\n  | { type: ActionTypes.SetActivationTrigger; trigger: ActivationTrigger }\n  | {\n      type: ActionTypes.UpdateVirtualConfiguration\n      options: T[]\n      disabled: ((value: any) => boolean) | null\n    }\n  | { type: ActionTypes.SetInputElement; element: HTMLInputElement | null }\n  | { type: ActionTypes.SetButtonElement; element: HTMLButtonElement | null }\n  | { type: ActionTypes.SetOptionsElement; element: HTMLElement | null }\n  | { type: ActionTypes.MarkInputAsMoved }\n\nlet reducers: {\n  [P in ActionTypes]: <T>(state: State<T>, action: Extract<Actions<T>, { type: P }>) => State<T>\n} = {\n  [ActionTypes.CloseCombobox](state) {\n    if (state.dataRef.current?.disabled) return state\n    if (state.comboboxState === ComboboxState.Closed) return state\n    let inputPositionState = state.inputElement\n      ? ElementPositionState.Tracked(computeVisualPosition(state.inputElement))\n      : state.inputPositionState\n\n    return {\n      ...state,\n      activeOptionIndex: null,\n      comboboxState: ComboboxState.Closed,\n\n      isTyping: false,\n\n      // Clear the last known activation trigger\n      // This is because if a user interacts with the combobox using a mouse\n      // resulting in it closing we might incorrectly handle the next interaction\n      // for example, not scrolling to the active option in a virtual list\n      activationTrigger: ActivationTrigger.Other,\n\n      inputPositionState,\n\n      __demoMode: false,\n    }\n  },\n  [ActionTypes.OpenCombobox](state) {\n    if (state.dataRef.current?.disabled) return state\n    if (state.comboboxState === ComboboxState.Open) return state\n\n    // Check if we have a selected value that we can make active\n    if (state.dataRef.current?.value) {\n      let idx = state.dataRef.current.calculateIndex(state.dataRef.current.value)\n      if (idx !== -1) {\n        return {\n          ...state,\n          activeOptionIndex: idx,\n          comboboxState: ComboboxState.Open,\n          __demoMode: false,\n          inputPositionState: ElementPositionState.Idle,\n        }\n      }\n    }\n\n    return {\n      ...state,\n      comboboxState: ComboboxState.Open,\n      inputPositionState: ElementPositionState.Idle,\n      __demoMode: false,\n    }\n  },\n  [ActionTypes.SetTyping](state, action) {\n    if (state.isTyping === action.isTyping) return state\n    return { ...state, isTyping: action.isTyping }\n  },\n  [ActionTypes.GoToOption](state, action) {\n    if (state.dataRef.current?.disabled) return state\n    if (\n      state.optionsElement &&\n      !state.dataRef.current?.optionsPropsRef.current.static &&\n      state.comboboxState === ComboboxState.Closed\n    ) {\n      return state\n    }\n\n    if (state.virtual) {\n      let { options, disabled } = state.virtual\n      let activeOptionIndex =\n        action.focus === Focus.Specific\n          ? action.idx\n          : calculateActiveIndex(action, {\n              resolveItems: () => options,\n              resolveActiveIndex: () =>\n                state.activeOptionIndex ?? options.findIndex((option) => !disabled(option)) ?? null,\n              resolveDisabled: disabled,\n              resolveId() {\n                throw new Error('Function not implemented.')\n              },\n            })\n\n      let activationTrigger = action.trigger ?? ActivationTrigger.Other\n\n      if (\n        state.activeOptionIndex === activeOptionIndex &&\n        state.activationTrigger === activationTrigger\n      ) {\n        return state\n      }\n\n      return {\n        ...state,\n        activeOptionIndex,\n        activationTrigger,\n        isTyping: false,\n        __demoMode: false,\n      }\n    }\n\n    let adjustedState = adjustOrderedState(state)\n\n    // It's possible that the activeOptionIndex is set to `null` internally, but\n    // this means that we will fallback to the first non-disabled option by default.\n    // We have to take this into account.\n    if (adjustedState.activeOptionIndex === null) {\n      let localActiveOptionIndex = adjustedState.options.findIndex(\n        (option) => !option.dataRef.current.disabled\n      )\n\n      if (localActiveOptionIndex !== -1) {\n        adjustedState.activeOptionIndex = localActiveOptionIndex\n      }\n    }\n\n    let activeOptionIndex =\n      action.focus === Focus.Specific\n        ? action.idx\n        : calculateActiveIndex(action, {\n            resolveItems: () => adjustedState.options,\n            resolveActiveIndex: () => adjustedState.activeOptionIndex,\n            resolveId: (item) => item.id,\n            resolveDisabled: (item) => item.dataRef.current.disabled,\n          })\n    let activationTrigger = action.trigger ?? ActivationTrigger.Other\n\n    if (\n      state.activeOptionIndex === activeOptionIndex &&\n      state.activationTrigger === activationTrigger\n    ) {\n      return state\n    }\n\n    return {\n      ...state,\n      ...adjustedState,\n      isTyping: false,\n      activeOptionIndex,\n      activationTrigger,\n      __demoMode: false,\n    }\n  },\n  [ActionTypes.RegisterOption]: (state, action) => {\n    if (state.dataRef.current?.virtual) {\n      return {\n        ...state,\n        options: [...state.options, action.payload],\n      }\n    }\n\n    let option = action.payload\n\n    let adjustedState = adjustOrderedState(state, (options) => {\n      options.push(option)\n      return options\n    })\n\n    // Check if we need to make the newly registered option active.\n    if (state.activeOptionIndex === null) {\n      if (state.dataRef.current.isSelected?.(action.payload.dataRef.current.value)) {\n        adjustedState.activeOptionIndex = adjustedState.options.indexOf(option)\n      }\n    }\n\n    let nextState = {\n      ...state,\n      ...adjustedState,\n      activationTrigger: ActivationTrigger.Other,\n    }\n\n    if (state.dataRef.current?.__demoMode && state.dataRef.current.value === undefined) {\n      nextState.activeOptionIndex = 0\n    }\n\n    return nextState\n  },\n  [ActionTypes.UnregisterOption]: (state, action) => {\n    if (state.dataRef.current?.virtual) {\n      return {\n        ...state,\n        options: state.options.filter((option) => option.id !== action.id),\n      }\n    }\n\n    let adjustedState = adjustOrderedState(state, (options) => {\n      let idx = options.findIndex((option) => option.id === action.id)\n      if (idx !== -1) options.splice(idx, 1)\n      return options\n    })\n\n    return {\n      ...state,\n      ...adjustedState,\n      activationTrigger: ActivationTrigger.Other,\n    }\n  },\n  [ActionTypes.DefaultToFirstOption]: (state, action) => {\n    if (state.defaultToFirstOption === action.value) return state\n\n    return {\n      ...state,\n      defaultToFirstOption: action.value,\n    }\n  },\n  [ActionTypes.SetActivationTrigger]: (state, action) => {\n    if (state.activationTrigger === action.trigger) {\n      return state\n    }\n\n    return {\n      ...state,\n      activationTrigger: action.trigger,\n    }\n  },\n  [ActionTypes.UpdateVirtualConfiguration]: (state, action) => {\n    if (state.virtual === null) {\n      return {\n        ...state,\n        virtual: { options: action.options, disabled: action.disabled ?? (() => false) },\n      }\n    }\n\n    if (state.virtual.options === action.options && state.virtual.disabled === action.disabled) {\n      return state\n    }\n\n    let adjustedActiveOptionIndex = state.activeOptionIndex\n    if (state.activeOptionIndex !== null) {\n      let idx = action.options.indexOf(state.virtual.options[state.activeOptionIndex])\n      if (idx !== -1) {\n        adjustedActiveOptionIndex = idx\n      } else {\n        adjustedActiveOptionIndex = null\n      }\n    }\n\n    return {\n      ...state,\n      activeOptionIndex: adjustedActiveOptionIndex,\n      virtual: { options: action.options, disabled: action.disabled ?? (() => false) },\n    }\n  },\n  [ActionTypes.SetInputElement]: (state, action) => {\n    if (state.inputElement === action.element) return state\n    return { ...state, inputElement: action.element }\n  },\n  [ActionTypes.SetButtonElement]: (state, action) => {\n    if (state.buttonElement === action.element) return state\n    return { ...state, buttonElement: action.element }\n  },\n  [ActionTypes.SetOptionsElement]: (state, action) => {\n    if (state.optionsElement === action.element) return state\n    return { ...state, optionsElement: action.element }\n  },\n  [ActionTypes.MarkInputAsMoved](state) {\n    if (state.inputPositionState.kind !== 'Tracked') return state\n\n    return {\n      ...state,\n      inputPositionState: ElementPositionState.Moved,\n    }\n  },\n}\n\nexport class ComboboxMachine<T> extends Machine<State<T>, Actions<T>> {\n  static new<T, TMultiple extends boolean | undefined>({\n    id,\n    virtual = null,\n    __demoMode = false,\n  }: {\n    id: string\n    virtual?: {\n      options: TMultiple extends true ? EnsureArray<NoInfer<T>> : NoInfer<T>[]\n      disabled?: (\n        value: TMultiple extends true ? EnsureArray<NoInfer<T>>[number] : NoInfer<T>\n      ) => boolean\n    } | null\n    __demoMode?: boolean\n  }) {\n    return new ComboboxMachine({\n      id,\n      // @ts-expect-error TODO: Re-structure such that we don't need to ignore this\n      dataRef: { current: {} },\n      comboboxState: __demoMode ? ComboboxState.Open : ComboboxState.Closed,\n      isTyping: false,\n      options: [],\n      // @ts-expect-error TODO: Ensure we use the correct type\n      virtual: virtual\n        ? { options: virtual.options, disabled: virtual.disabled ?? (() => false) }\n        : null,\n      activeOptionIndex: null,\n      activationTrigger: ActivationTrigger.Other,\n      inputElement: null,\n      buttonElement: null,\n      optionsElement: null,\n      __demoMode,\n      inputPositionState: ElementPositionState.Idle,\n    })\n  }\n\n  constructor(initialState: State<T>) {\n    super(initialState)\n\n    // When the combobox is open, and it's not on the top of the hierarchy, we\n    // should close it again.\n    {\n      let id = this.state.id\n      let stackMachine = stackMachines.get(null)\n\n      this.disposables.add(\n        stackMachine.on(StackActionTypes.Push, (state) => {\n          if (\n            !stackMachine.selectors.isTop(state, id) &&\n            this.state.comboboxState === ComboboxState.Open\n          ) {\n            this.actions.closeCombobox()\n          }\n        })\n      )\n\n      this.on(ActionTypes.OpenCombobox, () => stackMachine.actions.push(id))\n      this.on(ActionTypes.CloseCombobox, () => stackMachine.actions.pop(id))\n    }\n\n    // Track whether the input moved or not\n    this.disposables.group((d) => {\n      this.on(ActionTypes.CloseCombobox, (state) => {\n        if (!state.inputElement) return\n\n        d.dispose()\n        d.add(\n          detectMovement(state.inputElement, state.inputPositionState, () => {\n            this.send({ type: ActionTypes.MarkInputAsMoved })\n          })\n        )\n      })\n    })\n  }\n\n  actions = {\n    onChange: (newValue: T) => {\n      let { onChange, compare, mode, value } = this.state.dataRef.current\n\n      return match(mode, {\n        [ValueMode.Single]: () => {\n          return onChange?.(newValue)\n        },\n        [ValueMode.Multi]: () => {\n          let copy = (value as T[]).slice()\n\n          let idx = copy.findIndex((item) => compare(item, newValue))\n          if (idx === -1) {\n            copy.push(newValue)\n          } else {\n            copy.splice(idx, 1)\n          }\n\n          return onChange?.(copy as T)\n        },\n      })\n    },\n    registerOption: (id: string, dataRef: ComboboxOptionDataRef<T>) => {\n      this.send({ type: ActionTypes.RegisterOption, payload: { id, dataRef } })\n      return () => {\n        // When we are unregistering the currently active option, then we also have to make sure to\n        // reset the `defaultToFirstOption` flag, so that visually something is selected and the next\n        // time you press a key on your keyboard it will go to the proper next or previous option in\n        // the list.\n        //\n        // Since this was the active option and it could have been anywhere in the list, resetting to\n        // the very first option seems like a fine default. We _could_ be smarter about this by going\n        // to the previous / next item in list if we know the direction of the keyboard navigation,\n        // but that might be too complex/confusing from an end users perspective.\n        if (\n          this.state.activeOptionIndex ===\n          this.state.dataRef.current.calculateIndex(dataRef.current.value)\n        ) {\n          this.send({ type: ActionTypes.DefaultToFirstOption, value: true })\n        }\n\n        this.send({ type: ActionTypes.UnregisterOption, id })\n      }\n    },\n    goToOption: (\n      focus: { focus: Focus.Specific; idx: number } | { focus: Exclude<Focus, Focus.Specific> },\n      trigger?: ActivationTrigger\n    ) => {\n      this.send({ type: ActionTypes.DefaultToFirstOption, value: false })\n      return this.send({ type: ActionTypes.GoToOption, ...focus, trigger })\n    },\n    setIsTyping: (isTyping: boolean) => {\n      this.send({ type: ActionTypes.SetTyping, isTyping })\n    },\n    closeCombobox: () => {\n      this.send({ type: ActionTypes.CloseCombobox })\n      this.send({ type: ActionTypes.DefaultToFirstOption, value: false })\n      this.state.dataRef.current.onClose?.()\n    },\n    openCombobox: () => {\n      this.send({ type: ActionTypes.OpenCombobox })\n      this.send({ type: ActionTypes.DefaultToFirstOption, value: true })\n    },\n    setActivationTrigger: (trigger: ActivationTrigger) => {\n      this.send({ type: ActionTypes.SetActivationTrigger, trigger })\n    },\n    selectActiveOption: () => {\n      let activeOptionIndex = this.selectors.activeOptionIndex(this.state)\n      if (activeOptionIndex === null) return\n\n      this.actions.setIsTyping(false)\n\n      if (this.state.virtual) {\n        this.actions.onChange(this.state.virtual.options[activeOptionIndex])\n      } else {\n        let { dataRef } = this.state.options[activeOptionIndex]\n        this.actions.onChange(dataRef.current.value)\n      }\n\n      // It could happen that the `activeOptionIndex` stored in state is actually null, but we are\n      // getting the fallback active option back instead.\n      this.actions.goToOption({ focus: Focus.Specific, idx: activeOptionIndex })\n    },\n    setInputElement: (element: HTMLInputElement | null) => {\n      this.send({ type: ActionTypes.SetInputElement, element })\n    },\n    setButtonElement: (element: HTMLButtonElement | null) => {\n      this.send({ type: ActionTypes.SetButtonElement, element })\n    },\n    setOptionsElement: (element: HTMLElement | null) => {\n      this.send({ type: ActionTypes.SetOptionsElement, element })\n    },\n  }\n\n  selectors = {\n    activeDescendantId: (state: State<T>) => {\n      let activeOptionIndex = this.selectors.activeOptionIndex(state)\n      if (activeOptionIndex === null) {\n        return undefined\n      }\n\n      if (!state.virtual) {\n        return state.options[activeOptionIndex]?.id\n      }\n\n      return state.options.find((option) => {\n        return (\n          !option.dataRef.current.disabled &&\n          state.dataRef.current.compare(\n            option.dataRef.current.value,\n            state.virtual!.options[activeOptionIndex]\n          )\n        )\n      })?.id\n    },\n\n    activeOptionIndex: (state: State<T>) => {\n      if (\n        state.defaultToFirstOption &&\n        state.activeOptionIndex === null &&\n        (state.virtual ? state.virtual.options.length > 0 : state.options.length > 0)\n      ) {\n        if (state.virtual) {\n          let { options, disabled } = state.virtual\n          let activeOptionIndex = options.findIndex((option) => !(disabled?.(option) ?? false))\n\n          if (activeOptionIndex !== -1) {\n            return activeOptionIndex\n          }\n        }\n\n        let activeOptionIndex = state.options.findIndex((option) => {\n          return !option.dataRef.current.disabled\n        })\n\n        if (activeOptionIndex !== -1) {\n          return activeOptionIndex\n        }\n      }\n\n      return state.activeOptionIndex\n    },\n\n    activeOption: (state: State<T>) => {\n      let activeOptionIndex = this.selectors.activeOptionIndex(state)\n      return activeOptionIndex === null\n        ? null\n        : state.virtual\n          ? state.virtual.options[activeOptionIndex ?? 0]\n          : state.options[activeOptionIndex]?.dataRef.current.value ?? null\n    },\n\n    isActive: (state: State<T>, value: T, id: string) => {\n      let activeOptionIndex = this.selectors.activeOptionIndex(state)\n      if (activeOptionIndex === null) return false\n\n      if (state.virtual) {\n        return activeOptionIndex === state.dataRef.current.calculateIndex(value)\n      }\n\n      return state.options[activeOptionIndex]?.id === id\n    },\n\n    shouldScrollIntoView: (state: State<T>, value: T, id: string): boolean => {\n      if (state.virtual) return false\n      if (state.__demoMode) return false\n      if (state.comboboxState !== ComboboxState.Open) return false\n      if (state.activationTrigger === ActivationTrigger.Pointer) return false\n\n      let active = this.selectors.isActive(state, value, id)\n      if (!active) return false\n\n      return true\n    },\n\n    didInputMove(state: State<T>) {\n      return state.inputPositionState.kind === 'Moved'\n    },\n  }\n\n  reduce(state: Readonly<State<T>>, action: Actions<T>): State<T> {\n    return match(action.type, reducers, state, action) as State<T>\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox/combobox.test.tsx",
    "content": "import { render, waitFor } from '@testing-library/react'\nimport React, { Fragment, createElement, useEffect, useState } from 'react'\nimport {\n  ComboboxMode,\n  ComboboxState,\n  assertActiveComboboxOption,\n  assertActiveElement,\n  assertCombobox,\n  assertComboboxButton,\n  assertComboboxButtonLinkedWithCombobox,\n  assertComboboxButtonLinkedWithComboboxLabel,\n  assertComboboxInput,\n  assertComboboxLabel,\n  assertComboboxLabelLinkedWithCombobox,\n  assertComboboxList,\n  assertComboboxOption,\n  assertNoActiveComboboxOption,\n  assertNoSelectedComboboxOption,\n  assertNotActiveComboboxOption,\n  getByText,\n  getComboboxButton,\n  getComboboxButtons,\n  getComboboxInput,\n  getComboboxInputs,\n  getComboboxLabel,\n  getComboboxOptions,\n  getComboboxes,\n} from '../../test-utils/accessibility-assertions'\nimport {\n  Keys,\n  MouseButton,\n  blur,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  rawClick,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { mockingConsoleLogs, suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Transition } from '../transition/transition'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxOption,\n  ComboboxOptions,\n} from './combobox'\n\nlet NOOP = () => {}\n\n// Mocking the `getBoundingClientRect` method for the virtual tests otherwise\n// the `Virtualizer` from `@tanstack/react-virtual` will not work as expected\n// because it couldn't measure the elements correctly.\njest.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(() => ({\n  width: 120,\n  height: 40,\n  top: 0,\n  left: 0,\n  bottom: 0,\n  right: 0,\n  x: 0,\n  y: 0,\n  toJSON: () => {},\n}))\n\njest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockReturnValue(100)\njest.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(40)\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\ndescribe('safeguards', () => {\n  it.each([\n    ['Combobox.Button', Combobox.Button],\n    ['Combobox.Label', Combobox.Label],\n    ['Combobox.Options', Combobox.Options],\n    ['Combobox.Option', Combobox.Option],\n  ])(\n    'should error when we are using a <%s /> without a parent <Combobox />',\n    suppressConsoleLogs((name, Component) => {\n      if (name === 'Combobox.Label') {\n        // @ts-expect-error This is fine\n        expect(() => render(createElement(Component))).toThrow(\n          'You used a <Label /> component, but it is not inside a relevant parent.'\n        )\n      } else {\n        // @ts-expect-error This is fine\n        expect(() => render(createElement(Component))).toThrow(\n          `<${name} /> is missing a parent <Combobox /> component.`\n        )\n      }\n    })\n  )\n\n  it(\n    'should be possible to render a Combobox without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options>\n            <Combobox.Option value=\"a\">Option A</Combobox.Option>\n            <Combobox.Option value=\"b\">Option B</Combobox.Option>\n            <Combobox.Option value=\"c\">Option C</Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Combobox', () => {\n    it(\n      'should be possible to render a Combobox using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            {({ open }) => (\n              <>\n                <Combobox.Input onChange={NOOP} />\n                <Combobox.Button>Trigger</Combobox.Button>\n                {open && (\n                  <Combobox.Options>\n                    <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                    <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                    <Combobox.Option value=\"c\">Option C</Combobox.Option>\n                  </Combobox.Options>\n                )}\n              </>\n            )}\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.Visible })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to disable a Combobox',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value={undefined} onChange={(x) => console.log(x)} disabled>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await press(Keys.Enter, getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        // The input should also be disabled\n        assertComboboxInput({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { disabled: '' },\n        })\n\n        // And even if we try to focus it, it should not open the combobox\n        await focus(getComboboxInput())\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not crash in multiple mode',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox multiple name=\"abc\">\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value={{ id: 1, name: 'alice' }}>alice</Combobox.Option>\n              <Combobox.Option value={{ id: 2, name: 'bob' }}>bob</Combobox.Option>\n              <Combobox.Option value={{ id: 3, name: 'charlie' }}>charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        await click(getComboboxButton())\n        let [alice, bob, charlie] = getComboboxOptions()\n\n        await click(alice)\n        await click(bob)\n        await click(charlie)\n      })\n    )\n\n    describe('Equality', () => {\n      let options = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n\n      it(\n        'should use object equality by default',\n        suppressConsoleLogs(async () => {\n          render(\n            <Combobox value={options[1]} onChange={(x) => console.log(x)}>\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                {options.map((option) => (\n                  <Combobox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({ active: true, focus: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare null values by a field',\n        suppressConsoleLogs(async () => {\n          render(\n            <Combobox value={null} onChange={(x) => console.log(x)} by=\"id\">\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                {options.map((option) => (\n                  <Combobox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n\n          await click(getComboboxButton())\n\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: false,\n              disabled: false,\n            })\n          )\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: false,\n              focus: false,\n              selected: false,\n              disabled: false,\n            })\n          )\n          expect(charlie).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: false,\n              focus: false,\n              selected: false,\n              disabled: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a field',\n        suppressConsoleLogs(async () => {\n          render(\n            <Combobox value={{ id: 2, name: 'Bob' }} onChange={(x) => console.log(x)} by=\"id\">\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                {options.map((option) => (\n                  <Combobox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: true,\n              disabled: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a comparator function',\n        suppressConsoleLogs(async () => {\n          render(\n            <Combobox\n              value={{ id: 2, name: 'Bob' }}\n              onChange={(x) => console.log(x)}\n              by={(a, z) => a.id === z.id}\n            >\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                {options.map((option) => (\n                  <Combobox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: true,\n              disabled: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (single mode)',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState<{ id: number; name: string } | null>({\n              id: 2,\n              name: 'Bob',\n            })\n\n            return (\n              <Combobox value={value} onChange={setValue} by=\"id\">\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options>\n                  <Combobox.Option value={{ id: 1, name: 'alice' }}>alice</Combobox.Option>\n                  <Combobox.Option value={{ id: 2, name: 'bob' }}>bob</Combobox.Option>\n                  <Combobox.Option value={{ id: 3, name: 'charlie' }}>charlie</Combobox.Option>\n                </Combobox.Options>\n              </Combobox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getComboboxButton())\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getComboboxOptions()[2])\n          await click(getComboboxButton())\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getComboboxOptions()[1])\n          await click(getComboboxButton())\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (multiple mode)',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState([{ id: 2, name: 'Bob' }])\n\n            return (\n              <Combobox value={value} onChange={setValue} by=\"id\" multiple>\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options>\n                  <Combobox.Option value={{ id: 1, name: 'alice' }}>alice</Combobox.Option>\n                  <Combobox.Option value={{ id: 2, name: 'bob' }}>bob</Combobox.Option>\n                  <Combobox.Option value={{ id: 3, name: 'charlie' }}>charlie</Combobox.Option>\n                </Combobox.Options>\n              </Combobox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getComboboxButton())\n\n          await click(getComboboxOptions()[2])\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getComboboxOptions()[2])\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n    })\n\n    it(\n      'should not crash when a defaultValue is not given',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        render(\n          <Combobox<(typeof data)[number]> name=\"assignee\" by=\"id\">\n            <Combobox.Input\n              displayValue={(value: { name: string }) => value.name}\n              onChange={NOOP}\n            />\n            <Combobox.Options>\n              {data.map((person) => (\n                <Combobox.Option key={person.id} value={person}>\n                  {person.label}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n        )\n      })\n    )\n\n    it(\n      'should keep the defaultValue when the Combobox state changes',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        function Example() {\n          let [person, setPerson] = useState<(typeof data)[number] | null>(data[1])\n\n          return (\n            <Combobox value={person} onChange={setPerson} name=\"assignee\" by=\"id\">\n              <Combobox.Input displayValue={() => String(Math.random())} />\n              <Combobox.Button />\n              <Combobox.Options>\n                {data.map((person) => (\n                  <Combobox.Option key={person.id} value={person}>\n                    {person.label}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        let value = getComboboxInput()?.value\n\n        // Toggle the state a few times combobox\n        await click(getComboboxButton())\n        await click(getComboboxButton())\n        await click(getComboboxButton())\n\n        // Verify the value is still the same\n        expect(getComboboxInput()?.value).toBe(value)\n\n        // Choose an option, which should update the value\n        await click(getComboboxOptions()[2])\n\n        // Verify the value changed\n        expect(getComboboxInput()?.value).not.toBe(value)\n      })\n    )\n\n    it(\n      'should close the Combobox when the input is blurred',\n      suppressConsoleLogs(async () => {\n        let closeHandler = jest.fn()\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        render(\n          <Combobox<(typeof data)[number]> name=\"assignee\" by=\"id\" onClose={closeHandler}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button />\n            <Combobox.Options>\n              {data.map((person) => (\n                <Combobox.Option key={person.id} value={person}>\n                  {person.label}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Verify it is open\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        // Close the combobox\n        expect(closeHandler).toHaveBeenCalledTimes(0)\n        await blur(getComboboxInput())\n        expect(closeHandler).toHaveBeenCalledTimes(1)\n\n        // Verify it is closed\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not crash when the `Combobox` still contains a `nullable` prop',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        render(\n          <Combobox nullable as={Fragment}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button />\n            <Combobox.Options>\n              {data.map((person) => (\n                <Combobox.Option key={person.id} value={person}>\n                  {person.label}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n        )\n      })\n    )\n  })\n\n  describe('Combobox.Input', () => {\n    it(\n      'selecting an option puts the value into Combobox.Input when displayValue is not provided',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState(null)\n\n          return (\n            <Combobox value={value} onChange={setValue}>\n              <Combobox.Input onChange={NOOP} />\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                <Combobox.Option value=\"c\">Option C</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        assertComboboxInput({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxInput({ state: ComboboxState.Visible })\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        await click(getComboboxOptions()[1])\n\n        expect(getComboboxInput()).toHaveValue('b')\n      })\n    )\n\n    it(\n      'selecting an option puts the display value into Combobox.Input when displayValue is provided',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState(null)\n\n          return (\n            <Combobox value={value} onChange={setValue}>\n              <Combobox.Input\n                onChange={NOOP}\n                displayValue={(str?: string) => str?.toUpperCase() ?? ''}\n              />\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                <Combobox.Option value=\"c\">Option C</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        await click(getComboboxButton())\n\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        await click(getComboboxOptions()[1])\n\n        expect(getComboboxInput()).toHaveValue('B')\n      })\n    )\n\n    it(\n      'selecting an option puts the display value into Combobox.Input when displayValue is provided (when value is undefined)',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState<null | undefined>(undefined)\n\n          return (\n            <Combobox value={value} onChange={setValue}>\n              <Combobox.Input\n                onChange={NOOP}\n                displayValue={(str?: string) => str?.toUpperCase() ?? ''}\n              />\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                <Combobox.Option value=\"c\">Option C</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the input\n        await focus(getComboboxInput())\n\n        // Type in it\n        await type(word('A'), getComboboxInput())\n\n        // Stop typing (and clear the input)\n        await press(Keys.Escape, getComboboxInput())\n\n        // Focus the body (so the input loses focus)\n        await focus(document.body)\n\n        expect(getComboboxInput()).toHaveValue('')\n      })\n    )\n\n    it(\n      'conditionally rendering the input should allow changing the display value',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState(null)\n          let [suffix, setSuffix] = useState(false)\n\n          return (\n            <>\n              <Combobox value={value} onChange={setValue}>\n                <Combobox.Input\n                  onChange={NOOP}\n                  displayValue={(str?: string) =>\n                    `${str?.toUpperCase() ?? ''} ${suffix ? 'with suffix' : 'no suffix'}`\n                  }\n                />\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options>\n                  <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                  <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                  <Combobox.Option value=\"c\">Option C</Combobox.Option>\n                </Combobox.Options>\n                <button onClick={() => setSuffix((v) => !v)}>Toggle suffix</button>\n              </Combobox>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        expect(getComboboxInput()).toHaveValue(' no suffix')\n\n        await click(getComboboxButton())\n\n        expect(getComboboxInput()).toHaveValue(' no suffix')\n\n        await click(getComboboxOptions()[1])\n\n        expect(getComboboxInput()).toHaveValue('B no suffix')\n\n        await click(getByText('Toggle suffix'))\n\n        expect(getComboboxInput()).toHaveValue('B with suffix')\n\n        await click(getComboboxButton())\n\n        expect(getComboboxInput()).toHaveValue('B with suffix')\n\n        await click(getComboboxOptions()[0])\n\n        expect(getComboboxInput()).toHaveValue('A with suffix')\n      })\n    )\n\n    it(\n      'should be possible to override the `type` on the input',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState(null)\n\n          return (\n            <Combobox value={value} onChange={setValue}>\n              <Combobox.Input type=\"search\" onChange={NOOP} />\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                <Combobox.Option value=\"c\">Option C</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        expect(getComboboxInput()).toHaveAttribute('type', 'search')\n      })\n    )\n\n    xit(\n      'should reflect the value in the input when the value changes and when you are typing',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [value, setValue] = useState<string | null>('bob')\n          let [_query, setQuery] = useState('')\n\n          return (\n            <Combobox value={value} onChange={setValue}>\n              {({ open }) => (\n                <>\n                  <Combobox.Input\n                    onChange={(event) => setQuery(event.target.value)}\n                    displayValue={(person) => `${person ?? ''} - ${open ? 'open' : 'closed'}`}\n                  />\n\n                  <Combobox.Button />\n\n                  <Combobox.Options>\n                    <Combobox.Option value=\"alice\">alice</Combobox.Option>\n                    <Combobox.Option value=\"bob\">bob</Combobox.Option>\n                    <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n                  </Combobox.Options>\n                </>\n              )}\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        // Check for proper state sync\n        expect(getComboboxInput()).toHaveValue('bob - closed')\n        await click(getComboboxButton())\n        expect(getComboboxInput()).toHaveValue('bob - open')\n        await click(getComboboxButton())\n        expect(getComboboxInput()).toHaveValue('bob - closed')\n\n        // Check if we can still edit the input\n        for (let _ of Array(' - closed'.length)) {\n          await press(Keys.Backspace, getComboboxInput())\n        }\n        getComboboxInput()?.select()\n        await type(word('alice'), getComboboxInput())\n        expect(getComboboxInput()).toHaveValue('alice')\n\n        // Open the combobox and choose an option\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie - closed')\n      })\n    )\n\n    it(\n      'should move the caret to the end of the input when syncing the value',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          return (\n            <Combobox>\n              <Combobox.Input />\n              <Combobox.Button />\n\n              <Combobox.Options>\n                <Combobox.Option value=\"alice\">alice</Combobox.Option>\n                <Combobox.Option value=\"bob\">bob</Combobox.Option>\n                <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Choose charlie\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie')\n\n        // Ensure the selection is in the correct position\n        expect(getComboboxInput()?.selectionStart).toBe('charlie'.length)\n        expect(getComboboxInput()?.selectionEnd).toBe('charlie'.length)\n      })\n    )\n\n    // Skipped because JSDOM doesn't implement this properly: https://github.com/jsdom/jsdom/issues/3524\n    xit(\n      'should not move the caret to the end of the input when syncing the value if a custom selection is made',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          return (\n            <Combobox>\n              <Combobox.Input\n                onFocus={(e) => {\n                  e.target.select()\n                  e.target.setSelectionRange(0, e.target.value.length)\n                }}\n              />\n              <Combobox.Button />\n\n              <Combobox.Options>\n                <Combobox.Option value=\"alice\">alice</Combobox.Option>\n                <Combobox.Option value=\"bob\">bob</Combobox.Option>\n                <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        render(<Example />)\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Choose charlie\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie')\n\n        // Ensure the selection is in the correct position\n        expect(getComboboxInput()?.selectionStart).toBe(0)\n        expect(getComboboxInput()?.selectionEnd).toBe('charlie'.length)\n      })\n    )\n  })\n\n  describe('Combobox.Label', () => {\n    it(\n      'should be possible to render a Combobox.Label using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Label>{(slot) => <>{JSON.stringify(slot)}</>}</Combobox.Label>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxLabel({ textContent: JSON.stringify({ open: false, disabled: false }) })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxLabel({ textContent: JSON.stringify({ open: true, disabled: false }) })\n        assertComboboxList({ state: ComboboxState.Visible })\n        assertComboboxLabelLinkedWithCombobox()\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to link Input/Button and Label if Label is rendered last',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"Test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button />\n            <Combobox.Label>Label</Combobox.Label>\n          </Combobox>\n        )\n\n        assertComboboxLabelLinkedWithCombobox()\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to render a Combobox.Label using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Label as=\"p\">{(slot) => <>{JSON.stringify(slot)}</>}</Combobox.Label>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxLabel({\n          textContent: JSON.stringify({ open: false, disabled: false }),\n          tag: 'p',\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n        assertComboboxLabel({\n          textContent: JSON.stringify({ open: true, disabled: false }),\n          tag: 'p',\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n  })\n\n  describe('Combobox.Button', () => {\n    it(\n      'should be possible to render a Combobox.Button using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>{(slot) => <>{JSON.stringify(slot)}</>}</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            invalid: false,\n            value: 'test',\n            hover: false,\n            focus: false,\n          }),\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            invalid: false,\n            value: 'test',\n            hover: false,\n            focus: false,\n          }),\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Combobox.Button using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button as=\"div\" role=\"button\">\n              {(slot) => <>{JSON.stringify(slot)}</>}\n            </Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            invalid: false,\n            value: 'test',\n            hover: false,\n            focus: false,\n          }),\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            invalid: false,\n            value: 'test',\n            hover: false,\n            focus: false,\n          }),\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Combobox.Button and a Combobox.Label and see them linked together',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Label>Label</Combobox.Label>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option A</Combobox.Option>\n              <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              <Combobox.Option value=\"c\">Option C</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        // TODO: Needed to make it similar to vue test implementation?\n        // await new Promise(requestAnimationFrame)\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        render(\n          <Combobox value={null} onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n          </Combobox>\n        )\n\n        expect(getComboboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        render(\n          <Combobox value={null} onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button type=\"submit\">Trigger</Combobox.Button>\n          </Combobox>\n        )\n\n        expect(getComboboxButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n        let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n          <button ref={ref} {...props} />\n        ))\n\n        render(\n          <Combobox value={null} onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button as={CustomButton}>Trigger</Combobox.Button>\n          </Combobox>\n        )\n\n        expect(getComboboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        render(\n          <Combobox value={null} onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button as=\"div\">Trigger</Combobox.Button>\n          </Combobox>\n        )\n\n        expect(getComboboxButton()).not.toHaveAttribute('type')\n      })\n\n      it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n        let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n          <div ref={ref} {...props} />\n        ))\n\n        render(\n          <Combobox value={null} onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button as={CustomButton}>Trigger</Combobox.Button>\n          </Combobox>\n        )\n\n        expect(getComboboxButton()).not.toHaveAttribute('type')\n      })\n    })\n\n    it(\n      'should be possible to render a ComboboxButton using as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox>\n            <ComboboxInput />\n            <ComboboxButton as={Fragment}>\n              <button>Toggle</button>\n            </ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              <ComboboxOption value=\"b\">Option B</ComboboxOption>\n              <ComboboxOption value=\"c\">Option C</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.Visible })\n      })\n    )\n  })\n\n  describe('Combobox.Options', () => {\n    it(\n      'should be possible to render Combobox.Options using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              {(data) => (\n                <>\n                  <Combobox.Option value=\"a\">{JSON.stringify(data)}</Combobox.Option>\n                </>\n              )}\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.Visible })\n        assertComboboxList({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertActiveElement(getComboboxInput())\n      })\n    )\n\n    it('should be possible to always render the Combobox.Options if we provide it a `static` prop', () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options static>\n            <Combobox.Option value=\"a\">Option A</Combobox.Option>\n            <Combobox.Option value=\"b\">Option B</Combobox.Option>\n            <Combobox.Option value=\"c\">Option C</Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      // Let's verify that the Combobox is already there\n      expect(getComboboxInput()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the Combobox.Options', async () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options unmount={false}>\n            <Combobox.Option value=\"a\">Option A</Combobox.Option>\n            <Combobox.Option value=\"b\">Option B</Combobox.Option>\n            <Combobox.Option value=\"c\">Option C</Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      assertComboboxList({ state: ComboboxState.InvisibleHidden })\n\n      // Let's open the Combobox, to see if it is not hidden anymore\n      await click(getComboboxButton())\n\n      assertComboboxList({ state: ComboboxState.Visible })\n    })\n  })\n\n  describe('Combobox.Option', () => {\n    it(\n      'should be possible to render a Combobox.Option using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">{(slot) => <>{JSON.stringify(slot)}</>}</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n\n        assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({ state: ComboboxState.Visible })\n        assertComboboxList({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({\n            active: true,\n            focus: true,\n            selected: false,\n            disabled: false,\n          }),\n        })\n      })\n    )\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    function Example({ hide = false }) {\n      return (\n        <>\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">Option 1</Combobox.Option>\n              {!hide && <Combobox.Option value=\"b\">Option 2</Combobox.Option>}\n              <Combobox.Option value=\"c\">Option 3</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        </>\n      )\n    }\n\n    let { rerender } = render(<Example />)\n\n    // Open the Combobox\n    await click(getByText('Trigger'))\n\n    rerender(<Example hide={true} />) // Remove Combobox.Option 2\n    rerender(<Example hide={false} />) // Re-add Combobox.Option 2\n\n    assertComboboxList({ state: ComboboxState.Visible })\n\n    let options = getComboboxOptions()\n\n    // Verify that the first combobox option is active\n    assertActiveComboboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second combobox option is active\n    assertActiveComboboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third combobox option is active\n    assertActiveComboboxOption(options[2])\n  })\n\n  it('should guarantee the order of options based on `order` when performing actions', async () => {\n    function Example({ hide = false }) {\n      return (\n        <>\n          <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"a\" order={1}>\n                Option 1\n              </Combobox.Option>\n              {!hide && (\n                <Combobox.Option value=\"b\" order={2}>\n                  Option 2\n                </Combobox.Option>\n              )}\n              <Combobox.Option value=\"c\" order={3}>\n                Option 3\n              </Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        </>\n      )\n    }\n\n    let { rerender } = render(<Example />)\n\n    // Open the Combobox\n    await click(getByText('Trigger'))\n\n    rerender(<Example hide={true} />) // Remove Combobox.Option 2\n    rerender(<Example hide={false} />) // Re-add Combobox.Option 2\n\n    assertComboboxList({ state: ComboboxState.Visible })\n\n    let options = getComboboxOptions()\n\n    // Verify that the first combobox option is active\n    assertActiveComboboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second combobox option is active\n    assertActiveComboboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third combobox option is active\n    assertActiveComboboxOption(options[2])\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox name=\"assignee\">\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">Alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">Bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">Charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose charlie\n      await click(getComboboxOptions()[2])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should expose the value via the render prop', async () => {\n      let handleSubmission = jest.fn()\n\n      let { getByTestId } = render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox<string> name=\"assignee\">\n            {({ value }) => (\n              <>\n                <div data-testid=\"value\">{value}</div>\n                <Combobox.Input onChange={NOOP} />\n                <Combobox.Button>\n                  {({ value }) => (\n                    <>\n                      Trigger\n                      <div data-testid=\"value-2\">{value}</div>\n                    </>\n                  )}\n                </Combobox.Button>\n                <Combobox.Options>\n                  <Combobox.Option value=\"alice\">Alice</Combobox.Option>\n                  <Combobox.Option value=\"bob\">Bob</Combobox.Option>\n                  <Combobox.Option value=\"charlie\">Charlie</Combobox.Option>\n                </Combobox.Options>\n              </>\n            )}\n          </Combobox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n      expect(getByTestId('value')).toHaveTextContent('alice')\n      expect(getByTestId('value-2')).toHaveTextContent('alice')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose charlie\n      await click(getComboboxOptions()[2])\n      expect(getByTestId('value')).toHaveTextContent('charlie')\n      expect(getByTestId('value-2')).toHaveTextContent('charlie')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox name=\"assignee\" defaultValue=\"bob\">\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">Alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">Bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">Charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n    })\n\n    it('should be possible to reset to the default value if the form is reset', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox name=\"assignee\" defaultValue=\"bob\">\n            <Combobox.Button>{({ value }) => value ?? 'Trigger'}</Combobox.Button>\n            <Combobox.Input onChange={NOOP} displayValue={(value: string) => value} />\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">Alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">Bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">Charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n      expect(getComboboxButton()).toHaveTextContent('alice')\n      expect(getComboboxInput()).toHaveValue('alice')\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // The combobox should be reset to bob\n      expect(getComboboxButton()).toHaveTextContent('bob')\n      expect(getComboboxInput()).toHaveValue('bob')\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n\n    it('should be possible to reset to the default value if the form is reset (using objects)', async () => {\n      let handleSubmission = jest.fn()\n\n      let data = [\n        { id: 1, name: 'alice', label: 'Alice' },\n        { id: 2, name: 'bob', label: 'Bob' },\n        { id: 3, name: 'charlie', label: 'Charlie' },\n      ]\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox<(typeof data)[number]>\n            name=\"assignee\"\n            defaultValue={{ id: 2, name: 'bob', label: 'Bob' }}\n            by=\"id\"\n          >\n            <Combobox.Button>{({ value }) => value?.name ?? 'Trigger'}</Combobox.Button>\n            <Combobox.Input\n              onChange={NOOP}\n              displayValue={(value: (typeof data)[0]) => value.name}\n            />\n            <Combobox.Options>\n              {data.map((person) => (\n                <Combobox.Option key={person.id} value={person}>\n                  {person.label}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[id]': '2',\n        'assignee[name]': 'bob',\n        'assignee[label]': 'Bob',\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n      expect(getComboboxButton()).toHaveTextContent('alice')\n      expect(getComboboxInput()).toHaveValue('alice')\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // The combobox should be reset to bob\n      expect(getComboboxButton()).toHaveTextContent('bob')\n      expect(getComboboxInput()).toHaveValue('bob')\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n\n    it('should be possible to reset to the default value in multiple mode', async () => {\n      let handleSubmission = jest.fn()\n      let data = ['alice', 'bob', 'charlie']\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Combobox name=\"assignee\" defaultValue={['bob'] as string[]} multiple>\n            <Combobox.Button>{({ value }) => value.join(', ') || 'Trigger'}</Combobox.Button>\n            <Combobox.Options>\n              {data.map((person) => (\n                <Combobox.Option key={person} value={person}>\n                  {person}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n\n      await click(document.getElementById('reset'))\n      await click(document.getElementById('submit'))\n\n      // Bob is still the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      render(\n        <Combobox name=\"assignee\" onChange={handleChange}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options>\n            <Combobox.Option value=\"alice\">Alice</Combobox.Option>\n            <Combobox.Option value=\"bob\">Bob</Combobox.Option>\n            <Combobox.Option value=\"charlie\">Charlie</Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose bob\n      await click(getComboboxOptions()[1])\n\n      // Change handler should have been called twice\n      expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n      expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n    })\n  })\n\n  describe.each([{ virtual: true }, { virtual: false }])('Data attributes', ({ virtual }) => {\n    let data = ['Option A', 'Option B', 'Option C']\n    function MyCombobox<T>({\n      options = data.slice() as T[],\n      useComboboxOptions = true,\n      comboboxProps = {},\n      inputProps = {},\n      buttonProps = {},\n      optionProps = {},\n    }: {\n      options?: T[]\n      useComboboxOptions?: boolean\n      comboboxProps?: Record<string, any>\n      inputProps?: Record<string, any>\n      buttonProps?: Record<string, any>\n      optionProps?: Record<string, any>\n    }) {\n      function isDisabled(option: T): boolean {\n        return typeof option === 'string'\n          ? false\n          : typeof option === 'object' &&\n              option !== null &&\n              'disabled' in option &&\n              typeof option.disabled === 'boolean'\n            ? option?.disabled ?? false\n            : false\n      }\n      if (virtual) {\n        return (\n          <Combobox\n            virtual={{\n              options,\n              disabled: isDisabled,\n            }}\n            value={'test' as T}\n            onChange={NOOP}\n            {...comboboxProps}\n          >\n            <Combobox.Input onChange={NOOP} {...inputProps} />\n            <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n            {useComboboxOptions && (\n              <Combobox.Options>\n                {({ option }) => {\n                  return <Combobox.Option {...optionProps} value={option} />\n                }}\n              </Combobox.Options>\n            )}\n          </Combobox>\n        )\n      }\n\n      return (\n        <Combobox value=\"test\" onChange={NOOP} {...comboboxProps}>\n          <Combobox.Input onChange={NOOP} {...inputProps} />\n          <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n          {useComboboxOptions && (\n            <Combobox.Options>\n              {options.map((option, idx) => {\n                return (\n                  <Combobox.Option\n                    key={idx}\n                    disabled={isDisabled(option)}\n                    {...optionProps}\n                    value={option}\n                  />\n                )\n              })}\n            </Combobox.Options>\n          )}\n        </Combobox>\n      )\n    }\n\n    it('Disabled options should get a data-disabled attribute', async () => {\n      render(\n        <MyCombobox\n          options={[\n            { name: 'Option A', disabled: false },\n            { name: 'Option B', disabled: true },\n            { name: 'Option C', disabled: false },\n          ]}\n        />\n      )\n\n      // Open the Combobox\n      await click(getByText('Trigger'))\n\n      let options = getComboboxOptions()\n\n      expect(options[0]).not.toHaveAttribute('data-disabled')\n      expect(options[1]).toHaveAttribute('data-disabled', '')\n      expect(options[2]).not.toHaveAttribute('data-disabled')\n    })\n  })\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to conditionally render classNames (aka className can be a function?!)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options>\n            <Combobox.Option value=\"a\" className={(bag) => JSON.stringify(bag)}>\n              Option A\n            </Combobox.Option>\n            <Combobox.Option value=\"b\" disabled className={(bag) => JSON.stringify(bag)}>\n              Option B\n            </Combobox.Option>\n            <Combobox.Option value=\"c\" className=\"no-special-treatment\">\n              Option C\n            </Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open Combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // Verify that the first combobox option is active\n      assertActiveComboboxOption(options[0])\n\n      // Verify correct classNames\n      expect('' + options[0].classList).toEqual(\n        JSON.stringify({\n          active: true,\n          focus: true,\n          selected: false,\n          disabled: false,\n        })\n      )\n      expect('' + options[1].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: true,\n        })\n      )\n      expect('' + options[2].classList).toEqual('no-special-treatment')\n\n      // Let's go down, this should go to the third option since the second option is disabled!\n      await press(Keys.ArrowDown)\n\n      // Verify the classNames\n      expect('' + options[0].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: false,\n        })\n      )\n      expect('' + options[1].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: true,\n        })\n      )\n      expect('' + options[2].classList).toEqual('no-special-treatment')\n\n      // Double check that the last option is the active one\n      assertActiveComboboxOption(options[2])\n    })\n  )\n\n  it(\n    'should be possible to swap the Combobox option with a button for example',\n    suppressConsoleLogs(async () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options>\n            <Combobox.Option as=\"button\" value=\"a\">\n              Option A\n            </Combobox.Option>\n            <Combobox.Option as=\"button\" value=\"b\">\n              Option B\n            </Combobox.Option>\n            <Combobox.Option as=\"button\" value=\"c\">\n              Option C\n            </Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open Combobox\n      await click(getComboboxButton())\n\n      // Verify options are buttons now\n      getComboboxOptions().forEach((option) => assertComboboxOption(option, { tag: 'button' }))\n    })\n  )\n\n  it(\n    'should mark all the elements between Combobox.Options and Combobox.Option with role none',\n    suppressConsoleLogs(async () => {\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button />\n          <div className=\"outer\">\n            <Combobox.Options>\n              <div className=\"inner py-1\">\n                <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                <Combobox.Option value=\"b\">Option B</Combobox.Option>\n              </div>\n              <div className=\"inner py-1\">\n                <Combobox.Option value=\"c\">Option C</Combobox.Option>\n                <Combobox.Option value=\"d\">\n                  <div>\n                    <div className=\"outer\">Option D</div>\n                  </div>\n                </Combobox.Option>\n              </div>\n              <div className=\"inner py-1\">\n                <form className=\"inner\">\n                  <Combobox.Option value=\"e\">Option E</Combobox.Option>\n                </form>\n              </div>\n            </Combobox.Options>\n          </div>\n        </Combobox>\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      expect.hasAssertions()\n\n      document.querySelectorAll('.outer').forEach((element) => {\n        expect(element).not.toHaveAttribute('role', 'none')\n      })\n\n      document.querySelectorAll('.inner').forEach((element) => {\n        expect(element).toHaveAttribute('role', 'none')\n      })\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {\n    useEffect(() => {\n      fn(`Mounting - ${name}`)\n      return () => {\n        fn(`Unmounting - ${name}`)\n      }\n    }, [fn, name])\n    return null\n  }\n\n  it(\n    'should be possible to wrap the Combobox.Options with a Transition component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Debug name=\"Combobox\" fn={orderFn} />\n          <Transition>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Combobox.Options>\n              <Combobox.Option value=\"a\">\n                {(data) => (\n                  <>\n                    {JSON.stringify(data)}\n                    <Debug name=\"Combobox.Option\" fn={orderFn} />\n                  </>\n                )}\n              </Combobox.Option>\n            </Combobox.Options>\n          </Transition>\n        </Combobox>\n      )\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      await rawClick(getComboboxButton())\n\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({\n        state: ComboboxState.Visible,\n        textContent: JSON.stringify({\n          active: true,\n          focus: true,\n          selected: false,\n          disabled: false,\n        }),\n      })\n\n      await rawClick(getComboboxButton())\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Combobox'],\n        ['Mounting - Transition'],\n        ['Mounting - Combobox.Option'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Combobox.Option'],\n      ])\n    })\n  )\n})\n\ndescribe.each([{ virtual: true }, { virtual: false }])(\n  'Keyboard interactions %s',\n  ({ virtual }) => {\n    let data = ['Option A', 'Option B', 'Option C']\n    function MyCombobox<T>({\n      options = data.slice() as T[],\n      useComboboxOptions = true,\n      comboboxProps = {},\n      inputProps = {},\n      buttonProps = {},\n      optionProps = {},\n    }: {\n      options?: T[]\n      useComboboxOptions?: boolean\n      comboboxProps?: Record<string, any>\n      inputProps?: Record<string, any>\n      buttonProps?: Record<string, any>\n      optionProps?: Record<string, any>\n    }) {\n      function isDisabled(option: T): boolean {\n        return typeof option === 'string'\n          ? false\n          : typeof option === 'object' &&\n              option !== null &&\n              'disabled' in option &&\n              typeof option.disabled === 'boolean'\n            ? option?.disabled ?? false\n            : false\n      }\n      if (virtual) {\n        return (\n          <Combobox\n            virtual={{\n              options,\n              disabled: isDisabled,\n            }}\n            value={'test' as T}\n            onChange={NOOP}\n            {...comboboxProps}\n          >\n            <Combobox.Input onChange={NOOP} {...inputProps} />\n            <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n            {useComboboxOptions && (\n              <Combobox.Options>\n                {({ option }) => {\n                  return <Combobox.Option {...optionProps} value={option} />\n                }}\n              </Combobox.Options>\n            )}\n          </Combobox>\n        )\n      }\n\n      return (\n        <Combobox value=\"test\" onChange={NOOP} {...comboboxProps}>\n          <Combobox.Input onChange={NOOP} {...inputProps} />\n          <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n          {useComboboxOptions && (\n            <Combobox.Options>\n              {options.map((option, idx) => {\n                return (\n                  <Combobox.Option\n                    key={idx}\n                    disabled={isDisabled(option)}\n                    {...optionProps}\n                    value={option}\n                  />\n                )\n              })}\n            </Combobox.Options>\n          )}\n        </Combobox>\n      )\n    }\n\n    describe('Button', () => {\n      describe('`Enter` key', () => {\n        it(\n          'should be possible to open the combobox with Enter',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option, { selected: false }))\n\n            assertActiveComboboxOption(options[0])\n            assertNoSelectedComboboxOption()\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with Enter when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Try to focus the button\n            await focus(getComboboxButton())\n\n            // Try to open the combobox\n            await press(Keys.Enter)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option (when using the `hidden` render strategy)',\n          suppressConsoleLogs(async () => {\n            if (virtual) return // Incompatible with virtual rendering\n\n            render(\n              <Combobox value=\"b\" onChange={(x) => console.log(x)}>\n                <Combobox.Input onChange={NOOP} />\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options unmount={false}>\n                  <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                  <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                  <Combobox.Option value=\"c\">Option C</Combobox.Option>\n                </Combobox.Options>\n              </Combobox>\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleHidden })\n            assertComboboxList({ state: ComboboxState.InvisibleHidden })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            let options = getComboboxOptions()\n\n            // Hover over Option A\n            await mouseMove(options[0])\n\n            // Verify that Option A is active\n            assertActiveComboboxOption(options[0])\n\n            // Verify that Option B is still selected\n            assertComboboxOption(options[1], { selected: true })\n\n            // Close/Hide the combobox\n            await press(Keys.Escape)\n\n            // Re-open the combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option (with a list of objects)',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Space` key', () => {\n        it(\n          'should be possible to open the combobox with Space',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with Space when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined, disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Try to open the combobox\n            await press(Keys.Space)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Space, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({\n              state: ComboboxState.InvisibleUnmounted,\n            })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should have no active combobox option upon Space key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'alice', children: 'alice', disabled: true },\n                  { value: 'bob', children: 'bob', disabled: true },\n                  { value: 'charlie', children: 'charlie', disabled: true },\n                ]}\n              />\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Escape` key', () => {\n        it(\n          'should be possible to close an open combobox with Escape',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Re-focus the button\n            await focus(getComboboxButton())\n            assertActiveElement(getComboboxButton())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify the input is focused again\n            assertActiveElement(getComboboxInput())\n          })\n        )\n\n        it(\n          'should not propagate the Escape event when the combobox is open',\n          suppressConsoleLogs(async () => {\n            let handleKeyDown = jest.fn()\n            render(\n              <div onKeyDown={handleKeyDown}>\n                <MyCombobox />\n              </div>\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // We should never see the Escape event\n            expect(handleKeyDown).toHaveBeenCalledTimes(0)\n          })\n        )\n\n        it(\n          'should propagate the Escape event when the combobox is closed',\n          suppressConsoleLogs(async () => {\n            let handleKeyDown = jest.fn()\n            render(\n              <div onKeyDown={handleKeyDown}>\n                <MyCombobox />\n              </div>\n            )\n\n            // Focus the input field\n            await focus(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // We should never see the Escape event\n            expect(handleKeyDown).toHaveBeenCalledTimes(1)\n          })\n        )\n      })\n\n      describe('`ArrowDown` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowDown',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // Verify that the first combobox option is active\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowDown when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Try to open the combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowDown, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`ArrowUp` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowUp and the last option should be active',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // ! ALERT: The LAST option should now be active\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowUp and the last option should be active when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Try to open the combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowUp, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'alice', children: 'alice', disabled: false },\n                  { value: 'bob', children: 'bob', disabled: true },\n                  { value: 'charlie', children: 'charlie', disabled: true },\n                ]}\n              />\n            )\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            await focus(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n          })\n        )\n      })\n    })\n\n    describe('Input', () => {\n      describe('`Enter` key', () => {\n        it(\n          'should be possible to close the combobox with Enter and choose the active combobox option',\n          suppressConsoleLogs(async () => {\n            let handleChange = jest.fn()\n            let closeHandler = jest.fn()\n\n            function Example() {\n              let [value, setValue] = useState<string | undefined>(undefined)\n\n              return (\n                <MyCombobox\n                  comboboxProps={{\n                    value,\n                    onClose: closeHandler,\n                    onChange(value: string | undefined) {\n                      setValue(value)\n                      handleChange(value)\n                    },\n                  }}\n                />\n              )\n            }\n\n            render(<Example />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n\n            // Activate the first combobox option\n            let options = getComboboxOptions()\n            await mouseMove(options[0])\n\n            // Choose option, and close combobox\n            expect(closeHandler).toHaveBeenCalledTimes(0)\n            await press(Keys.Enter)\n            expect(closeHandler).toHaveBeenCalledTimes(1)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify we got the change event\n            expect(handleChange).toHaveBeenCalledTimes(1)\n            expect(handleChange).toHaveBeenCalledWith('Option A')\n\n            // Verify the button is focused again\n            assertActiveElement(getComboboxInput())\n\n            // Open combobox again\n            await click(getComboboxButton())\n\n            // Verify the active option is the previously selected one\n            assertActiveComboboxOption(getComboboxOptions()[0])\n          })\n        )\n\n        it(\n          'should submit the form on `Enter`',\n          suppressConsoleLogs(async () => {\n            let submits = jest.fn()\n\n            function Example() {\n              let [value, setValue] = useState<string>('b')\n\n              return (\n                <form\n                  onKeyUp={(event) => {\n                    // JSDom doesn't automatically submit the form but if we can\n                    // catch an `Enter` event, we can assume it was a submit.\n                    if (event.key === 'Enter') event.currentTarget.submit()\n                  }}\n                  onSubmit={(event) => {\n                    event.preventDefault()\n                    submits([...new FormData(event.currentTarget).entries()])\n                  }}\n                >\n                  <MyCombobox comboboxProps={{ value, onChange: setValue, name: 'option' }} />\n                  <button>Submit</button>\n                </form>\n              )\n            }\n\n            render(<Example />)\n\n            // Focus the input field\n            await focus(getComboboxInput())\n            assertActiveElement(getComboboxInput())\n\n            // Press enter (which should submit the form)\n            await press(Keys.Enter)\n\n            // Verify the form was submitted\n            expect(submits).toHaveBeenCalledTimes(1)\n            expect(submits).toHaveBeenCalledWith([['option', 'b']])\n          })\n        )\n\n        it(\n          'should submit the form on `Enter` (when no submit button was found)',\n          suppressConsoleLogs(async () => {\n            let submits = jest.fn()\n\n            function Example() {\n              let [value, setValue] = useState<string>('b')\n\n              return (\n                <form\n                  onKeyUp={(event) => {\n                    // JSDom doesn't automatically submit the form but if we can\n                    // catch an `Enter` event, we can assume it was a submit.\n                    if (event.key === 'Enter') event.currentTarget.submit()\n                  }}\n                  onSubmit={(event) => {\n                    event.preventDefault()\n                    submits([...new FormData(event.currentTarget).entries()])\n                  }}\n                >\n                  <MyCombobox comboboxProps={{ value, onChange: setValue, name: 'option' }} />\n                </form>\n              )\n            }\n\n            render(<Example />)\n\n            // Focus the input field\n            await focus(getComboboxInput())\n            assertActiveElement(getComboboxInput())\n\n            // Press enter (which should submit the form)\n            await press(Keys.Enter)\n\n            // Verify the form was submitted\n            expect(submits).toHaveBeenCalledTimes(1)\n            expect(submits).toHaveBeenCalledWith([['option', 'b']])\n          })\n        )\n      })\n\n      describe('`Tab` key', () => {\n        it(\n          'pressing Tab should select the active item and move to the next DOM node',\n          suppressConsoleLogs(async () => {\n            function Example() {\n              let [value, setValue] = useState<string | undefined>(undefined)\n\n              return (\n                <>\n                  <input id=\"before-combobox\" />\n                  <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n                  <input id=\"after-combobox\" />\n                </>\n              )\n            }\n\n            render(<Example />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Select the 2nd option\n            await press(Keys.ArrowDown)\n\n            // Tab to the next DOM node\n            await press(Keys.Tab)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // That the selected value was the highlighted one\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // And focus has moved to the next element\n            assertActiveElement(document.querySelector('#after-combobox'))\n          })\n        )\n\n        it(\n          'pressing Shift+Tab should select the active item and move to the previous DOM node',\n          suppressConsoleLogs(async () => {\n            function Example() {\n              let [value, setValue] = useState<string | undefined>(undefined)\n\n              return (\n                <>\n                  <input id=\"before-combobox\" />\n                  <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n                  <input id=\"after-combobox\" />\n                </>\n              )\n            }\n\n            render(<Example />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Select the 2nd option\n            await press(Keys.ArrowDown)\n\n            // Tab to the next DOM node\n            await press(shift(Keys.Tab))\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // That the selected value was the highlighted one\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // And focus has moved to the next element\n            assertActiveElement(document.querySelector('#before-combobox'))\n          })\n        )\n\n        it(\n          'pressing Tab should sync the ComboboxInput value again',\n          suppressConsoleLogs(async () => {\n            function Example() {\n              let [value, setValue] = useState<string | null>(null)\n\n              return (\n                <>\n                  <MyCombobox\n                    comboboxProps={{ value, onChange: setValue }}\n                    inputProps={{\n                      displayValue: (value: string | null) => value ?? '<cleared>',\n                    }}\n                  />\n                  <button id=\"clear\" onClick={() => setValue(null)}>\n                    Clear selection\n                  </button>\n                </>\n              )\n            }\n\n            render(<Example />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Select the 2nd option\n            await press(Keys.ArrowDown)\n\n            // Tab to the next DOM node\n            await press(Keys.Tab)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify the selected value was the highlighted one\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // Clear the option\n            await click(document.querySelector('button#clear') as HTMLButtonElement)\n\n            // Verify the input value is cleared\n            expect(getComboboxInput()?.value).toBe('<cleared>')\n          })\n        )\n      })\n\n      describe('`Escape` key', () => {\n        it(\n          'should be possible to close an open combobox with Escape',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify the button is focused again\n            assertActiveElement(getComboboxInput())\n          })\n        )\n\n        it(\n          'should bubble escape when using `static` on Combobox.Options',\n          suppressConsoleLogs(async () => {\n            if (virtual) return // Incompatible with virtual rendering\n\n            render(\n              <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n                <Combobox.Input onChange={NOOP} />\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options static>\n                  <Combobox.Option value=\"a\">Option A</Combobox.Option>\n                  <Combobox.Option value=\"b\">Option B</Combobox.Option>\n                  <Combobox.Option value=\"c\">Option C</Combobox.Option>\n                </Combobox.Options>\n              </Combobox>\n            )\n\n            let spy = jest.fn()\n\n            window.addEventListener(\n              'keydown',\n              (evt) => {\n                if (evt.key === 'Escape') {\n                  spy()\n                }\n              },\n              { capture: true }\n            )\n\n            window.addEventListener('keydown', (evt) => {\n              if (evt.key === 'Escape') {\n                spy()\n              }\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input is focused\n            assertActiveElement(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is still focused\n            assertActiveElement(getComboboxInput())\n\n            // The external event handler should've been called twice\n            // Once in the capture phase and once in the bubble phase\n            expect(spy).toHaveBeenCalledTimes(2)\n          })\n        )\n\n        it(\n          'should bubble escape when not using Combobox.Options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox useComboboxOptions={false} />)\n\n            let spy = jest.fn()\n\n            window.addEventListener(\n              'keydown',\n              (evt) => {\n                if (evt.key === 'Escape') {\n                  spy()\n                }\n              },\n              { capture: true }\n            )\n\n            window.addEventListener('keydown', (evt) => {\n              if (evt.key === 'Escape') {\n                spy()\n              }\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input is focused\n            assertActiveElement(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is still focused\n            assertActiveElement(getComboboxInput())\n\n            // The external event handler should've been called twice\n            // Once in the capture phase and once in the bubble phase\n            expect(spy).toHaveBeenCalledTimes(2)\n          })\n        )\n\n        it(\n          'should sync the input field correctly and reset it when pressing Escape',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input has the selected value\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // Override the input by typing something\n            await type(word('test'), getComboboxInput())\n            expect(getComboboxInput()?.value).toBe('test')\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is reset correctly\n            expect(getComboboxInput()?.value).toBe('Option B')\n          })\n        )\n      })\n\n      describe('`ArrowDown` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowDown',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // Verify that the first combobox option is active\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowDown when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined, disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Try to open the combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowDown, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go down once\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down again\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n\n            // We should NOT be able to go down again (because last option).\n            // Current implementation won't go around.\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options and skip the first disabled one',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ]}\n              />\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down once\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ]}\n              />\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to go to the next item if no value is set',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: null }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // Verify that we are on the first option\n            assertActiveComboboxOption(options[0])\n\n            // Go down once\n            await press(Keys.ArrowDown)\n\n            // We should be on the next item\n            assertActiveComboboxOption(options[1])\n          })\n        )\n      })\n\n      describe('`ArrowUp` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowUp and the last option should be active',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // ! ALERT: The LAST option should now be active\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowUp and the last option should be active when the button is disabled',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ disabled: true }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Try to open the combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is still closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowUp, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: 'Option B' }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox options={[]} />)\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                ]}\n              />\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to navigate up or down if there is only a single non-disabled option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ]}\n              />\n            )\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // Going up or down should select the single available option\n            await press(Keys.ArrowUp)\n\n            // We should not be able to go up (because those are disabled)\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go down (because this is the last option)\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined }} />)\n\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go down once\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down again\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[0])\n\n            // We should NOT be able to go up again (because first option). Current implementation won't go around.\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n      })\n\n      describe('`End` key', () => {\n        it(\n          'should be possible to use the End key to go to the last combobox option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await press(Keys.End)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the End key to go to the last non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last non-disabled option\n            await press(Keys.End)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to use the End key to go to the first combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should not be able to go to the end (no-op)\n            await press(Keys.End)\n\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon End key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.End)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`PageDown` key', () => {\n        it(\n          'should be possible to use the PageDown key to go to the last combobox option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await press(Keys.PageDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the PageDown key to go to the last non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last non-disabled option\n            await press(Keys.PageDown)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to use the PageDown key to go to the first combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should not be able to go to the end\n            await press(Keys.PageDown)\n\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon PageDown key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.PageDown)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Home` key', () => {\n        it(\n          'should be possible to use the Home key to go to the first combobox option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined }} />)\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go to the first option\n            await press(Keys.Home)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to use the Home key to go to the first non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the Home key to go to the last combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[3])\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            assertActiveComboboxOption(options[3])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon Home key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`PageUp` key', () => {\n        it(\n          'should be possible to use the PageUp key to go to the first combobox option',\n          suppressConsoleLogs(async () => {\n            render(<MyCombobox comboboxProps={{ value: undefined }} />)\n\n            // Focus the input\n            await focus(getComboboxInput())\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go to the first option\n            await press(Keys.PageUp)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to use the PageUp key to go to the first non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We opened via click, we default to the first non-disabled option\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go to the end (no-op — already there)\n            await press(Keys.PageUp)\n\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the PageUp key to go to the last combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We opened via click, we default to the first non-disabled option\n            assertActiveComboboxOption(options[3])\n\n            // We should not be able to go to the end (no-op — already there)\n            await press(Keys.PageUp)\n\n            assertActiveComboboxOption(options[3])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon PageUp key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            render(\n              <MyCombobox\n                options={[\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.PageUp)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Backspace` key', () => {\n        it(\n          'should reset the value when the last character is removed',\n          suppressConsoleLogs(async () => {\n            let handleChange = jest.fn()\n            function Example() {\n              let [value, setValue] = useState<string | null>('bob')\n              let [, setQuery] = useState<string>('')\n\n              return (\n                <Combobox\n                  value={value}\n                  onChange={(value) => {\n                    setValue(value)\n                    handleChange(value)\n                  }}\n                >\n                  <Combobox.Input onChange={(event) => setQuery(event.target.value)} />\n                  <Combobox.Button>Trigger</Combobox.Button>\n                  <Combobox.Options>\n                    <Combobox.Option order={virtual ? 1 : undefined} value=\"alice\">\n                      Alice\n                    </Combobox.Option>\n                    <Combobox.Option order={virtual ? 1 : undefined} value=\"bob\">\n                      Bob\n                    </Combobox.Option>\n                    <Combobox.Option order={virtual ? 1 : undefined} value=\"charlie\">\n                      Charlie\n                    </Combobox.Option>\n                  </Combobox.Options>\n                </Combobox>\n              )\n            }\n\n            render(<Example />)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // Bob should be active\n            options = getComboboxOptions()\n            expect(getComboboxInput()).toHaveValue('bob')\n            assertActiveComboboxOption(options[1])\n\n            assertActiveElement(getComboboxInput())\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('bo')\n            assertActiveComboboxOption(options[1])\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('b')\n            assertActiveComboboxOption(options[1])\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('')\n\n            // Verify that we don't have an selected option anymore\n            assertNotActiveComboboxOption(options[1])\n\n            // Verify that we saw the `null` change coming in\n            expect(handleChange).toHaveBeenCalledTimes(1)\n            expect(handleChange).toHaveBeenCalledWith(null)\n          })\n        )\n      })\n\n      describe('`Any` key aka search', () => {\n        type Option = { value: string; name: string; disabled: boolean }\n        function Example(props: { people: { value: string; name: string; disabled: boolean }[] }) {\n          let [value, setValue] = useState<Option | null | undefined>(undefined)\n          let [query, setQuery] = useState<string>('')\n          let filteredPeople =\n            query === ''\n              ? props.people\n              : props.people.filter((person) =>\n                  person.name.toLowerCase().includes(query.toLowerCase())\n                )\n\n          if (virtual) {\n            return (\n              <Combobox\n                virtual={{\n                  options: filteredPeople,\n                  disabled: (person) => person?.disabled ?? false,\n                }}\n                value={value}\n                by=\"value\"\n                onChange={setValue}\n              >\n                <Combobox.Input onChange={(event) => setQuery(event.target.value)} />\n                <Combobox.Button>Trigger</Combobox.Button>\n                <Combobox.Options>\n                  {({ option }) => {\n                    return (\n                      <Combobox.Option {...(option as Option)} value={option}>\n                        {(option as Option).name}\n                      </Combobox.Option>\n                    )\n                  }}\n                </Combobox.Options>\n              </Combobox>\n            )\n          }\n\n          return (\n            <Combobox value={value} onChange={setValue} by=\"value\">\n              <Combobox.Input onChange={(event) => setQuery(event.target.value)} />\n              <Combobox.Button>Trigger</Combobox.Button>\n              <Combobox.Options>\n                {filteredPeople.map((person) => (\n                  <Combobox.Option key={person.value} {...person} value={person}>\n                    {person.name}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </Combobox>\n          )\n        }\n\n        it(\n          'should be possible to type a full word that has a perfect match',\n          suppressConsoleLogs(async () => {\n            render(\n              <Example\n                people={[\n                  { value: 'alice', name: 'alice', disabled: false },\n                  { value: 'bob', name: 'bob', disabled: false },\n                  { value: 'charlie', name: 'charlie', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bob'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('alice'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('charlie'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to type a partial of a word',\n          suppressConsoleLogs(async () => {\n            render(\n              <Example\n                people={[\n                  { value: 'alice', name: 'alice', disabled: false },\n                  { value: 'bob', name: 'bob', disabled: false },\n                  { value: 'charlie', name: 'charlie', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bo'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('ali'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('char'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to type words with spaces',\n          suppressConsoleLogs(async () => {\n            render(\n              <Example\n                people={[\n                  { value: 'alice', name: 'alice jones', disabled: false },\n                  { value: 'bob', name: 'bob the builder', disabled: false },\n                  { value: 'charlie', name: 'charlie bit me', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bob t'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob the builder')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('alice j'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice jones')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('charlie b'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie bit me')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to search and activate a disabled option',\n          suppressConsoleLogs(async () => {\n            render(\n              <Example\n                people={[\n                  { value: 'alice', name: 'alice', disabled: false },\n                  { value: 'bob', name: 'bob', disabled: true },\n                  { value: 'charlie', name: 'charlie', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We should not be able to go to the disabled option\n            await type(word('bo'))\n            await press(Keys.Home)\n\n            assertNoActiveComboboxOption()\n            assertNoSelectedComboboxOption()\n          })\n        )\n\n        it(\n          'should maintain activeIndex and activeOption when filtering',\n          suppressConsoleLogs(async () => {\n            render(\n              <Example\n                people={[\n                  { value: 'a', name: 'person a', disabled: false },\n                  { value: 'b', name: 'person b', disabled: false },\n                  { value: 'c', name: 'person c', disabled: false },\n                ]}\n              />\n            )\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            options = getComboboxOptions()\n            expect(options[0]).toHaveTextContent('person a')\n            assertActiveComboboxOption(options[0])\n\n            await press(Keys.ArrowDown)\n\n            // Person B should be active\n            options = getComboboxOptions()\n            expect(options[1]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[1])\n\n            // Filter more, remove `person a`\n            await type(word('person b'))\n            options = getComboboxOptions()\n            expect(options[0]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[0])\n\n            // Filter less, insert `person a` before `person b`\n            await type(word('person'))\n            options = getComboboxOptions()\n            expect(options[1]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[1])\n          })\n        )\n      })\n    })\n  }\n)\n\ndescribe.each([{ virtual: true }, { virtual: false }])('Mouse interactions %s', ({ virtual }) => {\n  let data = ['Option A', 'Option B', 'Option C']\n  function MyCombobox<T>({\n    options = data.slice() as T[],\n    label = true,\n    comboboxProps = {},\n    inputProps = {},\n    buttonProps = {},\n    optionProps = {},\n    optionsProps = {},\n  }: {\n    options?: T[]\n    label?: boolean\n    comboboxProps?: Record<string, any>\n    inputProps?: Record<string, any>\n    buttonProps?: Record<string, any>\n    optionProps?: Record<string, any>\n    optionsProps?: Record<string, any>\n  }) {\n    function isDisabled(option: T): boolean {\n      return typeof option === 'string'\n        ? false\n        : typeof option === 'object' && option !== null && 'disabled' in option\n          ? (option?.disabled as boolean | undefined) ?? false\n          : false\n    }\n    if (virtual) {\n      return (\n        <Combobox\n          virtual={{\n            options,\n            disabled: isDisabled,\n          }}\n          value={'test' as T}\n          onChange={NOOP}\n          {...comboboxProps}\n        >\n          {label && <Combobox.Label>Label</Combobox.Label>}\n          <Combobox.Input onChange={NOOP} {...inputProps} />\n          <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n          <Combobox.Options {...optionsProps}>\n            {({ option }) => {\n              return <Combobox.Option {...optionProps} value={option} />\n            }}\n          </Combobox.Options>\n        </Combobox>\n      )\n    }\n\n    return (\n      <Combobox value=\"test\" onChange={NOOP} {...comboboxProps}>\n        {label && <Combobox.Label>Label</Combobox.Label>}\n        <Combobox.Input onChange={NOOP} {...inputProps} />\n        <Combobox.Button {...buttonProps}>Trigger</Combobox.Button>\n        <Combobox.Options {...optionsProps}>\n          {options.map((option, idx) => {\n            return (\n              <Combobox.Option\n                key={idx}\n                disabled={isDisabled(option)}\n                {...optionProps}\n                value={option}\n              />\n            )\n          })}\n        </Combobox.Options>\n      </Combobox>\n    )\n  }\n\n  it(\n    'should focus the Combobox.Input when we click the Combobox.Label',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getComboboxLabel())\n\n      // Ensure that the actual button is focused instead\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should not focus the Combobox.Input when we right click the Combobox.Label',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getComboboxLabel(), MouseButton.Right)\n\n      // Ensure that the body is still active\n      assertActiveElement(document.body)\n    })\n  )\n\n  it(\n    'should be possible to open the combobox by focusing the input with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ immediate: true }} label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertComboboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox by focusing the input with immediate mode disabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is invisible\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox by focusing the input with immediate mode enabled when button is disabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ immediate: true, disabled: true }} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is invisible\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a combobox on click with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ immediate: true }} />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to close a focused combobox on click with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ immediate: true }} />)\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox by focusing input\n      await focus(getComboboxInput())\n      assertActiveElement(getComboboxInput())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to open the combobox on click',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertComboboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox on right click',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Try to open the combobox\n      await click(getComboboxButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ value: undefined, disabled: true }} label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Try to open the combobox\n      await click(getComboboxButton())\n\n      // Verify it is still closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to open the combobox on click, and focus the selected option',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ value: 'Option B' }} label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n      // Verify that the second combobox option is active (because it is already selected)\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to close a combobox on click',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be a no-op when we click outside of a closed combobox',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Verify that the window is closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Click something that is not related to the combobox\n      await click(document.body)\n\n      // Should still be closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  // TODO: JSDOM doesn't quite work here\n  // Clicking outside on the body should fire a mousedown (which it does) and then change the active element (which it doesn't)\n  xit(\n    'should be possible to click outside of the combobox which should close the combobox',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <MyCombobox />\n          <div tabIndex={-1} data-test-focusable>\n            after\n          </div>\n        </>\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      // Click something that is not related to the combobox\n      await click(getByText('after'))\n\n      // Should be closed now\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getByText('after'))\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox on another combobox button which should close the current combobox and open the new combobox',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <MyCombobox />\n          <MyCombobox />\n        </div>\n      )\n\n      let [button1, button2] = getComboboxButtons()\n\n      // Click the first combobox button\n      await click(button1)\n      expect(getComboboxes()).toHaveLength(1) // Only 1 combobox should be visible\n\n      // Verify that the first input is focused\n      assertActiveElement(getComboboxInputs()[0])\n\n      // Click the second combobox button\n      await click(button2)\n\n      expect(getComboboxes()).toHaveLength(1) // Only 1 combobox should be visible\n\n      // Verify that the first input is focused\n      assertActiveElement(getComboboxInputs()[1])\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox which should close the combobox (even if we press the combobox button)',\n    suppressConsoleLogs(async () => {\n      let closeHandler = jest.fn()\n      render(<MyCombobox comboboxProps={{ onClose: closeHandler }} />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      // Click the combobox button again\n      expect(closeHandler).toHaveBeenCalledTimes(0)\n      await click(getComboboxButton())\n      expect(closeHandler).toHaveBeenCalledTimes(1)\n\n      // Should be closed now\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Verify the input is focused again\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox, on an element which is within a focusable element, which closes the combobox',\n    suppressConsoleLogs(async () => {\n      let focusFn = jest.fn()\n      render(\n        <div>\n          <MyCombobox inputProps={{ onFocus: focusFn }} />\n\n          <button id=\"btn\">\n            <span>Next</span>\n          </button>\n        </div>\n      )\n\n      // Click the combobox button\n      await click(getComboboxButton())\n\n      // Ensure the combobox is open\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      // Click the span inside the button\n      await click(getByText('Next'))\n\n      // Ensure the combobox is closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Ensure the outside button is focused\n      assertActiveElement(document.getElementById('btn'))\n\n      // Ensure that the focus button only got focus once (first click)\n      expect(focusFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active when using `static`',\n    suppressConsoleLogs(async () => {\n      if (virtual) return // Incompatible with virtual rendering\n\n      render(\n        <Combobox value=\"test\" onChange={(x) => console.log(x)}>\n          <Combobox.Input onChange={NOOP} />\n          <Combobox.Button>Trigger</Combobox.Button>\n          <Combobox.Options static>\n            <Combobox.Option value=\"alice\">alice</Combobox.Option>\n            <Combobox.Option value=\"bob\">bob</Combobox.Option>\n            <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n          </Combobox.Options>\n        </Combobox>\n      )\n\n      let options = getComboboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n    })\n  )\n\n  it(\n    'should make a combobox option active when you move the mouse over it',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the combobox option is already active',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      await mouseMove(options[1])\n\n      // Nothing should be changed\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the combobox option is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <MyCombobox\n          options={[\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ]}\n        />\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      await mouseMove(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to hover an option that is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <MyCombobox\n          options={[\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ]}\n        />\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n\n      // We should not have option 1 as the active option now\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to mouse leave an option and make it inactive',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNoActiveComboboxOption()\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      await mouseLeave(options[0])\n      assertNoActiveComboboxOption()\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n\n      await mouseLeave(options[2])\n      assertNoActiveComboboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave a disabled option and be a no-op',\n    suppressConsoleLogs(async () => {\n      render(\n        <MyCombobox\n          options={[\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ]}\n        />\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n      assertNotActiveComboboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to click a combobox option, which closes the combobox',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      function Example() {\n        let [value, setValue] = useState<string | undefined>(undefined)\n\n        return (\n          <MyCombobox\n            comboboxProps={{\n              value,\n              onChange(value: string | undefined) {\n                setValue(value)\n                handleChange(value)\n              },\n            }}\n          />\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      expect(handleChange).toHaveBeenCalledTimes(1)\n      expect(handleChange).toHaveBeenCalledWith('Option B')\n\n      // Verify the input is focused again\n      assertActiveElement(getComboboxInput())\n\n      // Open combobox again\n      await click(getComboboxButton())\n\n      // Verify the active option is the previously selected one\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n  )\n\n  it(\n    'should be possible to click a combobox option, which closes the combobox with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox comboboxProps={{ immediate: true }} />)\n\n      // Open combobox by focusing input\n      await focus(getComboboxInput())\n      assertActiveElement(getComboboxInput())\n\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click a disabled combobox option, which is a no-op',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      function Example() {\n        let [value, setValue] = useState<string | undefined>(undefined)\n\n        return (\n          <MyCombobox\n            comboboxProps={{\n              value,\n              onChange(value: string | undefined) {\n                setValue(value)\n                handleChange(value)\n              },\n            }}\n            options={[\n              { value: 'alice', children: 'Alice', disabled: false },\n              { value: 'bob', children: 'Bob', disabled: true },\n              { value: 'charlie', children: 'Charlie', disabled: false },\n            ]}\n          />\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should not be able to click the disabled option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertNotActiveComboboxOption(options[1])\n      assertActiveElement(getComboboxInput())\n      expect(handleChange).toHaveBeenCalledTimes(0)\n\n      // Close the combobox\n      await click(getComboboxButton())\n\n      // Open combobox again\n      await click(getComboboxButton())\n\n      options = getComboboxOptions()\n\n      // Verify the active option is not the disabled one\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible focus a combobox option, so that it becomes active',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string | undefined>(undefined)\n\n        return <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // Verify that the first item is active\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to focus the second option\n      await focus(options[1])\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to focus a combobox option which is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <MyCombobox\n          options={[\n            { value: 'alice', disabled: false, children: 'alice' },\n            { value: 'bob', disabled: true, children: 'bob' },\n            { value: 'charlie', disabled: false, children: 'charlie' },\n          ]}\n        />\n      )\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should not be able to focus the first option\n      await focus(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to hold the last active option',\n    suppressConsoleLogs(async () => {\n      render(<MyCombobox optionsProps={{ hold: true }} label={false} />)\n\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      await click(getComboboxButton())\n\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // Hover the first item\n      await mouseMove(options[0])\n\n      // Verify that the first combobox option is active\n      assertActiveComboboxOption(options[0])\n\n      // Focus the second item\n      await mouseMove(options[1])\n\n      // Verify that the second combobox option is active\n      assertActiveComboboxOption(options[1])\n\n      // Move the mouse off of the second combobox option\n      await mouseLeave(options[1])\n      await mouseMove(document.body)\n\n      // Verify that the second combobox option is still active\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should sync the input field correctly and reset it when resetting the value from outside (to null)',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string | null>('Option B')\n\n        return (\n          <>\n            <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n            <button onClick={() => setValue(null)}>reset</button>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify the input has the selected value\n      expect(getComboboxInput()?.value).toBe('Option B')\n\n      // Override the input by typing something\n      await type(word('test'), getComboboxInput())\n      expect(getComboboxInput()?.value).toBe('test')\n\n      // Reset from outside\n      await click(getByText('reset'))\n\n      // Verify the input is reset correctly\n      expect(getComboboxInput()?.value).toBe('')\n    })\n  )\n\n  it(\n    'should warn when changing the combobox from uncontrolled to controlled',\n    mockingConsoleLogs(async (spy) => {\n      function Example() {\n        let [value, setValue] = useState<string | undefined>(undefined)\n\n        return (\n          <>\n            <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n            <button onClick={() => setValue('bob')}>to controlled</button>\n          </>\n        )\n      }\n\n      // Render a uncontrolled combobox\n      render(<Example />)\n\n      // Change to an controlled combobox\n      await click(getByText('to controlled'))\n\n      // Make sure we get a warning\n      expect(spy).toHaveBeenCalledTimes(1)\n      expect(spy.mock.calls.map((args) => args[0])).toEqual([\n        'A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.',\n      ])\n\n      // Render a fresh uncontrolled combobox\n      render(<Example />)\n\n      // Change to an controlled combobox\n      await click(getByText('to controlled'))\n\n      // We shouldn't have gotten another warning as we do not want to warn on every render\n      expect(spy).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should warn when changing the combobox from controlled to uncontrolled',\n    mockingConsoleLogs(async (spy) => {\n      function Example() {\n        let [value, setValue] = useState<string | undefined>('bob')\n\n        return (\n          <>\n            <MyCombobox comboboxProps={{ value, onChange: setValue }} />\n            <button onClick={() => setValue(undefined)}>to uncontrolled</button>\n          </>\n        )\n      }\n\n      // Render a controlled combobox\n      render(<Example />)\n\n      // Change to an uncontrolled combobox\n      await click(getByText('to uncontrolled'))\n\n      // Make sure we get a warning\n      expect(spy).toHaveBeenCalledTimes(1)\n      expect(spy.mock.calls.map((args) => args[0])).toEqual([\n        'A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.',\n      ])\n\n      // Render a fresh controlled combobox\n      render(<Example />)\n\n      // Change to an uncontrolled combobox\n      await click(getByText('to uncontrolled'))\n\n      // We shouldn't have gotten another warning as we do not want to warn on every render\n      expect(spy).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)',\n    suppressConsoleLogs(async () => {\n      let people = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n\n      function Example() {\n        let [value, setValue] = useState<(typeof people)[number] | null>(people[1])\n\n        return (\n          <>\n            <MyCombobox\n              options={people}\n              comboboxProps={{ value, onChange: setValue }}\n              inputProps={{\n                displayValue: (person: (typeof people)[number]) => person?.name,\n              }}\n            />\n            <button onClick={() => setValue(null)}>reset</button>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify the input has the selected value\n      expect(getComboboxInput()?.value).toBe('Bob')\n\n      // Override the input by typing something\n      await type(word('test'), getComboboxInput())\n      expect(getComboboxInput()?.value).toBe('test')\n\n      // Reset from outside\n      await click(getByText('reset'))\n\n      // Verify the input is reset correctly\n      expect(getComboboxInput()?.value).toBe('')\n    })\n  )\n})\n\ndescribe('Multi-select', () => {\n  it(\n    'should be possible to pass multiple values to the Combobox component',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Combobox value={value} onChange={setValue} multiple>\n            <Combobox.Input onChange={() => {}} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify that we have an open combobox with multiple mode\n      assertCombobox({ state: ComboboxState.Visible, mode: ComboboxMode.Multiple })\n\n      // Verify that we have multiple selected combobox options\n      let options = getComboboxOptions()\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should make the first selected option the active item',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Combobox value={value} onChange={setValue} multiple>\n            <Combobox.Input onChange={() => {}} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify that bob is the active option\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n  )\n\n  it(\n    'should keep the combobox open when selecting an item via the keyboard',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Combobox value={value} onChange={setValue} multiple>\n            <Combobox.Input onChange={() => {}} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      // Verify that bob is the active option\n      await click(getComboboxOptions()[0])\n\n      // Verify that the combobox is still open\n      assertCombobox({ state: ComboboxState.Visible })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Combobox value={value} onChange={setValue} multiple>\n            <Combobox.Input onChange={() => {}} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              <Combobox.Option value=\"alice\">alice</Combobox.Option>\n              <Combobox.Option value=\"bob\">bob</Combobox.Option>\n              <Combobox.Option value=\"charlie\">charlie</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: false })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should reset the active option, if the active option gets unmounted',\n    suppressConsoleLogs(async () => {\n      let users = ['alice', 'bob', 'charlie']\n      function Example() {\n        let [value, setValue] = useState<string[]>([])\n\n        return (\n          <Combobox value={value} onChange={setValue} multiple>\n            <Combobox.Input onChange={() => {}} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Options>\n              {users\n                .filter((user) => !value.includes(user))\n                .map((user) => (\n                  <Combobox.Option key={user} value={user}>\n                    {user}\n                  </Combobox.Option>\n                ))}\n            </Combobox.Options>\n          </Combobox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // Go to the next option\n      await press(Keys.ArrowDown)\n      assertActiveComboboxOption(options[1])\n\n      // Select the option\n      await press(Keys.Enter)\n\n      // The active option is reset to the very first one\n      assertActiveComboboxOption(options[0])\n    })\n  )\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState(null)\n      return (\n        <div>\n          <Combobox form=\"my-form\" value={value} onChange={setValue} name=\"delivery\">\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Label>Pizza Delivery</Combobox.Label>\n            <Combobox.Options>\n              <Combobox.Option value=\"pickup\">Pickup</Combobox.Option>\n              <Combobox.Option value=\"home-delivery\">Home delivery</Combobox.Option>\n              <Combobox.Option value=\"dine-in\">Dine in</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n\n          <form\n            id=\"my-form\"\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <button>Submit</button>\n          </form>\n        </div>\n      )\n    }\n\n    render(<Example />)\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should be possible to submit a form with a value', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState(null)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Combobox value={value} onChange={setValue} name=\"delivery\">\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Label>Pizza Delivery</Combobox.Label>\n            <Combobox.Options>\n              <Combobox.Option value=\"pickup\">Pickup</Combobox.Option>\n              <Combobox.Option value=\"home-delivery\">Home delivery</Combobox.Option>\n              <Combobox.Option value=\"dine-in\">Dine in</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Open combobox again\n    await click(getComboboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Open combobox again\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should not submit the data if the Combobox is disabled', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState<string | null>('home-delivery')\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <Combobox value={value} onChange={setValue} name=\"delivery\" disabled>\n            <Combobox.Input onChange={NOOP} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Label>Pizza Delivery</Combobox.Label>\n            <Combobox.Options>\n              <Combobox.Option value=\"pickup\">Pickup</Combobox.Option>\n              <Combobox.Option value=\"home-delivery\">Home delivery</Combobox.Option>\n              <Combobox.Option value=\"dine-in\">Dine in</Combobox.Option>\n            </Combobox.Options>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it('should be possible to submit a form with a complex value object', async () => {\n    let submits = jest.fn()\n    let options = [\n      {\n        id: 1,\n        value: 'pickup',\n        label: 'Pickup',\n        extra: { info: 'Some extra info' },\n      },\n      {\n        id: 2,\n        value: 'home-delivery',\n        label: 'Home delivery',\n        extra: { info: 'Some extra info' },\n      },\n      {\n        id: 3,\n        value: 'dine-in',\n        label: 'Dine in',\n        extra: { info: 'Some extra info' },\n      },\n    ]\n\n    function Example() {\n      let [value, setValue] = useState<(typeof options)[number] | null>(options[0])\n\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Combobox value={value} onChange={setValue} name=\"delivery\">\n            <Combobox.Input onChange={(x) => console.log(x)} />\n            <Combobox.Button>Trigger</Combobox.Button>\n            <Combobox.Label>Pizza Delivery</Combobox.Label>\n            <Combobox.Options>\n              {options.map((option) => (\n                <Combobox.Option key={option.id} value={option}>\n                  {option.label}\n                </Combobox.Option>\n              ))}\n            </Combobox.Options>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '2'],\n      ['delivery[value]', 'home-delivery'],\n      ['delivery[label]', 'Home delivery'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n  })\n})\n\ndescribe('transitions', () => {\n  it(\n    'should be possible to close the Combobox when using the `transition` prop',\n    suppressConsoleLogs(async () => {\n      render(\n        <Combobox>\n          <ComboboxButton>Toggle</ComboboxButton>\n          <ComboboxInput />\n          <ComboboxOptions transition>\n            <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n            <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n            <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n          </ComboboxOptions>\n        </Combobox>\n      )\n\n      // Open the combobox\n      await click(getComboboxButton())\n\n      // Ensure the combobox is visible\n      assertCombobox({ state: ComboboxState.Visible })\n\n      // Close the combobox\n      await click(getComboboxButton())\n\n      // Wait for the transition to finish, and the combobox to close\n      await waitFor(() => {\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      })\n\n      // Ensure the input got the restored focus\n      assertActiveElement(getComboboxInput())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox/combobox.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { Virtualizer, useVirtualizer } from '@tanstack/react-virtual'\nimport React, {\n  Fragment,\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ElementType,\n  type MutableRefObject,\n  type FocusEvent as ReactFocusEvent,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useByComparator, type ByComparator } from '../../hooks/use-by-comparator'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useDefaultValue } from '../../hooks/use-default-value'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useElementSize } from '../../hooks/use-element-size'\nimport { useEvent } from '../../hooks/use-event'\nimport { useHandleToggle } from '../../hooks/use-handle-toggle'\nimport { useId } from '../../hooks/use-id'\nimport { useInertOthers } from '../../hooks/use-inert-others'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useOnDisappear } from '../../hooks/use-on-disappear'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport { Action as QuickReleaseAction, useQuickRelease } from '../../hooks/use-quick-release'\nimport { useRefocusableInput } from '../../hooks/use-refocusable-input'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useScrollLock } from '../../hooks/use-scroll-lock'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { useTreeWalker } from '../../hooks/use-tree-walker'\nimport { useWatch } from '../../hooks/use-watch'\nimport { useDisabled } from '../../internal/disabled'\nimport {\n  FloatingProvider,\n  useFloatingPanel,\n  useFloatingPanelProps,\n  useFloatingReference,\n  useResolvedAnchor,\n  type AnchorProps,\n} from '../../internal/floating'\nimport { FormFields } from '../../internal/form-fields'\nimport { Frozen, useFrozenData } from '../../internal/frozen'\nimport { useProvidedId } from '../../internal/id'\nimport { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'\nimport { stackMachines } from '../../machines/stack-machine'\nimport { useSlice } from '../../react-glue'\nimport type { EnsureArray, Props } from '../../types'\nimport { history } from '../../utils/active-element-history'\nimport { Focus } from '../../utils/calculate-active-index'\nimport { disposables } from '../../utils/disposables'\nimport * as DOM from '../../utils/dom'\nimport { match } from '../../utils/match'\nimport { isActiveElement } from '../../utils/owner'\nimport { isMobile } from '../../utils/platform'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { Keys } from '../keyboard'\nimport { Label, useLabelledBy, useLabels, type _internal_ComponentLabel } from '../label/label'\nimport { MouseButton } from '../mouse'\nimport { Portal } from '../portal/portal'\nimport {\n  ActionTypes,\n  ActivationTrigger,\n  ComboboxState,\n  ValueMode,\n  type ComboboxOptionDataRef,\n} from './combobox-machine'\nimport {\n  ComboboxContext,\n  useComboboxMachine,\n  useComboboxMachineContext,\n} from './combobox-machine-glue'\n\nlet ComboboxDataContext = createContext<{\n  value: unknown\n  defaultValue: unknown\n  disabled: boolean\n  invalid: boolean\n  mode: ValueMode\n  immediate: boolean\n\n  virtual: { options: unknown[]; disabled: (value: unknown) => boolean } | null\n  calculateIndex(value: unknown): number\n  compare(a: unknown, z: unknown): boolean\n  isSelected(value: unknown): boolean\n  onChange(value: unknown): void\n\n  __demoMode: boolean\n\n  optionsPropsRef: MutableRefObject<{\n    static: boolean\n    hold: boolean\n  }>\n} | null>(null)\nComboboxDataContext.displayName = 'ComboboxDataContext'\n\nfunction useData(component: string) {\n  let context = useContext(ComboboxDataContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Combobox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useData)\n    throw err\n  }\n  return context\n}\ntype _Data = ReturnType<typeof useData>\n\nlet VirtualContext = createContext<Virtualizer<any, any> | null>(null)\n\nfunction VirtualProvider(props: {\n  slot: OptionsRenderPropArg\n  children: (data: { option: unknown; open: boolean }) => React.ReactElement\n}) {\n  let machine = useComboboxMachineContext('VirtualProvider')\n  let data = useData('VirtualProvider')\n  let { options } = data.virtual!\n\n  let optionsElement = useSlice(machine, (state) => state.optionsElement)\n\n  let [paddingStart, paddingEnd] = useMemo(() => {\n    let el = optionsElement\n    if (!el) return [0, 0]\n\n    let styles = window.getComputedStyle(el)\n\n    return [\n      parseFloat(styles.paddingBlockStart || styles.paddingTop),\n      parseFloat(styles.paddingBlockEnd || styles.paddingBottom),\n    ]\n  }, [optionsElement])\n\n  let virtualizer = useVirtualizer({\n    enabled: options.length !== 0,\n    scrollPaddingStart: paddingStart,\n    scrollPaddingEnd: paddingEnd,\n    count: options.length,\n    estimateSize() {\n      return 40\n    },\n    getScrollElement() {\n      return machine.state.optionsElement\n    },\n    overscan: 12,\n  })\n\n  let [baseKey, setBaseKey] = useState(0)\n  useIsoMorphicEffect(() => {\n    setBaseKey((v) => v + 1)\n  }, [options])\n\n  let items = virtualizer.getVirtualItems()\n\n  let isPointerActivationTrigger = useSlice(machine, (state) => {\n    return state.activationTrigger === ActivationTrigger.Pointer\n  })\n  let activeOptionIndex = useSlice(machine, machine.selectors.activeOptionIndex)\n\n  if (items.length === 0) {\n    return null\n  }\n\n  return (\n    <VirtualContext.Provider value={virtualizer}>\n      <div\n        style={{\n          position: 'relative',\n          width: '100%',\n          height: `${virtualizer.getTotalSize()}px`,\n        }}\n        ref={(el) => {\n          if (!el) return\n\n          // Do not scroll when the mouse/pointer is being used\n          if (isPointerActivationTrigger) return\n\n          // Scroll to the active index\n          if (activeOptionIndex !== null && options.length > activeOptionIndex) {\n            virtualizer.scrollToIndex(activeOptionIndex)\n          }\n        }}\n      >\n        {items.map((item) => {\n          return (\n            <Fragment key={item.key}>\n              {React.cloneElement(\n                props.children?.({\n                  ...props.slot,\n                  option: options[item.index],\n                }),\n                {\n                  key: `${baseKey}-${item.key}`,\n                  'data-index': item.index,\n                  'aria-setsize': options.length,\n                  'aria-posinset': item.index + 1,\n                  style: {\n                    position: 'absolute',\n                    top: 0,\n                    left: 0,\n                    transform: `translateY(${item.start}px)`,\n                    overflowAnchor: 'none',\n                  },\n                }\n              )}\n            </Fragment>\n          )\n        })}\n      </div>\n    </VirtualContext.Provider>\n  )\n}\n\n// ---\n\nlet DEFAULT_COMBOBOX_TAG = Fragment\ntype ComboboxRenderPropArg<TValue, TActive = TValue> = {\n  open: boolean\n  disabled: boolean\n  invalid: boolean\n  activeIndex: number | null\n  activeOption: TActive | null\n  value: TValue\n}\n\nexport type ComboboxProps<\n  TValue,\n  TMultiple extends boolean | undefined,\n  TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG,\n> = Props<\n  TTag,\n  ComboboxRenderPropArg<NoInfer<TValue>>,\n  'value' | 'defaultValue' | 'multiple' | 'onChange' | 'by',\n  {\n    value?: TMultiple extends true ? EnsureArray<TValue> : TValue\n    defaultValue?: TMultiple extends true ? EnsureArray<NoInfer<TValue>> : NoInfer<TValue>\n\n    onChange?: (value: TMultiple extends true ? EnsureArray<TValue> : TValue | null) => void\n    by?: ByComparator<\n      TMultiple extends true ? EnsureArray<NoInfer<TValue>>[number] : NoInfer<TValue>\n    >\n\n    /** @deprecated The `<Combobox />` is now nullable default */\n    nullable?: boolean\n\n    multiple?: TMultiple\n    disabled?: boolean\n    invalid?: boolean\n    form?: string\n    name?: string\n    immediate?: boolean\n    virtual?: {\n      options: TMultiple extends true ? EnsureArray<NoInfer<TValue>> : NoInfer<TValue>[]\n      disabled?: (\n        value: TMultiple extends true ? EnsureArray<NoInfer<TValue>>[number] : NoInfer<TValue>\n      ) => boolean\n    } | null\n\n    onClose?: () => void\n\n    __demoMode?: boolean\n  }\n>\n\nfunction ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG>(\n  props: ComboboxProps<TValue, boolean | undefined, TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let id = useId()\n\n  let providedDisabled = useDisabled()\n  let {\n    value: controlledValue,\n    defaultValue: _defaultValue,\n    onChange: controlledOnChange,\n    form,\n    name,\n    by,\n    invalid = false,\n    disabled = providedDisabled || false,\n    onClose: theirOnClose,\n    __demoMode = false,\n    multiple = false,\n    immediate = false,\n    virtual = null,\n    // Deprecated, but let's pluck it from the props such that it doesn't end up\n    // on the `Fragment`\n    nullable: _nullable,\n    ...theirProps\n  } = props\n  let defaultValue = useDefaultValue(_defaultValue)\n  let [value = multiple ? [] : undefined, theirOnChange] = useControllable<any>(\n    controlledValue,\n    controlledOnChange,\n    defaultValue\n  )\n\n  let machine = useComboboxMachine({ id, virtual, __demoMode })\n\n  let optionsPropsRef = useRef<_Data['optionsPropsRef']['current']>({ static: false, hold: false })\n\n  type TActualValue = true extends typeof multiple ? EnsureArray<TValue>[number] : TValue\n  let compare = useByComparator<TActualValue>(by)\n\n  let calculateIndex = useEvent((value: TValue) => {\n    if (virtual) {\n      if (by === null) {\n        return virtual.options.indexOf(value)\n      } else {\n        return virtual.options.findIndex((other) => compare(other, value))\n      }\n    } else {\n      return machine.state.options.findIndex((other) => compare(other.dataRef.current.value, value))\n    }\n  })\n\n  let isSelected: (value: TValue) => boolean = useCallback(\n    (other) => {\n      return match(data.mode, {\n        [ValueMode.Multi]: () => {\n          return (value as EnsureArray<TValue>).some((option) => compare(option, other))\n        },\n        [ValueMode.Single]: () => compare(value as TValue, other),\n      })\n    },\n    [value]\n  )\n\n  let virtualSlice = useSlice(machine, (state) => state.virtual)\n  let onClose = useEvent(() => theirOnClose?.())\n  let data = useMemo<_Data>(\n    () => ({\n      __demoMode,\n      immediate,\n      optionsPropsRef,\n      value,\n      defaultValue,\n      disabled,\n      invalid,\n      mode: multiple ? ValueMode.Multi : ValueMode.Single,\n      virtual: virtual ? virtualSlice : null,\n      onChange: theirOnChange,\n      isSelected,\n      calculateIndex,\n      compare,\n      onClose,\n    }),\n    [\n      __demoMode,\n      immediate,\n      optionsPropsRef,\n      value,\n      defaultValue,\n      disabled,\n      invalid,\n      multiple,\n      virtual,\n      virtualSlice,\n      theirOnChange,\n      isSelected,\n      calculateIndex,\n      compare,\n      onClose,\n    ]\n  )\n\n  useIsoMorphicEffect(() => {\n    if (!virtual) return\n    machine.send({\n      type: ActionTypes.UpdateVirtualConfiguration,\n      options: virtual.options,\n      disabled: virtual.disabled ?? null,\n    })\n  }, [virtual, virtual?.options, virtual?.disabled])\n\n  useIsoMorphicEffect(() => {\n    machine.state.dataRef.current = data\n  }, [data])\n\n  let [comboboxState, buttonElement, inputElement, optionsElement] = useSlice(machine, (state) => [\n    state.comboboxState,\n    state.buttonElement,\n    state.inputElement,\n    state.optionsElement,\n  ])\n\n  let stackMachine = stackMachines.get(null)\n  let isTopLayer = useSlice(\n    stackMachine,\n    useCallback((state) => stackMachine.selectors.isTop(state, id), [stackMachine, id])\n  )\n\n  // Handle outside click\n  useOutsideClick(isTopLayer, [buttonElement, inputElement, optionsElement], () =>\n    machine.actions.closeCombobox()\n  )\n\n  let activeOptionIndex = useSlice(machine, machine.selectors.activeOptionIndex)\n  let activeOption = useSlice(machine, machine.selectors.activeOption)\n\n  let slot = useSlot<ComboboxRenderPropArg<unknown>>({\n    open: comboboxState === ComboboxState.Open,\n    disabled,\n    invalid,\n    activeIndex: activeOptionIndex,\n    activeOption,\n    value,\n  })\n\n  let [labelledby, LabelProvider] = useLabels()\n\n  let ourProps = ref === null ? {} : { ref }\n\n  let reset = useCallback(() => {\n    if (defaultValue === undefined) return\n    return theirOnChange?.(defaultValue)\n  }, [theirOnChange, defaultValue])\n\n  let render = useRender()\n\n  return (\n    <LabelProvider\n      value={labelledby}\n      props={{\n        htmlFor: inputElement?.id,\n      }}\n      slot={{\n        open: comboboxState === ComboboxState.Open,\n        disabled,\n      }}\n    >\n      <FloatingProvider>\n        <ComboboxDataContext.Provider value={data}>\n          <ComboboxContext.Provider value={machine}>\n            <OpenClosedProvider\n              value={match(comboboxState, {\n                [ComboboxState.Open]: State.Open,\n                [ComboboxState.Closed]: State.Closed,\n              })}\n            >\n              {name != null && (\n                <FormFields\n                  disabled={disabled}\n                  data={value != null ? { [name]: value } : {}}\n                  form={form}\n                  onReset={reset}\n                />\n              )}\n              {render({\n                ourProps,\n                theirProps,\n                slot,\n                defaultTag: DEFAULT_COMBOBOX_TAG,\n                name: 'Combobox',\n              })}\n            </OpenClosedProvider>\n          </ComboboxContext.Provider>\n        </ComboboxDataContext.Provider>\n      </FloatingProvider>\n    </LabelProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_INPUT_TAG = 'input' as const\ntype InputRenderPropArg = {\n  open: boolean\n  disabled: boolean\n  invalid: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n}\ntype InputPropsWeControl =\n  | 'aria-activedescendant'\n  | 'aria-autocomplete'\n  | 'aria-controls'\n  | 'aria-expanded'\n  | 'aria-labelledby'\n  | 'disabled'\n  | 'role'\n\nexport type ComboboxInputProps<\n  TTag extends ElementType = typeof DEFAULT_INPUT_TAG,\n  TType = string,\n> = Props<\n  TTag,\n  InputRenderPropArg,\n  InputPropsWeControl,\n  {\n    defaultValue?: TType\n    disabled?: boolean\n    displayValue?: (item: TType) => string\n    onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void\n    autoFocus?: boolean\n  }\n>\n\nfunction InputFn<\n  TTag extends ElementType = typeof DEFAULT_INPUT_TAG,\n  // TODO: One day we will be able to infer this type from the generic in Combobox itself.\n  // But today is not that day..\n  TType = Parameters<typeof ComboboxRoot>[0]['value'],\n>(props: ComboboxInputProps<TTag, TType>, ref: Ref<HTMLInputElement>) {\n  let machine = useComboboxMachineContext('Combobox.Input')\n  let data = useData('Combobox.Input')\n\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let {\n    id = providedId || `headlessui-combobox-input-${internalId}`,\n    onChange,\n    displayValue,\n    disabled = data.disabled || false,\n    autoFocus = false,\n    // @ts-ignore: We know this MAY NOT exist for a given tag but we only care when it _does_ exist.\n    type = 'text',\n    ...theirProps\n  } = props\n\n  let internalInputRef = useRef<HTMLInputElement | null>(null)\n  let inputRef = useSyncRefs(\n    internalInputRef,\n    ref,\n    useFloatingReference(),\n    machine.actions.setInputElement\n  )\n\n  let [comboboxState, isTyping] = useSlice(machine, (state) => [\n    state.comboboxState,\n    state.isTyping,\n  ])\n\n  let d = useDisposables()\n\n  let clear = useEvent(() => {\n    machine.actions.onChange(null)\n    if (machine.state.optionsElement) {\n      machine.state.optionsElement.scrollTop = 0\n    }\n    machine.actions.goToOption({ focus: Focus.Nothing })\n  })\n\n  // When a `displayValue` prop is given, we should use it to transform the current selected\n  // option(s) so that the format can be chosen by developers implementing this. This is useful if\n  // your data is an object and you just want to pick a certain property or want to create a dynamic\n  // value like `firstName + ' ' + lastName`.\n  //\n  // Note: This can also be used with multiple selected options, but this is a very simple transform\n  // which should always result in a string (since we are filling in the value of the text input),\n  // you don't have to use this at all, a more common UI is a \"tag\" based UI, which you can render\n  // yourself using the selected option(s).\n  let currentDisplayValue = useMemo(() => {\n    if (typeof displayValue === 'function' && data.value !== undefined) {\n      return displayValue(data.value as TType) ?? ''\n    } else if (typeof data.value === 'string') {\n      return data.value\n    } else {\n      return ''\n    }\n  }, [data.value, displayValue])\n\n  // Syncing the input value has some rules attached to it to guarantee a smooth and expected user\n  // experience:\n  //\n  // - When a user is not typing in the input field, it is safe to update the input value based on\n  //   the selected option(s). See `currentDisplayValue` computation from above.\n  // - The value can be updated when:\n  //   - The `value` is set from outside of the component\n  //   - The `value` is set when the user uses their keyboard (confirm via enter or space)\n  //   - The `value` is set when the user clicks on a value to select it\n  // - The value will be reset to the current selected option(s), when:\n  //   - The user is _not_ typing (otherwise you will loose your current state / query)\n  //   - The user cancels the current changes:\n  //     - By pressing `escape`\n  //     - By clicking `outside` of the Combobox\n  useWatch(\n    ([currentDisplayValue, state], [oldCurrentDisplayValue, oldState]) => {\n      // When the user is typing, we want to not touch the `input` at all. Especially when they are\n      // using an IME, we don't want to mess with the input at all.\n      if (machine.state.isTyping) return\n\n      let input = internalInputRef.current\n      if (!input) return\n\n      if (oldState === ComboboxState.Open && state === ComboboxState.Closed) {\n        input.value = currentDisplayValue\n      } else if (currentDisplayValue !== oldCurrentDisplayValue) {\n        input.value = currentDisplayValue\n      }\n\n      // Once we synced the input value, we want to make sure the cursor is at the end of the input\n      // field. This makes it easier to continue typing and append to the query. We will bail out if\n      // the user is currently typing, because we don't want to mess with the cursor position while\n      // typing.\n      requestAnimationFrame(() => {\n        if (machine.state.isTyping) return\n        if (!input) return\n\n        // Bail when the input is not the currently focused element. When it is not the focused\n        // element, and we call the `setSelectionRange`, then it will become the focused\n        // element which may be unwanted.\n        if (isActiveElement(input)) return\n\n        let { selectionStart, selectionEnd } = input\n\n        // A custom selection is used, no need to move the caret\n        if (Math.abs((selectionEnd ?? 0) - (selectionStart ?? 0)) !== 0) return\n\n        // A custom caret position is used, no need to move the caret\n        if (selectionStart !== 0) return\n\n        // Move the caret to the end\n        input.setSelectionRange(input.value.length, input.value.length)\n      })\n    },\n    [currentDisplayValue, comboboxState, isTyping]\n  )\n\n  // Trick VoiceOver in behaving a little bit better. Manually \"resetting\" the input makes VoiceOver\n  // a bit more happy and doesn't require some changes manually first before announcing items\n  // correctly. This is a bit of a hacks, but it is a workaround for a VoiceOver bug.\n  //\n  // TODO: VoiceOver is still relatively buggy if you start VoiceOver while the Combobox is already\n  // in an open state.\n  useWatch(\n    ([newState], [oldState]) => {\n      if (newState === ComboboxState.Open && oldState === ComboboxState.Closed) {\n        // When the user is typing, we want to not touch the `input` at all. Especially when they are\n        // using an IME, we don't want to mess with the input at all.\n        if (machine.state.isTyping) return\n\n        let input = internalInputRef.current\n        if (!input) return\n\n        // Capture current state\n        let currentValue = input.value\n        let { selectionStart, selectionEnd, selectionDirection } = input\n\n        // Trick VoiceOver into announcing the value\n        input.value = ''\n\n        // Rollback to original state\n        input.value = currentValue\n        if (selectionDirection !== null) {\n          input.setSelectionRange(selectionStart, selectionEnd, selectionDirection)\n        } else {\n          input.setSelectionRange(selectionStart, selectionEnd)\n        }\n      }\n    },\n    [comboboxState]\n  )\n\n  let isComposing = useRef(false)\n  let handleCompositionStart = useEvent(() => {\n    isComposing.current = true\n  })\n  let handleCompositionEnd = useEvent(() => {\n    d.nextFrame(() => {\n      isComposing.current = false\n    })\n  })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLInputElement>) => {\n    machine.actions.setIsTyping(true)\n\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n      case Keys.Enter:\n        if (machine.state.comboboxState !== ComboboxState.Open) return\n\n        // When the user is still in the middle of composing by using an IME, then we don't want to\n        // submit this value and close the Combobox yet. Instead, we will fallback to the default\n        // behavior which is to \"end\" the composition.\n        if (isComposing.current) return\n\n        event.preventDefault()\n        event.stopPropagation()\n\n        if (machine.selectors.activeOptionIndex(machine.state) === null) {\n          machine.actions.closeCombobox()\n          return\n        }\n\n        machine.actions.selectActiveOption()\n        if (data.mode === ValueMode.Single) {\n          machine.actions.closeCombobox()\n        }\n        break\n\n      case Keys.ArrowDown:\n        event.preventDefault()\n        event.stopPropagation()\n\n        return match(machine.state.comboboxState, {\n          [ComboboxState.Open]: () => machine.actions.goToOption({ focus: Focus.Next }),\n          [ComboboxState.Closed]: () => machine.actions.openCombobox(),\n        })\n\n      case Keys.ArrowUp:\n        event.preventDefault()\n        event.stopPropagation()\n        return match(machine.state.comboboxState, {\n          [ComboboxState.Open]: () => machine.actions.goToOption({ focus: Focus.Previous }),\n          [ComboboxState.Closed]: () => {\n            flushSync(() => machine.actions.openCombobox())\n            if (!data.value) machine.actions.goToOption({ focus: Focus.Last })\n          },\n        })\n\n      case Keys.Home:\n        if (machine.state.comboboxState === ComboboxState.Closed) {\n          break\n        }\n\n        if (event.shiftKey) {\n          break\n        }\n\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.First })\n\n      case Keys.PageUp:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.First })\n\n      case Keys.End:\n        if (machine.state.comboboxState === ComboboxState.Closed) {\n          break\n        }\n\n        if (event.shiftKey) {\n          break\n        }\n\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.Last })\n\n      case Keys.PageDown:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.Last })\n\n      case Keys.Escape:\n        if (machine.state.comboboxState !== ComboboxState.Open) return\n        event.preventDefault()\n        if (machine.state.optionsElement && !data.optionsPropsRef.current.static) {\n          event.stopPropagation()\n        }\n\n        if (data.mode === ValueMode.Single) {\n          // We want to clear the value when the user presses escape if and only if the current\n          // value is not set (aka, they didn't select anything yet, or they cleared the input which\n          // caused the value to be set to `null`). If the current value is set, then we want to\n          // fallback to that value when we press escape (this part is handled in the watcher that\n          // syncs the value with the input field again).\n          if (data.value === null) {\n            clear()\n          }\n        }\n\n        return machine.actions.closeCombobox()\n\n      case Keys.Tab:\n        machine.actions.setIsTyping(false)\n        if (machine.state.comboboxState !== ComboboxState.Open) return\n        if (\n          data.mode === ValueMode.Single &&\n          machine.state.activationTrigger !== ActivationTrigger.Focus\n        ) {\n          machine.actions.selectActiveOption()\n        }\n        machine.actions.closeCombobox()\n        break\n    }\n  })\n\n  let handleChange = useEvent((event: React.ChangeEvent<HTMLInputElement>) => {\n    // Always call the onChange listener even if the user is still typing using an IME (Input Method\n    // Editor).\n    //\n    // The main issue is Android, where typing always uses the IME APIs. Just waiting until the\n    // compositionend event is fired to trigger an onChange is not enough, because then filtering\n    // options while typing won't work at all because we are still in \"composing\" mode.\n    onChange?.(event)\n\n    // When the value becomes empty in a single value mode then we want to clear\n    // the option entirely.\n    //\n    // This is can happen when you press backspace, but also when you select all the text and press\n    // ctrl/cmd+x.\n    if (data.mode === ValueMode.Single && event.target.value === '') {\n      clear()\n    }\n\n    // Open the combobox to show the results based on what the user has typed\n    machine.actions.openCombobox()\n  })\n\n  let handleBlur = useEvent((event: ReactFocusEvent) => {\n    let relatedTarget =\n      (event.relatedTarget as HTMLElement) ?? history.find((x) => x !== event.currentTarget)\n\n    // Focus is moved into the list, we don't want to close yet.\n    if (machine.state.optionsElement?.contains(relatedTarget)) return\n\n    // Focus is moved to the button, we don't want to close yet.\n    if (machine.state.buttonElement?.contains(relatedTarget)) return\n\n    // Focus is moved, but the combobox is not open. This can mean two things:\n    //\n    // 1. The combobox was never opened, so we don't have to do anything.\n    // 2. The combobox was closed and focus was moved already. At that point we\n    //    don't need to try and select the active option.\n    if (machine.state.comboboxState !== ComboboxState.Open) return\n\n    event.preventDefault()\n\n    // We want to clear the value when the user presses escape or clicks outside\n    // the combobox if and only if the current value is not set (aka, they\n    // didn't select anything yet, or they cleared the input which caused the\n    // value to be set to `null`). If the current value is set, then we want to\n    // fallback to that value when we press escape (this part is handled in the\n    // watcher that syncs the value with the input field again).\n    if (data.mode === ValueMode.Single && data.value === null) {\n      clear()\n    }\n\n    return machine.actions.closeCombobox()\n  })\n\n  let handleFocus = useEvent((event: ReactFocusEvent) => {\n    let relatedTarget =\n      (event.relatedTarget as HTMLElement) ?? history.find((x) => x !== event.currentTarget)\n    if (machine.state.buttonElement?.contains(relatedTarget)) return\n    if (machine.state.optionsElement?.contains(relatedTarget)) return\n    if (data.disabled) return\n\n    if (!data.immediate) return\n    if (machine.state.comboboxState === ComboboxState.Open) return\n\n    // In a scenario where you have this setup:\n    //\n    // ```ts\n    // {condition && (\n    //   <Combobox immediate>\n    //     <ComboboxInput autoFocus />\n    //   </Combobox>\n    // )}\n    // ```\n    //\n    // Then we will trigger the `openCombobox` in a `flushSync`, but we are\n    // already in the middle of rendering. This will result in the following\n    // warning:\n    //\n    // ```\n    // Warning: flushSync was called from inside a lifecycle method. React\n    // cannot flush when React is already rendering. Consider moving this call\n    // to a scheduler task or micro task.\n    // ```\n    //\n    // Which is why we wrap this in a `microTask` to make sure we are not in the\n    // middle of rendering.\n    d.microTask(() => {\n      flushSync(() => machine.actions.openCombobox())\n\n      // We need to make sure that tabbing through a form doesn't result in\n      // incorrectly setting the value of the combobox. We will set the\n      // activation trigger to `Focus`, and we will ignore selecting the active\n      // option when the user tabs away.\n      machine.actions.setActivationTrigger(ActivationTrigger.Focus)\n    })\n  })\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let { isFocused: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let optionsElement = useSlice(machine, (state) => state.optionsElement)\n\n  let slot = useSlot<InputRenderPropArg>({\n    open: comboboxState === ComboboxState.Open,\n    disabled,\n    invalid: data.invalid,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let ourProps = mergeProps(\n    {\n      ref: inputRef,\n      id,\n      role: 'combobox',\n      type,\n      'aria-controls': optionsElement?.id,\n      'aria-expanded': comboboxState === ComboboxState.Open,\n      'aria-activedescendant': useSlice(machine, machine.selectors.activeDescendantId),\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      'aria-autocomplete': 'list',\n      defaultValue:\n        props.defaultValue ??\n        (data.defaultValue !== undefined ? displayValue?.(data.defaultValue as TType) : null) ??\n        data.defaultValue,\n      disabled: disabled || undefined,\n      autoFocus,\n      onCompositionStart: handleCompositionStart,\n      onCompositionEnd: handleCompositionEnd,\n      onKeyDown: handleKeyDown,\n      onChange: handleChange,\n      onFocus: handleFocus,\n      onBlur: handleBlur,\n    },\n    focusProps,\n    hoverProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_INPUT_TAG,\n    name: 'Combobox.Input',\n  })\n}\n\n// ---\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\ntype ButtonRenderPropArg = {\n  open: boolean\n  active: boolean\n  disabled: boolean\n  invalid: boolean\n  value: any\n  focus: boolean\n  hover: boolean\n}\ntype ButtonPropsWeControl =\n  | 'aria-controls'\n  | 'aria-expanded'\n  | 'aria-haspopup'\n  | 'aria-labelledby'\n  | 'disabled'\n  | 'tabIndex'\n\nexport type ComboboxButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    autoFocus?: boolean\n    disabled?: boolean\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: ComboboxButtonProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let machine = useComboboxMachineContext('Combobox.Button')\n  let data = useData('Combobox.Button')\n  let [localButtonElement, setLocalButtonElement] = useState<HTMLButtonElement | null>(null)\n  let buttonRef = useSyncRefs(ref, setLocalButtonElement, machine.actions.setButtonElement)\n\n  let internalId = useId()\n  let {\n    id = `headlessui-combobox-button-${internalId}`,\n    disabled = data.disabled || false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n\n  let [comboboxState, inputElement, optionsElement] = useSlice(machine, (state) => [\n    state.comboboxState,\n    state.inputElement,\n    state.optionsElement,\n  ])\n  let refocusInput = useRefocusableInput(inputElement)\n\n  let enableQuickRelease = comboboxState === ComboboxState.Open\n  useQuickRelease(enableQuickRelease, {\n    trigger: localButtonElement,\n    action: useCallback(\n      (e) => {\n        if (localButtonElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        if (inputElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        let option = e.target.closest('[role=\"option\"]:not([data-disabled])')\n        if (DOM.isHTMLElement(option)) {\n          return QuickReleaseAction.Select(option)\n        }\n\n        if (optionsElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        return QuickReleaseAction.Close\n      },\n      [localButtonElement, inputElement, optionsElement]\n    ),\n    close: machine.actions.closeCombobox,\n    select: machine.actions.selectActiveOption,\n  })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLElement>) => {\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n      case Keys.Space:\n      case Keys.Enter:\n        event.preventDefault()\n        event.stopPropagation()\n        if (machine.state.comboboxState === ComboboxState.Closed) {\n          flushSync(() => machine.actions.openCombobox())\n        }\n        refocusInput()\n        return\n\n      case Keys.ArrowDown:\n        event.preventDefault()\n        event.stopPropagation()\n        if (machine.state.comboboxState === ComboboxState.Closed) {\n          flushSync(() => machine.actions.openCombobox())\n          if (!machine.state.dataRef.current.value)\n            machine.actions.goToOption({ focus: Focus.First })\n        }\n        refocusInput()\n        return\n\n      case Keys.ArrowUp:\n        event.preventDefault()\n        event.stopPropagation()\n        if (machine.state.comboboxState === ComboboxState.Closed) {\n          flushSync(() => machine.actions.openCombobox())\n          if (!machine.state.dataRef.current.value) {\n            machine.actions.goToOption({ focus: Focus.Last })\n          }\n        }\n        refocusInput()\n        return\n\n      case Keys.Escape:\n        if (machine.state.comboboxState !== ComboboxState.Open) return\n        event.preventDefault()\n        if (machine.state.optionsElement && !data.optionsPropsRef.current.static) {\n          event.stopPropagation()\n        }\n        flushSync(() => machine.actions.closeCombobox())\n        refocusInput()\n        return\n\n      default:\n        return\n    }\n  })\n\n  let toggleProps = useHandleToggle(() => {\n    if (machine.state.comboboxState === ComboboxState.Open) {\n      machine.actions.closeCombobox()\n    } else {\n      machine.actions.openCombobox()\n    }\n\n    // Ensure we focus the input\n    refocusInput()\n  })\n\n  let labelledBy = useLabelledBy([id])\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<ButtonRenderPropArg>({\n    open: comboboxState === ComboboxState.Open,\n    active: active || comboboxState === ComboboxState.Open,\n    disabled,\n    invalid: data.invalid,\n    value: data.value,\n    hover,\n    focus,\n  })\n  let ourProps = mergeProps(\n    {\n      ref: buttonRef,\n      id,\n      type: useResolveButtonType(props, localButtonElement),\n      tabIndex: -1,\n      'aria-haspopup': 'listbox',\n      'aria-controls': optionsElement?.id,\n      'aria-expanded': comboboxState === ComboboxState.Open,\n      'aria-labelledby': labelledBy,\n      disabled: disabled || undefined,\n      autoFocus,\n      onKeyDown: handleKeyDown,\n    },\n    toggleProps,\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BUTTON_TAG,\n    name: 'Combobox.Button',\n  })\n}\n\n// ---\n\nlet DEFAULT_OPTIONS_TAG = 'div' as const\ntype OptionsRenderPropArg = {\n  open: boolean\n  option: any\n}\ntype OptionsPropsWeControl = 'aria-labelledby' | 'aria-multiselectable' | 'role' | 'tabIndex'\n\nlet OptionsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type ComboboxOptionsProps<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG> = Props<\n  TTag,\n  OptionsRenderPropArg,\n  OptionsPropsWeControl,\n  PropsForFeatures<typeof OptionsRenderFeatures> & {\n    hold?: boolean\n    anchor?: AnchorProps\n    portal?: boolean\n    modal?: boolean\n    transition?: boolean\n  }\n>\n\nfunction OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(\n  props: ComboboxOptionsProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-combobox-options-${internalId}`,\n    hold = false,\n    anchor: rawAnchor,\n    portal = false,\n    modal = true,\n    transition = false,\n    ...theirProps\n  } = props\n  let machine = useComboboxMachineContext('Combobox.Options')\n  let data = useData('Combobox.Options')\n  let anchor = useResolvedAnchor(rawAnchor)\n\n  // Always enable `portal` functionality, when `anchor` is enabled\n  if (anchor) {\n    portal = true\n  }\n\n  let [floatingRef, style] = useFloatingPanel(anchor)\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localOptionsElement, setLocalOptionsElement] = useState<HTMLElement | null>(null)\n\n  let getFloatingPanelProps = useFloatingPanelProps()\n  let optionsRef = useSyncRefs(\n    ref,\n    anchor ? floatingRef : null,\n    machine.actions.setOptionsElement,\n    setLocalOptionsElement\n  )\n  let [comboboxState, inputElement, buttonElement, optionsElement, activationTrigger] = useSlice(\n    machine,\n    (state) => [\n      state.comboboxState,\n      state.inputElement,\n      state.buttonElement,\n      state.optionsElement,\n      state.activationTrigger,\n    ]\n  )\n  let portalOwnerDocument = useOwnerDocument(inputElement || buttonElement)\n  let ownerDocument = useOwnerDocument(optionsElement)\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localOptionsElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : comboboxState === ComboboxState.Open\n  )\n\n  // Ensure we close the combobox as soon as the input becomes hidden\n  useOnDisappear(visible, inputElement, machine.actions.closeCombobox)\n\n  // Enable scroll locking when the combobox is visible, and `modal` is enabled\n  let scrollLockEnabled = data.__demoMode ? false : modal && comboboxState === ComboboxState.Open\n  useScrollLock(scrollLockEnabled, ownerDocument)\n\n  // Mark other elements as inert when the combobox is visible, and `modal` is enabled\n  let inertOthersEnabled = data.__demoMode ? false : modal && comboboxState === ComboboxState.Open\n  useInertOthers(inertOthersEnabled, {\n    allowed: useCallback(\n      () => [inputElement, buttonElement, optionsElement],\n      [inputElement, buttonElement, optionsElement]\n    ),\n  })\n\n  // We keep track whether the input moved or not, we only check this when the\n  // combobox state becomes closed. If the input moved, then we want to cancel\n  // pending transitions to prevent that the attached `ComboboxOptions` is still\n  // transitioning while the input visually moved away.\n  //\n  // If we don't cancel these transitions then there will be a period where the\n  // `ComboboxOptions` is visible and moving around because it is trying to\n  // re-position itself based on the new position.\n  let didInputMove = useSlice(machine, machine.selectors.didInputMove)\n\n  // Now that we know that the input did move or not, we can either disable the\n  // panel and all of its transitions, or rely on the `visible` state to hide\n  // the panel whenever necessary.\n  let panelEnabled = didInputMove ? false : visible\n\n  useIsoMorphicEffect(() => {\n    data.optionsPropsRef.current.static = props.static ?? false\n  }, [data.optionsPropsRef, props.static])\n  useIsoMorphicEffect(() => {\n    data.optionsPropsRef.current.hold = hold\n  }, [data.optionsPropsRef, hold])\n\n  useTreeWalker(comboboxState === ComboboxState.Open, {\n    container: optionsElement,\n    accept(node) {\n      if (node.getAttribute('role') === 'option') return NodeFilter.FILTER_REJECT\n      if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP\n      return NodeFilter.FILTER_ACCEPT\n    },\n    walk(node) {\n      node.setAttribute('role', 'none')\n    },\n  })\n\n  let labelledBy = useLabelledBy([buttonElement?.id])\n\n  let slot = useSlot<OptionsRenderPropArg>({\n    open: comboboxState === ComboboxState.Open,\n    option: undefined,\n  })\n\n  // When the user scrolls **using the mouse** (so scroll event isn't appropriate)\n  // we want to make sure that the current activation trigger is set to pointer.\n  let handleWheel = useEvent(() => {\n    machine.actions.setActivationTrigger(ActivationTrigger.Pointer)\n  })\n\n  let handleMouseDown = useEvent((event: ReactMouseEvent) => {\n    // When clicking inside of the scrollbar, a `click` event will be triggered\n    // on the focusable element _below_ the scrollbar. If you use a `<Combobox>`\n    // inside of a `<Dialog>`, clicking the scrollbar of the `<ComboboxOptions>`\n    // will move focus to the `<Dialog>` which blurs the `<ComboboxInput>` and\n    // closes the `<Combobox>`.\n    //\n    // Preventing the default behavior in the `mousedown` event (which happens\n    // before `click`) will prevent this issue because the `click` never fires.\n    event.preventDefault()\n\n    // When the user clicks in the `<Options/>`, we want to make sure that we\n    // set the activation trigger to `pointer` to prevent auto scrolling to the\n    // active option while the user is scrolling.\n    machine.actions.setActivationTrigger(ActivationTrigger.Pointer)\n  })\n\n  let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {\n    'aria-labelledby': labelledBy,\n    role: 'listbox',\n    'aria-multiselectable': data.mode === ValueMode.Multi ? true : undefined,\n    id,\n    ref: optionsRef,\n    style: {\n      ...theirProps.style,\n      ...style,\n      '--input-width': useElementSize(visible, inputElement, true).width,\n      '--button-width': useElementSize(visible, buttonElement, true).width,\n    } as CSSProperties,\n    onWheel: activationTrigger === ActivationTrigger.Pointer ? undefined : handleWheel,\n    onMouseDown: handleMouseDown,\n    ...transitionDataAttributes(transitionData),\n  })\n\n  // We should freeze when the combobox is visible but \"closed\". This means that\n  // a transition is currently happening and the component is still visible (for\n  // the transition) but closed from a functionality perspective.\n  //\n  // When the `static` prop is used, we should never freeze, because rendering\n  // is up to the user.\n  let shouldFreeze = visible && comboboxState === ComboboxState.Closed && !props.static\n\n  let options = useFrozenData(shouldFreeze, data.virtual?.options)\n\n  // Frozen state, the selected value will only update visually when the user re-opens the <Combobox />\n  let frozenValue = useFrozenData(shouldFreeze, data.value)\n\n  let isSelected = useCallback(\n    (compareValue: unknown) => data.compare(frozenValue, compareValue),\n    [data.compare, frozenValue]\n  )\n\n  // Map the children in a scrollable container when virtualization is enabled\n  let newDataContextValue = useMemo(() => {\n    if (!data.virtual) return data\n    if (options === undefined) throw new Error('Missing `options` in virtual mode')\n\n    return options !== data.virtual.options\n      ? { ...data, virtual: { ...data.virtual, options } }\n      : data\n  }, [data, options, data.virtual?.options])\n\n  if (data.virtual) {\n    Object.assign(theirProps, {\n      children: (\n        <ComboboxDataContext.Provider value={newDataContextValue}>\n          {/* @ts-expect-error The `children` prop now is a callback function that receives `{option}` */}\n          <VirtualProvider slot={slot}>{theirProps.children}</VirtualProvider>\n        </ComboboxDataContext.Provider>\n      ),\n    })\n  }\n\n  let render = useRender()\n\n  let newData = useMemo(() => {\n    return data.mode === ValueMode.Multi ? data : { ...data, isSelected }\n  }, [data, isSelected])\n\n  return (\n    <Portal enabled={portal ? props.static || visible : false} ownerDocument={portalOwnerDocument}>\n      <ComboboxDataContext.Provider value={newData}>\n        {render({\n          ourProps,\n          theirProps: {\n            ...theirProps,\n            children: (\n              <Frozen freeze={shouldFreeze}>\n                {typeof theirProps.children === 'function'\n                  ? theirProps.children?.(slot)\n                  : theirProps.children}\n              </Frozen>\n            ),\n          },\n          slot,\n          defaultTag: DEFAULT_OPTIONS_TAG,\n          features: OptionsRenderFeatures,\n          visible: panelEnabled,\n          name: 'Combobox.Options',\n        })}\n      </ComboboxDataContext.Provider>\n    </Portal>\n  )\n}\n\n// ---\n\nlet DEFAULT_OPTION_TAG = 'div' as const\ntype OptionRenderPropArg = {\n  focus: boolean\n  /** @deprecated use `focus` instead */\n  active: boolean\n  selected: boolean\n  disabled: boolean\n}\ntype OptionPropsWeControl = 'role' | 'tabIndex' | 'aria-disabled' | 'aria-selected'\n\nexport type ComboboxOptionProps<\n  TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n  TType = string,\n> = Props<\n  TTag,\n  OptionRenderPropArg,\n  OptionPropsWeControl,\n  {\n    disabled?: boolean\n    value: TType\n    order?: number\n  }\n>\n\nfunction OptionFn<\n  TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n  // TODO: One day we will be able to infer this type from the generic in Combobox itself.\n  // But today is not that day..\n  TType = Parameters<typeof ComboboxRoot>[0]['value'],\n>(props: ComboboxOptionProps<TTag, TType>, ref: Ref<HTMLElement>) {\n  let data = useData('Combobox.Option')\n  let machine = useComboboxMachineContext('Combobox.Option')\n\n  let internalId = useId()\n  let {\n    id = `headlessui-combobox-option-${internalId}`,\n    value,\n    disabled = data.virtual?.disabled?.(value) ?? false,\n    order = null,\n    ...theirProps\n  } = props\n\n  let [inputElement] = useSlice(machine, (state) => [state.inputElement])\n\n  let refocusInput = useRefocusableInput(inputElement)\n\n  let active = useSlice(\n    machine,\n    useCallback((state) => machine.selectors.isActive(state, value, id), [value, id])\n  )\n  let selected = data.isSelected(value)\n  let internalOptionRef = useRef<HTMLElement | null>(null)\n\n  let bag = useLatestValue<ComboboxOptionDataRef<TType>['current']>({\n    disabled,\n    value,\n    domRef: internalOptionRef,\n    order,\n  })\n\n  let virtualizer = useContext(VirtualContext)\n  let optionRef = useSyncRefs(\n    ref,\n    internalOptionRef,\n    virtualizer ? virtualizer.measureElement : null\n  )\n\n  let select = useEvent(() => {\n    machine.actions.setIsTyping(false)\n    machine.actions.onChange(value)\n  })\n  useIsoMorphicEffect(() => machine.actions.registerOption(id, bag), [bag, id])\n\n  let shouldScrollIntoView = useSlice(\n    machine,\n    useCallback((state) => machine.selectors.shouldScrollIntoView(state, value, id), [value, id])\n  )\n\n  useIsoMorphicEffect(() => {\n    if (!shouldScrollIntoView) return\n    return disposables().requestAnimationFrame(() => {\n      internalOptionRef.current?.scrollIntoView?.({ block: 'nearest' })\n    })\n  }, [shouldScrollIntoView, internalOptionRef])\n\n  let handleMouseDown = useEvent((event: ReactMouseEvent<HTMLButtonElement>) => {\n    // We use the `mousedown` event here since it fires before the focus event,\n    // allowing us to cancel the event before focus is moved from the\n    // `ComboboxInput` to the `ComboboxOption`. This keeps the input focused,\n    // preserving the cursor position and any text selection.\n    event.preventDefault()\n\n    // Since we're using the `mousedown` event instead of a `click` event here\n    // to preserve the focus of the `ComboboxInput`, we need to also check\n    // that the `left` mouse button was clicked.\n    if (event.button !== MouseButton.Left) {\n      return\n    }\n\n    if (disabled) return\n    select()\n\n    // We want to make sure that we don't accidentally trigger the virtual keyboard.\n    //\n    // This would happen if the input is focused, the options are open, you select an option (which\n    // would blur the input, and focus the option (button), then we re-focus the input).\n    //\n    // This would be annoying on mobile (or on devices with a virtual keyboard). Right now we are\n    // assuming that the virtual keyboard would open on mobile devices (iOS / Android). This\n    // assumption is not perfect, but will work in the majority of the cases.\n    //\n    // Ideally we can have a better check where we can explicitly check for the virtual keyboard.\n    // But right now this is still an experimental feature:\n    // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/virtualKeyboard\n    if (!isMobile()) {\n      requestAnimationFrame(() => refocusInput())\n    }\n\n    if (data.mode === ValueMode.Single) {\n      machine.actions.closeCombobox()\n    }\n  })\n\n  let handleFocus = useEvent(() => {\n    if (disabled) {\n      return machine.actions.goToOption({ focus: Focus.Nothing })\n    }\n    let idx = data.calculateIndex(value)\n    machine.actions.goToOption({ focus: Focus.Specific, idx })\n  })\n\n  let pointer = useTrackedPointer()\n\n  let handleEnter = useEvent((evt) => pointer.update(evt))\n\n  let handleMove = useEvent((evt) => {\n    if (!pointer.wasMoved(evt)) return\n    if (disabled) return\n\n    // Skip if the option is already active, however, if the activation trigger\n    // is not `Pointer` we have to convert it to a `Pointer` trigger\n    // activation instead.\n    if (active && machine.state.activationTrigger === ActivationTrigger.Pointer) return\n\n    let idx = data.calculateIndex(value)\n\n    // pointermove / mousemove will only be fired when the pointer is actually\n    // moving, therefore we can go to the option with the `Pointer` activation\n    // trigger.\n    machine.actions.goToOption({ focus: Focus.Specific, idx }, ActivationTrigger.Pointer)\n  })\n\n  let handleLeave = useEvent((evt) => {\n    if (!pointer.wasMoved(evt)) return\n    if (disabled) return\n    if (!active) return\n    if (data.optionsPropsRef.current.hold) return\n\n    // pointerenter / mouseenter will be fired when the mouse is on top of an\n    // element that scrolls into view even when using the keyboard to\n    // navigate. Only handle the event when the pointer was actually moved.\n    if (machine.state.activationTrigger !== ActivationTrigger.Pointer) return\n\n    machine.actions.goToOption({ focus: Focus.Nothing })\n  })\n\n  let slot = useSlot<OptionRenderPropArg>({\n    active,\n    focus: active,\n    selected,\n    disabled,\n  })\n\n  let ourProps = {\n    id,\n    ref: optionRef,\n    role: 'option',\n    tabIndex: disabled === true ? undefined : -1,\n    'aria-disabled': disabled === true ? true : undefined,\n    // According to the WAI-ARIA best practices, we should use aria-checked for\n    // multi-select,but Voice-Over disagrees. So we use aria-checked instead for\n    // both single and multi-select.\n    'aria-selected': selected,\n    disabled: undefined, // Never forward the `disabled` prop\n    onMouseDown: handleMouseDown,\n    onFocus: handleFocus,\n    onPointerEnter: handleEnter,\n    onMouseEnter: handleEnter,\n    onPointerMove: handleMove,\n    onMouseMove: handleMove,\n    onPointerLeave: handleLeave,\n    onMouseLeave: handleLeave,\n  }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_OPTION_TAG,\n    name: 'Combobox.Option',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentCombobox extends HasDisplayName {\n  <\n    TValue,\n    TMultiple extends boolean | undefined = false,\n    TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG,\n  >(\n    props: ComboboxProps<TValue, TMultiple, TTag> & RefProp<typeof ComboboxFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentComboboxButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: ComboboxButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentComboboxInput extends HasDisplayName {\n  <TType, TTag extends ElementType = typeof DEFAULT_INPUT_TAG>(\n    props: ComboboxInputProps<TTag, TType> & RefProp<typeof InputFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentComboboxLabel extends _internal_ComponentLabel {}\n\nexport interface _internal_ComponentComboboxOptions extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(\n    props: ComboboxOptionsProps<TTag> & RefProp<typeof OptionsFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentComboboxOption extends HasDisplayName {\n  <\n    TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n    TType = Parameters<typeof ComboboxRoot>[0]['value'],\n  >(\n    props: ComboboxOptionProps<TTag, TType> & RefProp<typeof OptionFn>\n  ): React.JSX.Element\n}\n\nlet ComboboxRoot = forwardRefWithAs(ComboboxFn) as _internal_ComponentCombobox\nexport let ComboboxButton = forwardRefWithAs(ButtonFn) as _internal_ComponentComboboxButton\nexport let ComboboxInput = forwardRefWithAs(InputFn) as _internal_ComponentComboboxInput\n/** @deprecated use `<Label>` instead of `<ComboboxLabel>` */\nexport let ComboboxLabel = Label as _internal_ComponentComboboxLabel\nexport let ComboboxOptions = forwardRefWithAs(OptionsFn) as _internal_ComponentComboboxOptions\nexport let ComboboxOption = forwardRefWithAs(OptionFn) as _internal_ComponentComboboxOption\n\nexport let Combobox = Object.assign(ComboboxRoot, {\n  /** @deprecated use `<ComboboxInput>` instead of `<Combobox.Input>` */\n  Input: ComboboxInput,\n  /** @deprecated use `<ComboboxButton>` instead of `<Combobox.Button>` */\n  Button: ComboboxButton,\n  /** @deprecated use `<Label>` instead of `<Combobox.Label>` */\n  Label: ComboboxLabel,\n  /** @deprecated use `<ComboboxOptions>` instead of `<Combobox.Options>` */\n  Options: ComboboxOptions,\n  /** @deprecated use `<ComboboxOption>` instead of `<Combobox.Option>` */\n  Option: ComboboxOption,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox-button/combobox-button.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../combobox/combobox'\nexport { ComboboxButton } from '../combobox/combobox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox-input/combobox-input.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../combobox/combobox'\nexport { ComboboxInput } from '../combobox/combobox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox-label/combobox-label.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../combobox/combobox'\nexport { ComboboxLabel } from '../combobox/combobox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox-option/combobox-option.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../combobox/combobox'\nexport { ComboboxOption } from '../combobox/combobox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/combobox-options/combobox-options.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../combobox/combobox'\nexport { ComboboxOptions } from '../combobox/combobox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/data-interactive/data-interactive.test.tsx",
    "content": "import { render, screen } from '@testing-library/react'\nimport React from 'react'\nimport { focus, mouseEnter } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { DataInteractive } from './data-interactive'\n\nit(\n  'should expose focus data attributes on the element',\n  suppressConsoleLogs(async () => {\n    render(\n      <DataInteractive>\n        <a href=\"alice\">Alice</a>\n      </DataInteractive>\n    )\n\n    let a = screen.getByText('Alice')\n\n    // Should not have the data attribute\n    expect(a).not.toHaveAttribute('data-focus')\n\n    // Focus on the element\n    await focus(a)\n\n    // Should be focused\n    expect(a).toHaveFocus()\n\n    // Should have the data attribute\n    expect(a).toHaveAttribute('data-focus')\n  })\n)\n\nit(\n  'should expose hover data attributes on the element',\n  suppressConsoleLogs(async () => {\n    render(\n      <DataInteractive>\n        <a href=\"alice\">Alice</a>\n      </DataInteractive>\n    )\n\n    let a = screen.getByText('Alice')\n\n    // Should not have the data attribute\n    expect(a).not.toHaveAttribute('data-hover')\n\n    // Hover on the element\n    await mouseEnter(a)\n\n    // Should have the data attribute\n    expect(a).toHaveAttribute('data-hover')\n  })\n)\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/data-interactive/data-interactive.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { Fragment, type ElementType, type Ref } from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useSlot } from '../../hooks/use-slot'\nimport type { Props } from '../../types'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\n\nlet DEFAULT_DATA_INTERACTIVE_TAG = Fragment\n\ntype DataInteractiveRenderPropArg = {\n  hover: boolean\n  focus: boolean\n  active: boolean\n}\ntype DataInteractivePropsWeControl = never\n\nexport type DataInteractiveProps<TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG> =\n  Props<TTag, DataInteractiveRenderPropArg, DataInteractivePropsWeControl, {}>\n\nfunction DataInteractiveFn<TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG>(\n  props: DataInteractiveProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { ...theirProps } = props\n\n  // Ideally we can use a `disabled` prop, but that would depend on the props of the child element\n  // and we don't have access to that in this component.\n\n  let disabled = false\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing()\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let ourProps = mergeProps({ ref }, focusProps, hoverProps, pressProps)\n\n  let slot = useSlot<DataInteractiveRenderPropArg>({ hover, focus, active })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_DATA_INTERACTIVE_TAG,\n    name: 'DataInteractive',\n  })\n}\n\nexport interface _internal_ComponentDataInteractive extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG>(\n    props: DataInteractiveProps<TTag> & RefProp<typeof DataInteractiveFn>\n  ): React.JSX.Element\n}\n\nexport let DataInteractive = forwardRefWithAs(\n  DataInteractiveFn\n) as _internal_ComponentDataInteractive\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/description/__snapshots__/description.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to use a DescriptionProvider and a single Description, and have them linked 1`] = `\n<div\n  aria-describedby=\"headlessui-description-1\"\n>\n  <p\n    data-headlessui-state=\"\"\n    id=\"headlessui-description-1\"\n  >\n    I am a description\n  </p>\n  <span>\n    Contents\n  </span>\n</div>\n`;\n\nexports[`should be possible to use a DescriptionProvider and multiple Description components, and have them linked 1`] = `\n<div\n  aria-describedby=\"headlessui-description-1 headlessui-description-2\"\n>\n  <p\n    data-headlessui-state=\"\"\n    id=\"headlessui-description-1\"\n  >\n    I am a description\n  </p>\n  <span>\n    Contents\n  </span>\n  <p\n    data-headlessui-state=\"\"\n    id=\"headlessui-description-2\"\n  >\n    I am also a description\n  </p>\n</div>\n`;\n\nexports[`should be possible to use a DescriptionProvider without using a Description 1`] = `\n<div>\n  No description\n</div>\n`;\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/description/description.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { type ReactNode } from 'react'\nimport { Description, useDescriptions } from './description'\n\njest.mock('../../hooks/use-id')\n\nit('should be possible to use a DescriptionProvider without using a Description', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [describedby, DescriptionProvider] = useDescriptions()\n\n    return (\n      <DescriptionProvider>\n        <div aria-describedby={describedby}>{props.children}</div>\n      </DescriptionProvider>\n    )\n  }\n\n  function Example() {\n    return <Component>No description</Component>\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n\nit('should be possible to use a DescriptionProvider and a single Description, and have them linked', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [describedby, DescriptionProvider] = useDescriptions()\n\n    return (\n      <DescriptionProvider>\n        <div aria-describedby={describedby}>{props.children}</div>\n      </DescriptionProvider>\n    )\n  }\n\n  function Example() {\n    return (\n      <Component>\n        <Description>I am a description</Description>\n        <span>Contents</span>\n      </Component>\n    )\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n\nit('should be possible to use a DescriptionProvider and multiple Description components, and have them linked', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [describedby, DescriptionProvider] = useDescriptions()\n\n    return (\n      <DescriptionProvider>\n        <div aria-describedby={describedby}>{props.children}</div>\n      </DescriptionProvider>\n    )\n  }\n\n  function Example() {\n    return (\n      <Component>\n        <Description>I am a description</Description>\n        <span>Contents</span>\n        <Description>I am also a description</Description>\n      </Component>\n    )\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/description/description.tsx",
    "content": "'use client'\n\nimport React, {\n  createContext,\n  useContext,\n  useMemo,\n  useState,\n  type ElementType,\n  type ReactNode,\n  type Ref,\n} from 'react'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useDisabled } from '../../internal/disabled'\nimport type { Props } from '../../types'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\n\n// ---\n\ninterface SharedData {\n  slot?: {}\n  name?: string\n  props?: {}\n}\n\nlet DescriptionContext = createContext<\n  ({ value: string | undefined; register(value: string): () => void } & SharedData) | null\n>(null)\nDescriptionContext.displayName = 'DescriptionContext'\n\nfunction useDescriptionContext() {\n  let context = useContext(DescriptionContext)\n  if (context === null) {\n    let err = new Error(\n      'You used a <Description /> component, but it is not inside a relevant parent.'\n    )\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDescriptionContext)\n    throw err\n  }\n  return context\n}\n\nexport function useDescribedBy() {\n  return useContext(DescriptionContext)?.value ?? undefined\n}\n\ninterface DescriptionProviderProps extends SharedData {\n  children: ReactNode\n  value?: string | undefined\n}\n\nexport function useDescriptions(): [\n  string | undefined,\n  (props: DescriptionProviderProps) => React.JSX.Element,\n] {\n  let [descriptionIds, setDescriptionIds] = useState<string[]>([])\n\n  return [\n    // The actual id's as string or undefined\n    descriptionIds.length > 0 ? descriptionIds.join(' ') : undefined,\n\n    // The provider component\n    useMemo(() => {\n      return function DescriptionProvider(props: DescriptionProviderProps) {\n        let register = useEvent((value: string) => {\n          setDescriptionIds((existing) => [...existing, value])\n\n          return () => {\n            return setDescriptionIds((existing) => {\n              let clone = existing.slice()\n              let idx = clone.indexOf(value)\n              if (idx !== -1) clone.splice(idx, 1)\n              return clone\n            })\n          }\n        })\n\n        let contextBag = useMemo(\n          () => ({\n            register,\n            slot: props.slot,\n            name: props.name,\n            props: props.props,\n            value: props.value,\n          }),\n          [register, props.slot, props.name, props.props, props.value]\n        )\n\n        return (\n          <DescriptionContext.Provider value={contextBag}>\n            {props.children}\n          </DescriptionContext.Provider>\n        )\n      }\n    }, [setDescriptionIds]),\n  ]\n}\n\n// ---\n\nlet DEFAULT_DESCRIPTION_TAG = 'p' as const\n\nexport type DescriptionProps<TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG> =\n  Props<TTag>\n\nfunction DescriptionFn<TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG>(\n  props: DescriptionProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedDisabled = useDisabled()\n  let { id = `headlessui-description-${internalId}`, ...theirProps } = props\n  let context = useDescriptionContext()\n  let descriptionRef = useSyncRefs(ref)\n\n  useIsoMorphicEffect(() => context.register(id), [id, context.register])\n\n  let slot = useSlot({ ...context.slot, disabled: providedDisabled || false })\n  let ourProps = { ref: descriptionRef, ...context.props, id }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_DESCRIPTION_TAG,\n    name: context.name || 'Description',\n  })\n}\n\n// ---\nexport interface _internal_ComponentDescription extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG>(\n    props: DescriptionProps<TTag> & RefProp<typeof DescriptionFn>\n  ): React.JSX.Element\n}\n\nlet DescriptionRoot = forwardRefWithAs(DescriptionFn) as _internal_ComponentDescription\n\nexport let Description = Object.assign(DescriptionRoot, {\n  //\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/dialog/dialog.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { Fragment, createElement, useCallback, useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport {\n  DialogState,\n  PopoverState,\n  assertActiveElement,\n  assertDialog,\n  assertDialogDescription,\n  assertDialogTitle,\n  assertPopoverPanel,\n  getByText,\n  getDialog,\n  getDialogs,\n  getPopoverButton,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, click, focus, mouseDrag, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport type { PropsOf } from '../../types'\nimport { Popover } from '../popover/popover'\nimport { Transition } from '../transition/transition'\nimport { Dialog } from './dialog'\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return frames(1)\n}\n\nasync function frames(count: number) {\n  for (let n = 0; n <= count; n++) {\n    await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))\n  }\n}\n\nfunction TabSentinel(props: PropsOf<'button'>) {\n  return <button {...props} />\n}\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['Dialog.Title', Dialog.Title],\n    ['Dialog.Panel', Dialog.Panel],\n  ])(\n    'should error when we are using a <%s /> without a parent <Dialog />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component))).toThrow(\n        `<${name} /> is missing a parent <Dialog /> component.`\n      )\n      expect.hasAssertions()\n    })\n  )\n\n  it(\n    'should be possible to render a Dialog without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Dialog autoFocus={false} open={false} onClose={console.log}>\n          <button>Trigger</button>\n          <Dialog.Title />\n          <p>Contents</p>\n          <Dialog.Description />\n        </Dialog>\n      )\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Dialog', () => {\n    it(\n      'should complain when the `open` and `onClose` prop are missing',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          // @ts-expect-error\n          render(<Dialog autoFocus={false} as=\"div\" />)\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You have to provide an \\`open\\` and an \\`onClose\\` prop to the \\`Dialog\\` component.\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should be able to explicitly choose role=dialog',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen(true)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen} role=\"dialog\">\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'dialog' } })\n      })\n    )\n\n    it(\n      'should be able to explicitly choose role=alertdialog',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen(true)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen} role=\"alertdialog\">\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'alertdialog' } })\n      })\n    )\n\n    it(\n      'should fall back to role=dialog for an invalid role',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen(true)}>\n                Trigger\n              </button>\n              <Dialog\n                open={isOpen}\n                onClose={setIsOpen}\n                // @ts-expect-error: We explicitly type role to only accept valid options — but we still want to verify runtime behavior\n                role=\"foobar\"\n              >\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'dialog' } })\n      }, 'warn')\n    )\n\n    it(\n      'should complain when an `open` prop is provided without an `onClose` prop',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          // @ts-expect-error\n          render(<Dialog autoFocus={false} as=\"div\" open={false} />)\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You provided an \\`open\\` prop to the \\`Dialog\\`, but forgot an \\`onClose\\` prop.\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should complain when an `onClose` prop is provided without an `open` prop',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          render(<Dialog autoFocus={false} as=\"div\" onClose={() => {}} />)\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You provided an \\`onClose\\` prop to the \\`Dialog\\`, but forgot an \\`open\\` prop.\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should complain when an `open` prop is not a boolean',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          // @ts-expect-error\n          render(<Dialog autoFocus={false} as=\"div\" open={null} onClose={console.log} />)\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You provided an \\`open\\` prop to the \\`Dialog\\`, but the value is not a boolean. Received: null\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should complain when an `onClose` prop is not a function',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          // @ts-expect-error\n          render(<Dialog autoFocus={false} as=\"div\" open={false} onClose={null} />)\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You provided an \\`onClose\\` prop to the \\`Dialog\\`, but the value is not a function. Received: null\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should be possible to render a Dialog using a render prop',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen(true)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                {(data) => (\n                  <>\n                    <pre>{JSON.stringify(data)}</pre>\n                    <TabSentinel />\n                  </>\n                )}\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        assertDialog({ state: DialogState.Visible, textContent: JSON.stringify({ open: true }) })\n      })\n    )\n\n    it('should be possible to always render the Dialog if we provide it a `static` prop (and enable focus trapping based on `open`)', async () => {\n      let focusCounter = jest.fn()\n      render(\n        <>\n          <button>Trigger</button>\n          <Dialog autoFocus={false} open={true} onClose={console.log} static>\n            <p>Contents</p>\n            <TabSentinel onFocus={focusCounter} />\n          </Dialog>\n        </>\n      )\n\n      await nextFrame()\n\n      // Let's verify that the Dialog is already there\n      expect(getDialog()).not.toBe(null)\n      expect(focusCounter).toHaveBeenCalledTimes(1)\n    })\n\n    it('should be possible to always render the Dialog if we provide it a `static` prop (and disable focus trapping based on `open`)', () => {\n      let focusCounter = jest.fn()\n      render(\n        <>\n          <button>Trigger</button>\n          <Dialog autoFocus={false} open={false} onClose={console.log} static>\n            <p>Contents</p>\n            <TabSentinel onFocus={focusCounter} />\n          </Dialog>\n        </>\n      )\n\n      // Let's verify that the Dialog is already there\n      expect(getDialog()).not.toBe(null)\n      expect(focusCounter).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to use a different render strategy for the Dialog', async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n\n        return (\n          <>\n            <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen} unmount={false}>\n              <input />\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.InvisibleHidden })\n\n      // Let's open the Dialog, to see if it is not hidden anymore\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      // Let's close the Dialog\n      await press(Keys.Escape)\n\n      assertDialog({ state: DialogState.InvisibleHidden })\n    })\n\n    it(\n      'should add a scroll lock to the html tag',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                <input id=\"a\" type=\"text\" />\n                <input id=\"b\" type=\"text\" />\n                <input id=\"c\" type=\"text\" />\n              </Dialog>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let btn = document.getElementById('trigger')\n\n        // Open the dialog\n        await click(btn)\n\n        // Expect overflow\n        expect(document.documentElement.style.overflow).toBe('hidden')\n      })\n    )\n\n    it(\n      'should wait to add a scroll lock to the html tag when unmount is false in a Transition',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n\n              <Transition as={Fragment} show={isOpen} unmount={false}>\n                <Dialog autoFocus={false} onClose={() => setIsOpen(false)} unmount={false}>\n                  <input id=\"a\" type=\"text\" />\n                  <input id=\"b\" type=\"text\" />\n                  <input id=\"c\" type=\"text\" />\n                </Dialog>\n              </Transition>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let btn = document.getElementById('trigger')\n\n        // Open the dialog\n        await click(btn)\n\n        // Expect overflow\n        expect(document.documentElement.style.overflow).toBe('hidden')\n      })\n    )\n\n    it(\n      'scroll locking should work when transitioning between dialogs',\n      suppressConsoleLogs(async () => {\n        // While we don't support multiple dialogs\n        // We at least want to work towards supporting it at some point\n        // The first step is just making sure that scroll locking works\n        // when there are multiple dialogs open at the same time\n\n        function Example() {\n          let [dialogs, setDialogs] = useState<string[]>([])\n          let toggle = useCallback(\n            (id: string, state: 'open' | 'close') => {\n              if (state === 'open' && !dialogs.includes(id)) {\n                setDialogs([id])\n              } else if (state === 'close' && dialogs.includes(id)) {\n                setDialogs(dialogs.filter((x) => x !== id))\n              }\n            },\n            [dialogs]\n          )\n\n          return (\n            <>\n              <DialogWrapper id=\"d1\" dialogs={dialogs} toggle={toggle} />\n              <DialogWrapper id=\"d2\" dialogs={dialogs} toggle={toggle} />\n              <DialogWrapper id=\"d3\" dialogs={dialogs} toggle={toggle} />\n            </>\n          )\n        }\n\n        function DialogWrapper({\n          id,\n          dialogs,\n          toggle,\n        }: {\n          id: string\n          dialogs: string[]\n          toggle: (id: string, state: 'open' | 'close') => void\n        }) {\n          return (\n            <>\n              <button id={`open_${id}`} onClick={() => toggle(id, 'open')}>\n                Open {id}\n              </button>\n              <Transition as={Fragment} show={dialogs.includes(id)}>\n                <Dialog autoFocus={false} onClose={() => toggle(id, 'close')}>\n                  <button id={`close_${id}`} onClick={() => toggle(id, 'close')}>\n                    Close {id}\n                  </button>\n                </Dialog>\n              </Transition>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let open1 = () => document.getElementById('open_d1')\n        let open2 = () => document.getElementById('open_d2')\n        let open3 = () => document.getElementById('open_d3')\n        let close3 = () => document.getElementById('close_d3')\n\n        // Open the dialog & expect overflow\n        await click(open1())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Open the dialog & expect overflow\n        await click(open2())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Open the dialog & expect overflow\n        await click(open3())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // At this point only the last dialog should be open\n        // Close the dialog & dont expect overflow\n        await click(close3())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should remove the scroll lock when the open closed state is `Closing`',\n      suppressConsoleLogs(async () => {\n        function Example({ open = true }) {\n          return (\n            <Dialog transition autoFocus={false} open={open} onClose={() => {}}>\n              <input id=\"a\" type=\"text\" />\n              <input id=\"b\" type=\"text\" />\n              <input id=\"c\" type=\"text\" />\n            </Dialog>\n          )\n        }\n\n        let { rerender } = render(<Example open={true} />)\n\n        // The overflow should be there\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Re-render but with an exit transition\n        rerender(<Example open={false} />)\n\n        // The moment the dialog is closing, the overflow should be gone\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should remove the scroll lock when the open closed state is `Closing` (using Transition wrapper)',\n      suppressConsoleLogs(async () => {\n        function Example({ open = true }) {\n          return (\n            <Transition show={open}>\n              <Dialog autoFocus={false} onClose={() => {}}>\n                <input id=\"a\" type=\"text\" />\n                <input id=\"b\" type=\"text\" />\n                <input id=\"c\" type=\"text\" />\n              </Dialog>\n            </Transition>\n          )\n        }\n\n        let { rerender } = render(<Example open={true} />)\n\n        // The overflow should be there\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Re-render but with an exit transition\n        rerender(<Example open={false} />)\n\n        // The moment the dialog is closing, the overflow should be gone\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should not have a scroll lock when the transition marked as not shown',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          return (\n            <Transition as={Fragment} show={false} unmount={false}>\n              <Dialog autoFocus={false} as=\"div\" onClose={() => {}}>\n                <input type=\"text\" />\n              </Dialog>\n            </Transition>\n          )\n        }\n\n        render(<Example />)\n\n        await nextFrame()\n\n        // The overflow should NOT be there\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n  })\n\n  describe('Dialog.Title', () => {\n    it(\n      'should be possible to render Dialog.Title using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Dialog autoFocus={false} open={true} onClose={console.log}>\n            <Dialog.Title>{(slot) => <>{JSON.stringify(slot)}</>}</Dialog.Title>\n            <TabSentinel />\n          </Dialog>\n        )\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible })\n        assertDialogTitle({\n          state: DialogState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n  })\n\n  describe('Dialog.Description', () => {\n    it(\n      'should be possible to render Dialog.Description using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Dialog autoFocus={false} open={true} onClose={console.log}>\n            <Dialog.Description>{(slot) => <>{JSON.stringify(slot)}</>}</Dialog.Description>\n            <TabSentinel />\n          </Dialog>\n        )\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible })\n        assertDialogDescription({\n          state: DialogState.Visible,\n          textContent: JSON.stringify({ open: true, disabled: false }),\n        })\n      })\n    )\n  })\n})\n\ndescribe('Composition', () => {\n  it(\n    'should be possible to open a dialog from inside a Popover (and then close it)',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isDialogOpen, setIsDialogOpen] = useState(false)\n\n        return (\n          <div>\n            <Popover>\n              <Popover.Button>Open Popover</Popover.Button>\n              <Popover.Panel>\n                <button id=\"openDialog\" onClick={() => setIsDialogOpen(true)}>\n                  Open dialog\n                </button>\n              </Popover.Panel>\n            </Popover>\n\n            <Dialog autoFocus={false} open={isDialogOpen} onClose={console.log}>\n              <Dialog.Panel>\n                <button id=\"closeDialog\" onClick={() => setIsDialogOpen(false)}>\n                  Close Dialog\n                </button>\n              </Dialog.Panel>\n            </Dialog>\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Nothing is open initially\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(document.body)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The popover should be open but the dialog should not\n      assertPopoverPanel({ state: PopoverState.Visible })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(getPopoverButton())\n\n      // Open the dialog from inside the popover\n      await click(document.getElementById('openDialog'))\n\n      // The dialog should be open but the popover should not\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.Visible })\n      assertActiveElement(document.getElementById('closeDialog'))\n\n      // Close the dialog from inside itself\n      await click(document.getElementById('closeDialog'))\n\n      // Nothing should be open\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to open the Dialog via a Transition component',\n    suppressConsoleLogs(async () => {\n      render(\n        <Transition show={true}>\n          <Dialog autoFocus={false} onClose={console.log}>\n            <Dialog.Description>{(slot) => <>{JSON.stringify(slot)}</>}</Dialog.Description>\n            <TabSentinel />\n          </Dialog>\n        </Transition>\n      )\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.Visible })\n      assertDialogDescription({\n        state: DialogState.Visible,\n        textContent: JSON.stringify({ open: true, disabled: false }),\n      })\n    })\n  )\n\n  it(\n    'should be possible to close the Dialog via a Transition component',\n    suppressConsoleLogs(async () => {\n      render(\n        <Transition show={false}>\n          <Dialog autoFocus={false} onClose={console.log}>\n            <Dialog.Description>{(slot) => <>{JSON.stringify(slot)}</>}</Dialog.Description>\n            <TabSentinel />\n          </Dialog>\n        </Transition>\n      )\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Escape` key', () => {\n    it(\n      'should be possible to close the dialog with Escape',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                Contents\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Close dialog\n        await press(Keys.Escape)\n\n        // Verify it is close\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the dialog with Escape, when a field is focused',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                Contents\n                <input id=\"name\" />\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Close dialog\n        await press(Keys.Escape)\n\n        // Verify it is close\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to close the dialog with Escape, when a field is focused but cancels the event',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                Contents\n                <input\n                  id=\"name\"\n                  onKeyDown={(event) => {\n                    event.preventDefault()\n                    event.stopPropagation()\n                  }}\n                />\n                <TabSentinel />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Try to close the dialog\n        await press(Keys.Escape)\n\n        // Verify it is still open\n        assertDialog({ state: DialogState.Visible })\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to tab around when using the initialFocus ref',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          let initialFocusRef = useRef(null)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <Dialog\n                autoFocus={false}\n                open={isOpen}\n                onClose={setIsOpen}\n                initialFocus={initialFocusRef}\n              >\n                Contents\n                <TabSentinel id=\"a\" />\n                <input type=\"text\" id=\"b\" ref={initialFocusRef} />\n              </Dialog>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n\n    it(\n      'should not escape the FocusTrap when there is only 1 focusable element (going forwards)',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <button>Before</button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                <Dialog.Panel>\n                  <input type=\"text\" id=\"a\" />\n                </Dialog.Panel>\n              </Dialog>\n              <button>After</button>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n\n    it(\n      'should not escape the FocusTrap when there is only 1 focusable element (going backwards)',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [isOpen, setIsOpen] = useState(false)\n          return (\n            <>\n              <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n                Trigger\n              </button>\n              <button>Before</button>\n              <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n                <Dialog.Panel>\n                  <input type=\"text\" id=\"a\" />\n                </Dialog.Panel>\n              </Dialog>\n              <button>After</button>\n            </>\n          )\n        }\n        render(<Example />)\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({ state: DialogState.Visible })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to close the dialog, and re-focus the button when we click outside on the body element',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n        return (\n          <>\n            <button onClick={() => setIsOpen((v) => !v)}>Trigger</button>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              Contents\n              <TabSentinel />\n            </Dialog>\n          </>\n        )\n      }\n      render(<Example />)\n\n      // Open dialog\n      await click(getByText('Trigger'))\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the body to close\n      await click(document.body)\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getByText('Trigger'))\n    })\n  )\n\n  it(\n    'should be possible to submit a form inside a Dialog',\n    suppressConsoleLogs(async () => {\n      let submitFn = jest.fn()\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n        return (\n          <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n            <form onSubmit={submitFn}>\n              <input type=\"hidden\" value=\"abc\" />\n              <button type=\"submit\">Submit</button>\n            </form>\n            <TabSentinel />\n          </Dialog>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      // Verify that the submitFn function has been called\n      expect(submitFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should stop propagating click events when clicking on an element inside the Dialog',\n    suppressConsoleLogs(async () => {\n      let wrapperFn = jest.fn()\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n        return (\n          <div onClick={wrapperFn}>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <Dialog.Panel>\n                Contents\n                <button onClick={() => setIsOpen(false)}>Inside</button>\n                <TabSentinel />\n              </Dialog.Panel>\n            </Dialog>\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n\n      // Click the button inside the Dialog\n      await click(getByText('Inside'))\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n    })\n  )\n\n  it(\n    'should should be possible to click on removed elements without closing the Dialog',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n        let wrapper = useRef<HTMLDivElement | null>(null)\n\n        return (\n          <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n            <div ref={wrapper}>\n              Contents\n              <button\n                onMouseDown={() => {\n                  // Remove this button before the Dialog's mousedown listener fires:\n                  wrapper.current?.remove()\n                }}\n              >\n                Inside\n              </button>\n              <TabSentinel />\n            </div>\n          </Dialog>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the Dialog\n      await click(getByText('Inside'))\n\n      // Verify it is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to click on elements created by third party libraries',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n      function ThirdPartyLibrary() {\n        return createPortal(\n          <>\n            <button data-lib onClick={fn}>\n              3rd party button\n            </button>\n          </>,\n          document.body\n        )\n      }\n\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n\n        return (\n          <div>\n            <span>Main app</span>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <div>\n                Contents\n                <TabSentinel />\n              </div>\n            </Dialog>\n            <ThirdPartyLibrary />\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the 3rd party library\n      await click(document.querySelector('[data-lib]'))\n\n      // Verify we clicked on the 3rd party button\n      expect(fn).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to focus elements created by third party libraries',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n      let handleFocus = jest.fn()\n\n      function ThirdPartyLibrary() {\n        return createPortal(\n          <>\n            <button data-lib onClick={fn}>\n              3rd party button\n            </button>\n          </>,\n          document.body\n        )\n      }\n\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n\n        return (\n          <div>\n            <span>Main app</span>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <div>\n                Contents\n                <TabSentinel onFocus={handleFocus} />\n              </div>\n            </Dialog>\n            <ThirdPartyLibrary />\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the 3rd party library\n      await focus(document.querySelector('[data-lib]'))\n\n      // Verify that the focus is on the 3rd party button, and that we are not redirecting focus to\n      // the dialog again.\n      assertActiveElement(document.querySelector('[data-lib]'))\n\n      // This should only have been called once (when opening the Dialog)\n      expect(handleFocus).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to click elements inside the dialog when they reside inside a shadow boundary',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n      function ShadowChildren({ id, buttonId }: { id: string; buttonId: string }) {\n        let container = useRef<HTMLDivElement | null>(null)\n\n        useEffect(() => {\n          if (!container.current || container.current.shadowRoot) {\n            return\n          }\n\n          let shadowRoot = container.current.attachShadow({ mode: 'open' })\n          let button = document.createElement('button')\n          button.id = buttonId\n          button.textContent = 'Inside shadow root'\n          button.addEventListener('click', fn)\n          shadowRoot.appendChild(button)\n        }, [])\n\n        return <div id={id} ref={container}></div>\n      }\n\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n\n        return (\n          <div>\n            <button onClick={() => setIsOpen(true)}>open</button>\n            <Dialog autoFocus={false} open={isOpen} onClose={() => setIsOpen(false)}>\n              <div>\n                <button id=\"btn_outside_light\" onClick={fn}>\n                  Button\n                </button>\n                <ShadowChildren id=\"outside_shadow\" buttonId=\"btn_outside_shadow\" />\n              </div>\n              <Dialog.Panel>\n                <button id=\"btn_inside_light\" onClick={fn}>\n                  Button\n                </button>\n                <ShadowChildren id=\"inside_shadow\" buttonId=\"btn_inside_shadow\" />\n              </Dialog.Panel>\n            </Dialog>\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (light DOM)\n      await click(document.querySelector('#btn_inside_light'))\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (shadow DOM)\n      await click(\n        document.querySelector('#inside_shadow')?.shadowRoot?.querySelector('#btn_inside_shadow') ??\n          null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(2)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button outside the dialog (shadow DOM)\n      await click(\n        document\n          .querySelector('#outside_shadow')\n          ?.shadowRoot?.querySelector('#btn_outside_shadow') ?? null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(3)\n\n      // Verify the dialog is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  // NOTE: This test doesn't actually fail in JSDOM when it's supposed to\n  // We're keeping it around for documentation purposes\n  it(\n    'should not close the Dialog if it starts open and we click inside the Dialog when it has only a panel',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(true)\n        return (\n          <>\n            <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <Dialog autoFocus={false} open={isOpen} onClose={() => setIsOpen(false)}>\n              <Dialog.Panel>\n                <p id=\"inside\">My content</p>\n                <button>close</button>\n              </Dialog.Panel>\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Open the dialog\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the p tag inside the dialog\n      await click(document.getElementById('inside'))\n\n      // It should not have closed\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should close the Dialog if we click outside the Dialog.Panel',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n        return (\n          <>\n            <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <Dialog.Panel>\n                <TabSentinel />\n              </Dialog.Panel>\n              <button id=\"outside\">Outside, technically</button>\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('outside'))\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not close the Dialog if we click inside the Dialog.Panel',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n        return (\n          <>\n            <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <Dialog.Panel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </Dialog.Panel>\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('inside'))\n\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should not close the dialog if opened during mouse up',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n        return (\n          <>\n            <button id=\"trigger\" onMouseUpCapture={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <Dialog.Panel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </Dialog.Panel>\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('inside'))\n\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should not close the dialog if click starts inside the dialog but ends outside',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [isOpen, setIsOpen] = useState(false)\n        return (\n          <>\n            <button id=\"trigger\" onClick={() => setIsOpen((v) => !v)}>\n              Trigger\n            </button>\n            <div id=\"i-am-outside\">this thing</div>\n            <Dialog autoFocus={false} open={isOpen} onClose={setIsOpen}>\n              <Dialog.Panel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </Dialog.Panel>\n            </Dialog>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Open the dialog\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      // Start a click inside the dialog and end it outside\n      await mouseDrag(document.getElementById('inside'), document.getElementById('i-am-outside'))\n\n      // It should not have hidden\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('i-am-outside'))\n\n      // It's gone\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Nesting', () => {\n  type RenderStrategy = 'mounted' | 'always'\n\n  function Nested({\n    onClose,\n    open = true,\n    level = 1,\n    renderWhen = 'mounted',\n  }: {\n    onClose: (value: boolean) => void\n    open?: boolean\n    level?: number\n    renderWhen?: RenderStrategy\n  }) {\n    let [showChild, setShowChild] = useState(false)\n\n    return (\n      <Dialog autoFocus={false} open={open} onClose={onClose}>\n        <div>\n          <p>Level: {level}</p>\n          <button onClick={() => setShowChild(true)}>Open {level + 1} a</button>\n          <button onClick={() => setShowChild(true)}>Open {level + 1} b</button>\n          <button onClick={() => setShowChild(true)}>Open {level + 1} c</button>\n        </div>\n        {renderWhen === 'always' ? (\n          <Nested\n            open={showChild}\n            onClose={setShowChild}\n            level={level + 1}\n            renderWhen={renderWhen}\n          />\n        ) : (\n          showChild && <Nested open={true} onClose={setShowChild} level={level + 1} />\n        )}\n      </Dialog>\n    )\n  }\n\n  function Example({ renderWhen = 'mounted' }: { renderWhen: RenderStrategy }) {\n    let [open, setOpen] = useState(false)\n\n    return (\n      <>\n        <button onClick={() => setOpen(true)}>Open 1</button>\n        {open && <Nested open={true} onClose={setOpen} renderWhen={renderWhen} />}\n      </>\n    )\n  }\n\n  it.each`\n    strategy                  | when         | action\n    ${'with `Escape`'}        | ${'mounted'} | ${() => press(Keys.Escape)}\n    ${'with `Outside Click`'} | ${'mounted'} | ${() => click(document.body)}\n    ${'with `Escape`'}        | ${'always'}  | ${() => press(Keys.Escape)}\n    ${'with `Outside Click`'} | ${'always'}  | ${() => click(document.body)}\n  `(\n    'should be possible to open nested Dialog components (visible when $when) and close them $strategy',\n    async ({ when, action }) => {\n      render(<Example renderWhen={when} />)\n\n      // Verify we have no open dialogs\n      expect(getDialogs()).toHaveLength(0)\n\n      // Open Dialog 1\n      await click(getByText('Open 1'))\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 a` has focus\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Open Dialog 2 via the second button\n      await click(getByText('Open 2 b'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Verify that the `Open 3 a` has focus\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 b` button got focused again\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Open Dialog 2 via button b\n      await click(getByText('Open 2 b'))\n\n      // Verify that the `Open 3 a` has focus\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Open Dialog 3 via button c\n      await click(getByText('Open 3 c'))\n\n      // Verify that the `Open 4 a` has focus\n      assertActiveElement(getByText('Open 4 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 a'))\n\n      // Verify that we have 3 open dialogs\n      expect(getDialogs()).toHaveLength(3)\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that the `Open 3 c` button got focused again\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 b` button got focused again\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 0 open dialogs\n      expect(getDialogs()).toHaveLength(0)\n\n      // Verify that the `Open 1` button got focused again\n      assertActiveElement(getByText('Open 1'))\n    }\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/dialog/dialog.tsx",
    "content": "'use client'\n\n// WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/\nimport React, {\n  Fragment,\n  createContext,\n  createRef,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useReducer,\n  useRef,\n  type ContextType,\n  type ElementType,\n  type MutableRefObject,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n  type RefObject,\n} from 'react'\nimport { useEscape } from '../../hooks/use-escape'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useInertOthers } from '../../hooks/use-inert-others'\nimport { useIsTouchDevice } from '../../hooks/use-is-touch-device'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useOnDisappear } from '../../hooks/use-on-disappear'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport {\n  MainTreeProvider,\n  useMainTreeNode,\n  useRootContainers,\n} from '../../hooks/use-root-containers'\nimport { useScrollLock } from '../../hooks/use-scroll-lock'\nimport { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { CloseProvider } from '../../internal/close-provider'\nimport { ResetOpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'\nimport { ForcePortalRoot } from '../../internal/portal-force-root'\nimport { stackMachines } from '../../machines/stack-machine'\nimport { useSlice } from '../../react-glue'\nimport type { Props } from '../../types'\nimport { match } from '../../utils/match'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport {\n  Description,\n  useDescriptions,\n  type _internal_ComponentDescription,\n} from '../description/description'\nimport { FocusTrap, FocusTrapFeatures } from '../focus-trap/focus-trap'\nimport { Portal, PortalGroup, useNestedPortals } from '../portal/portal'\nimport { Transition, TransitionChild } from '../transition/transition'\n\nenum DialogStates {\n  Open,\n  Closed,\n}\n\ninterface StateDefinition {\n  titleId: string | null\n  panelRef: MutableRefObject<HTMLElement | null>\n}\n\nenum ActionTypes {\n  SetTitleId,\n}\n\ntype Actions = { type: ActionTypes.SetTitleId; id: string | null }\n\nlet reducers: {\n  [P in ActionTypes]: (\n    state: StateDefinition,\n    action: Extract<Actions, { type: P }>\n  ) => StateDefinition\n} = {\n  [ActionTypes.SetTitleId](state, action) {\n    if (state.titleId === action.id) return state\n    return { ...state, titleId: action.id }\n  },\n}\n\nlet DialogContext = createContext<\n  | [\n      {\n        dialogState: DialogStates\n        unmount: boolean\n        close: () => void\n        setTitleId: (id: string | null) => void\n      },\n      StateDefinition,\n    ]\n  | null\n>(null)\nDialogContext.displayName = 'DialogContext'\n\nfunction useDialogContext(component: string) {\n  let context = useContext(DialogContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Dialog /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDialogContext)\n    throw err\n  }\n  return context\n}\n\nfunction stateReducer(state: StateDefinition, action: Actions) {\n  return match(action.type, reducers, state, action)\n}\n\n// ---\n\nlet InternalDialog = forwardRefWithAs(function InternalDialog<\n  TTag extends ElementType = typeof DEFAULT_DIALOG_TAG,\n>(props: DialogProps<TTag>, ref: Ref<HTMLElement>) {\n  let internalId = useId()\n  let {\n    id = `headlessui-dialog-${internalId}`,\n    open,\n    onClose,\n    initialFocus,\n    role = 'dialog',\n    autoFocus = true,\n    __demoMode = false,\n    unmount = false,\n    ...theirProps\n  } = props\n\n  let didWarnOnRole = useRef(false)\n\n  role = (function () {\n    if (role === 'dialog' || role === 'alertdialog') {\n      return role\n    }\n\n    if (!didWarnOnRole.current) {\n      didWarnOnRole.current = true\n      console.warn(\n        `Invalid role [${role}] passed to <Dialog />. Only \\`dialog\\` and and \\`alertdialog\\` are supported. Using \\`dialog\\` instead.`\n      )\n    }\n\n    return 'dialog'\n  })()\n\n  let usesOpenClosedState = useOpenClosed()\n  if (open === undefined && usesOpenClosedState !== null) {\n    // Update the `open` prop based on the open closed state\n    open = (usesOpenClosedState & State.Open) === State.Open\n  }\n\n  let internalDialogRef = useRef<HTMLElement | null>(null)\n  let dialogRef = useSyncRefs(internalDialogRef, ref)\n\n  let ownerDocument = useOwnerDocument(internalDialogRef.current)\n\n  let dialogState = open ? DialogStates.Open : DialogStates.Closed\n\n  let [state, dispatch] = useReducer(stateReducer, {\n    titleId: null,\n    descriptionId: null,\n    panelRef: createRef(),\n  } as StateDefinition)\n\n  let close = useEvent(() => onClose(false))\n\n  let setTitleId = useEvent((id: string | null) => dispatch({ type: ActionTypes.SetTitleId, id }))\n\n  let ready = useServerHandoffComplete()\n  let enabled = ready ? dialogState === DialogStates.Open : false\n  let [portals, PortalWrapper] = useNestedPortals()\n\n  // We use this because reading these values during initial render(s)\n  // can result in `null` rather then the actual elements\n  // This doesn't happen when using certain components like a\n  // `<Dialog.Title>` because they cause the parent to re-render\n  let defaultContainer: RefObject<HTMLElement> = {\n    get current() {\n      return state.panelRef.current ?? internalDialogRef.current\n    },\n  }\n\n  let mainTreeNode = useMainTreeNode()\n  let { resolveContainers: resolveRootContainers } = useRootContainers({\n    mainTreeNode,\n    portals,\n    defaultContainers: [defaultContainer],\n  })\n\n  // When the `Dialog` is wrapped in a `Transition` (or another Headless UI component that exposes\n  // the OpenClosed state) then we get some information via context about its state. When the\n  // `Transition` is about to close, then the `State.Closing` state will be exposed. This allows us\n  // to enable/disable certain functionality in the `Dialog` upfront instead of waiting until the\n  // `Transition` is done transitioning.\n  let isClosing =\n    usesOpenClosedState !== null ? (usesOpenClosedState & State.Closing) === State.Closing : false\n\n  // Ensure other elements can't be interacted with\n  let inertOthersEnabled = __demoMode ? false : isClosing ? false : enabled\n  useInertOthers(inertOthersEnabled, {\n    allowed: useEvent(() => [\n      // Allow the headlessui-portal of the Dialog to be interactive. This\n      // contains the current dialog and the necessary focus guard elements.\n      internalDialogRef.current?.closest<HTMLElement>('[data-headlessui-portal]') ?? null,\n    ]),\n    disallowed: useEvent(() => [\n      // Disallow the \"main\" tree root node\n      mainTreeNode?.closest<HTMLElement>('body > *:not(#headlessui-portal-root)') ?? null,\n    ]),\n  })\n\n  // Ensure that the Dialog is the top layer when it is opened.\n  //\n  // In a perfect world this is pushed / popped when we open / close the Dialog\n  // for within an event listener. But since the state is controlled by the\n  // user, this is the next best thing to do.\n  let stackMachine = stackMachines.get(null)\n  useIsoMorphicEffect(() => {\n    if (!enabled) return\n\n    stackMachine.actions.push(id)\n    return () => stackMachine.actions.pop(id)\n  }, [stackMachine, id, enabled])\n\n  // Check if the dialog is the current top layer\n  let isTopLayer = useSlice(\n    stackMachine,\n    useCallback((state) => stackMachine.selectors.isTop(state, id), [stackMachine, id])\n  )\n\n  // Close Dialog on outside click\n  useOutsideClick(isTopLayer, resolveRootContainers, (event) => {\n    event.preventDefault()\n    close()\n  })\n\n  // Handle `Escape` to close\n  useEscape(isTopLayer, ownerDocument?.defaultView, (event) => {\n    event.preventDefault()\n    event.stopPropagation()\n\n    // Ensure that we blur the current activeElement to prevent maintaining\n    // focus and potentially scrolling the page to the end (because the Dialog\n    // is rendered in a Portal at the end of the document.body and the browser\n    // tries to keep the focused element in view)\n    //\n    // Typically only happens in Safari.\n    if (\n      document.activeElement &&\n      'blur' in document.activeElement &&\n      typeof document.activeElement.blur === 'function'\n    ) {\n      document.activeElement.blur()\n    }\n\n    close()\n  })\n\n  // Scroll lock\n  let scrollLockEnabled = __demoMode ? false : isClosing ? false : enabled\n  useScrollLock(scrollLockEnabled, ownerDocument, resolveRootContainers)\n\n  // Ensure we close the dialog as soon as the dialog itself becomes hidden\n  useOnDisappear(enabled, internalDialogRef, close)\n\n  let [describedby, DescriptionProvider] = useDescriptions()\n\n  let contextBag = useMemo<ContextType<typeof DialogContext>>(\n    () => [{ dialogState, close, setTitleId, unmount }, state],\n    [dialogState, close, setTitleId, unmount, state]\n  )\n\n  let slot = useSlot<DialogRenderPropArg>({ open: dialogState === DialogStates.Open })\n\n  let ourProps = {\n    ref: dialogRef,\n    id,\n    role,\n    tabIndex: -1,\n    'aria-modal': __demoMode ? undefined : dialogState === DialogStates.Open ? true : undefined,\n    'aria-labelledby': state.titleId,\n    'aria-describedby': describedby,\n    unmount,\n  }\n\n  let shouldMoveFocusInside = !useIsTouchDevice()\n  let focusTrapFeatures = FocusTrapFeatures.None\n\n  if (enabled && !__demoMode) {\n    focusTrapFeatures |= FocusTrapFeatures.RestoreFocus\n    focusTrapFeatures |= FocusTrapFeatures.TabLock\n\n    if (autoFocus) {\n      focusTrapFeatures |= FocusTrapFeatures.AutoFocus\n    }\n\n    if (shouldMoveFocusInside) {\n      focusTrapFeatures |= FocusTrapFeatures.InitialFocus\n    }\n  }\n\n  let render = useRender()\n\n  return (\n    <ResetOpenClosedProvider>\n      <ForcePortalRoot force={true}>\n        <Portal>\n          <DialogContext.Provider value={contextBag}>\n            <PortalGroup target={internalDialogRef}>\n              <ForcePortalRoot force={false}>\n                <DescriptionProvider slot={slot}>\n                  <PortalWrapper>\n                    <FocusTrap\n                      initialFocus={initialFocus}\n                      initialFocusFallback={internalDialogRef}\n                      containers={resolveRootContainers}\n                      features={focusTrapFeatures}\n                    >\n                      <CloseProvider value={close}>\n                        {render({\n                          ourProps,\n                          theirProps,\n                          slot,\n                          defaultTag: DEFAULT_DIALOG_TAG,\n                          features: DialogRenderFeatures,\n                          visible: dialogState === DialogStates.Open,\n                          name: 'Dialog',\n                        })}\n                      </CloseProvider>\n                    </FocusTrap>\n                  </PortalWrapper>\n                </DescriptionProvider>\n              </ForcePortalRoot>\n            </PortalGroup>\n          </DialogContext.Provider>\n        </Portal>\n      </ForcePortalRoot>\n    </ResetOpenClosedProvider>\n  )\n})\n\n// ---\n\nlet DEFAULT_DIALOG_TAG = 'div' as const\ntype DialogRenderPropArg = {\n  open: boolean\n}\ntype DialogPropsWeControl = 'aria-describedby' | 'aria-labelledby' | 'aria-modal'\n\nlet DialogRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type DialogProps<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG> = Props<\n  TTag,\n  DialogRenderPropArg,\n  DialogPropsWeControl,\n  PropsForFeatures<typeof DialogRenderFeatures> & {\n    open?: boolean\n    onClose: (value: boolean) => void\n    initialFocus?: MutableRefObject<HTMLElement | null>\n    role?: 'dialog' | 'alertdialog'\n    autoFocus?: boolean\n    transition?: boolean\n    __demoMode?: boolean\n  }\n>\n\nfunction DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(\n  props: DialogProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { transition = false, open, ...rest } = props\n\n  // Validations\n  let usesOpenClosedState = useOpenClosed()\n  let hasOpen = props.hasOwnProperty('open') || usesOpenClosedState !== null\n  let hasOnClose = props.hasOwnProperty('onClose')\n\n  if (!hasOpen && !hasOnClose) {\n    throw new Error(\n      `You have to provide an \\`open\\` and an \\`onClose\\` prop to the \\`Dialog\\` component.`\n    )\n  }\n\n  if (!hasOpen) {\n    throw new Error(\n      `You provided an \\`onClose\\` prop to the \\`Dialog\\`, but forgot an \\`open\\` prop.`\n    )\n  }\n\n  if (!hasOnClose) {\n    throw new Error(\n      `You provided an \\`open\\` prop to the \\`Dialog\\`, but forgot an \\`onClose\\` prop.`\n    )\n  }\n\n  if (!usesOpenClosedState && typeof props.open !== 'boolean') {\n    throw new Error(\n      `You provided an \\`open\\` prop to the \\`Dialog\\`, but the value is not a boolean. Received: ${props.open}`\n    )\n  }\n\n  if (typeof props.onClose !== 'function') {\n    throw new Error(\n      `You provided an \\`onClose\\` prop to the \\`Dialog\\`, but the value is not a function. Received: ${props.onClose}`\n    )\n  }\n\n  if ((open !== undefined || transition) && !rest.static) {\n    return (\n      <MainTreeProvider>\n        <Transition show={open} transition={transition} unmount={rest.unmount}>\n          <InternalDialog ref={ref} {...rest} />\n        </Transition>\n      </MainTreeProvider>\n    )\n  }\n\n  return (\n    <MainTreeProvider>\n      <InternalDialog ref={ref} open={open} {...rest} />\n    </MainTreeProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_PANEL_TAG = 'div' as const\ntype PanelRenderPropArg = {\n  open: boolean\n}\n\nexport type DialogPanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = Props<\n  TTag,\n  PanelRenderPropArg,\n  never,\n  { transition?: boolean }\n>\n\nfunction PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n  props: DialogPanelProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let { id = `headlessui-dialog-panel-${internalId}`, transition = false, ...theirProps } = props\n  let [{ dialogState, unmount }, state] = useDialogContext('Dialog.Panel')\n  let panelRef = useSyncRefs(ref, state.panelRef)\n\n  let slot = useSlot<PanelRenderPropArg>({ open: dialogState === DialogStates.Open })\n\n  // Prevent the click events inside the Dialog.Panel from bubbling through the React Tree which\n  // could submit wrapping <form> elements even if we portalled the Dialog.\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    event.stopPropagation()\n  })\n\n  let ourProps = {\n    ref: panelRef,\n    id,\n    onClick: handleClick,\n  }\n\n  let Wrapper = transition ? TransitionChild : Fragment\n  let wrapperProps = transition ? { unmount } : {}\n\n  let render = useRender()\n\n  return (\n    <Wrapper {...wrapperProps}>\n      {render({\n        ourProps,\n        theirProps,\n        slot,\n        defaultTag: DEFAULT_PANEL_TAG,\n        name: 'Dialog.Panel',\n      })}\n    </Wrapper>\n  )\n}\n\n// ---\n\nlet DEFAULT_BACKDROP_TAG = 'div' as const\ntype BackdropRenderPropArg = {\n  open: boolean\n}\n\nexport type DialogBackdropProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> = Props<\n  TTag,\n  BackdropRenderPropArg,\n  never,\n  { transition?: boolean }\n>\n\nfunction BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(\n  props: DialogBackdropProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { transition = false, ...theirProps } = props\n  let [{ dialogState, unmount }] = useDialogContext('Dialog.Backdrop')\n\n  let slot = useSlot<BackdropRenderPropArg>({ open: dialogState === DialogStates.Open })\n\n  let ourProps = { ref, 'aria-hidden': true }\n\n  let Wrapper = transition ? TransitionChild : Fragment\n  let wrapperProps = transition ? { unmount } : {}\n\n  let render = useRender()\n\n  return (\n    <Wrapper {...wrapperProps}>\n      {render({\n        ourProps,\n        theirProps,\n        slot,\n        defaultTag: DEFAULT_BACKDROP_TAG,\n        name: 'Dialog.Backdrop',\n      })}\n    </Wrapper>\n  )\n}\n\n// ---\n\nlet DEFAULT_TITLE_TAG = 'h2' as const\ntype TitleRenderPropArg = {\n  open: boolean\n}\n\nexport type DialogTitleProps<TTag extends ElementType = typeof DEFAULT_TITLE_TAG> = Props<\n  TTag,\n  TitleRenderPropArg\n>\n\nfunction TitleFn<TTag extends ElementType = typeof DEFAULT_TITLE_TAG>(\n  props: DialogTitleProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let { id = `headlessui-dialog-title-${internalId}`, ...theirProps } = props\n  let [{ dialogState, setTitleId }] = useDialogContext('Dialog.Title')\n\n  let titleRef = useSyncRefs(ref)\n\n  useEffect(() => {\n    setTitleId(id)\n    return () => setTitleId(null)\n  }, [id, setTitleId])\n\n  let slot = useSlot<TitleRenderPropArg>({ open: dialogState === DialogStates.Open })\n\n  let ourProps = { ref: titleRef, id }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_TITLE_TAG,\n    name: 'Dialog.Title',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentDialog extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(\n    props: DialogProps<TTag> & RefProp<typeof DialogFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDialogPanel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n    props: DialogPanelProps<TTag> & RefProp<typeof PanelFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDialogBackdrop extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(\n    props: DialogBackdropProps<TTag> & RefProp<typeof BackdropFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDialogTitle extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TITLE_TAG>(\n    props: DialogTitleProps<TTag> & RefProp<typeof TitleFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDialogDescription extends _internal_ComponentDescription {}\n\nlet DialogRoot = forwardRefWithAs(DialogFn) as _internal_ComponentDialog\nexport let DialogPanel = forwardRefWithAs(PanelFn) as _internal_ComponentDialogPanel\nexport let DialogBackdrop = forwardRefWithAs(BackdropFn) as _internal_ComponentDialogBackdrop\nexport let DialogTitle = forwardRefWithAs(TitleFn) as _internal_ComponentDialogTitle\n/** @deprecated use `<Description>` instead of `<DialogDescription>` */\nexport let DialogDescription = Description as _internal_ComponentDialogDescription\n\nexport let Dialog = Object.assign(DialogRoot, {\n  /** @deprecated use `<DialogPanel>` instead of `<Dialog.Panel>` */\n  Panel: DialogPanel,\n  /** @deprecated use `<DialogTitle>` instead of `<Dialog.Title>` */\n  Title: DialogTitle,\n  /** @deprecated use `<Description>` instead of `<Dialog.Description>` */\n  Description: Description as _internal_ComponentDialogDescription,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/dialog-description/dialog-description.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../dialog/dialog'\nexport { DialogDescription } from '../dialog/dialog'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/dialog-panel/dialog-panel.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../dialog/dialog'\nexport { DialogPanel } from '../dialog/dialog'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/dialog-title/dialog-title.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../dialog/dialog'\nexport { DialogTitle } from '../dialog/dialog'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/disclosure/disclosure.test.tsx",
    "content": "import { render, waitFor } from '@testing-library/react'\nimport React, { Fragment, Suspense, createElement, useEffect, useRef } from 'react'\nimport {\n  DisclosureState,\n  assertActiveElement,\n  assertDisclosureButton,\n  assertDisclosurePanel,\n  getByText,\n  getDisclosureButton,\n  getDisclosurePanel,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, MouseButton, click, focus, press } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Transition } from '../transition/transition'\nimport { Disclosure, DisclosureButton, DisclosurePanel } from './disclosure'\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['Disclosure.Button', Disclosure.Button],\n    ['Disclosure.Panel', Disclosure.Panel],\n  ])(\n    'should error when we are using a <%s /> without a parent <Disclosure />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component as any))).toThrow(\n        `<${name} /> is missing a parent <Disclosure /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Disclosure without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Disclosure', () => {\n    it(\n      'should be possible to render a Disclosure using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            {({ open }) => (\n              <>\n                <Disclosure.Button>Trigger</Disclosure.Button>\n                <Disclosure.Panel>Panel is: {open ? 'open' : 'closed'}</Disclosure.Panel>\n              </>\n            )}\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })\n      })\n    )\n\n    it('should be possible to render a Disclosure in an open state by default', async () => {\n      render(\n        <Disclosure defaultOpen>\n          {({ open }) => (\n            <>\n              <Disclosure.Button>Trigger</Disclosure.Button>\n              <Disclosure.Panel>Panel is: {open ? 'open' : 'closed'}</Disclosure.Panel>\n            </>\n          )}\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })\n\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n    })\n\n    it(\n      'should expose a close function that closes the disclosure',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            {({ close }) => (\n              <>\n                <Disclosure.Button>Trigger</Disclosure.Button>\n                <Disclosure.Panel>\n                  <button onClick={() => close()}>Close me</button>\n                </Disclosure.Panel>\n              </>\n            )}\n          </Disclosure>\n        )\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the Disclosure.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button id=\"test\">restorable</button>\n            <Disclosure>\n              {({ close }) => (\n                <>\n                  <Disclosure.Button>Trigger</Disclosure.Button>\n                  <Disclosure.Panel>\n                    <button onClick={() => close(document.getElementById('test')!)}>\n                      Close me\n                    </button>\n                  </Disclosure.Panel>\n                </>\n              )}\n            </Disclosure>\n          </>\n        )\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a ref',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let elementRef = useRef(null)\n          return (\n            <>\n              <button ref={elementRef}>restorable</button>\n              <Disclosure>\n                {({ close }) => (\n                  <>\n                    <Disclosure.Button>Trigger</Disclosure.Button>\n                    <Disclosure.Panel>\n                      <button onClick={() => close(elementRef)}>Close me</button>\n                    </Disclosure.Panel>\n                  </>\n                )}\n              </Disclosure>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it('should not crash when using Suspense boundaries', async () => {\n      render(\n        <Disclosure defaultOpen={true}>\n          <Disclosure.Button>Click me!</Disclosure.Button>\n          <Disclosure.Panel>\n            <Suspense fallback={null}>\n              <p>Hi there</p>\n            </Suspense>\n          </Disclosure.Panel>\n        </Disclosure>\n      )\n    })\n  })\n\n  describe('Disclosure.Button', () => {\n    it(\n      'should be possible to render a Disclosure.Button using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>{(slot) => <>{JSON.stringify(slot)}</>}</Disclosure.Button>\n            <Disclosure.Panel></Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            hover: false,\n            active: false,\n            disabled: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            hover: false,\n            active: false,\n            disabled: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Disclosure.Button using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button as=\"div\" role=\"button\">\n              {(slot) => <>{JSON.stringify(slot)}</>}\n            </Disclosure.Button>\n            <Disclosure.Panel />\n          </Disclosure>\n        )\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            hover: false,\n            active: false,\n            disabled: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            hover: false,\n            active: false,\n            disabled: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n\n    it('should behave as a close button when used inside of the Disclosure.Panel', async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Open</Disclosure.Button>\n          <Disclosure.Panel>\n            <Disclosure.Button>Close</Disclosure.Button>\n          </Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      await click(getByText('Open'))\n\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      await click(getByText('Close'))\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n\n    it('should behave as a close button when used inside of the Disclosure.Panel (defaultOpen)', async () => {\n      render(\n        <Disclosure defaultOpen>\n          <Disclosure.Button>Open</Disclosure.Button>\n          <Disclosure.Panel>\n            <Disclosure.Button>Close</Disclosure.Button>\n          </Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      await click(getByText('Close'))\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n          </Disclosure>\n        )\n\n        expect(getDisclosureButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button type=\"submit\">Trigger</Disclosure.Button>\n          </Disclosure>\n        )\n\n        expect(getDisclosureButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n        let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n          <button ref={ref} {...props} />\n        ))\n\n        render(\n          <Disclosure>\n            <Disclosure.Button as={CustomButton}>Trigger</Disclosure.Button>\n          </Disclosure>\n        )\n\n        expect(getDisclosureButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button as=\"div\">Trigger</Disclosure.Button>\n          </Disclosure>\n        )\n\n        expect(getDisclosureButton()).not.toHaveAttribute('type')\n      })\n\n      it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n        let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n          <div ref={ref} {...props} />\n        ))\n\n        render(\n          <Disclosure>\n            <Disclosure.Button as={CustomButton}>Trigger</Disclosure.Button>\n          </Disclosure>\n        )\n\n        expect(getDisclosureButton()).not.toHaveAttribute('type')\n      })\n    })\n\n    it(\n      'should be possible to render a DisclosureButton using as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <DisclosureButton as={Fragment}>\n              <button>Toggle</button>\n            </DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n  })\n\n  describe('Disclosure.Panel', () => {\n    it(\n      'should be possible to render Disclosure.Panel using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>{(slot) => <>{JSON.stringify(slot)}</>}</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n\n    it('should be possible to always render the Disclosure.Panel if we provide it a `static` prop', () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel static>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      // Let's verify that the Disclosure is already there\n      expect(getDisclosurePanel()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the Disclosure.Panel', async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel unmount={false}>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleHidden })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })\n\n      // Let's open the Disclosure, to see if it is not hidden anymore\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      // Let's re-click the Disclosure, to see if it is hidden again\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleHidden })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })\n    })\n\n    it(\n      'should expose a close function that closes the disclosure',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>\n              {({ close }) => <button onClick={() => close()}>Close me</button>}\n            </Disclosure.Panel>\n          </Disclosure>\n        )\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the Disclosure.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button id=\"test\">restorable</button>\n            <Disclosure>\n              <Disclosure.Button>Trigger</Disclosure.Button>\n              <Disclosure.Panel>\n                {({ close }) => (\n                  <button onClick={() => close(document.getElementById('test')!)}>Close me</button>\n                )}\n              </Disclosure.Panel>\n            </Disclosure>\n          </>\n        )\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a ref',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let elementRef = useRef(null)\n          return (\n            <>\n              <button ref={elementRef}>restorable</button>\n              <Disclosure>\n                <Disclosure.Button>Trigger</Disclosure.Button>\n                <Disclosure.Panel>\n                  {({ close }) => <button onClick={() => close(elementRef)}>Close me</button>}\n                </Disclosure.Panel>\n              </Disclosure>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n  })\n})\n\ndescribe('Composition', () => {\n  function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {\n    useEffect(() => {\n      fn(`Mounting - ${name}`)\n      return () => {\n        fn(`Unmounting - ${name}`)\n      }\n    }, [fn, name])\n    return null\n  }\n\n  it(\n    'should be possible to control the Disclosure.Panel by wrapping it in a Transition component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Debug name=\"Disclosure\" fn={orderFn} />\n          <Transition>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Disclosure.Panel>\n              <Transition.Child as=\"div\">\n                <Debug name=\"Transition.Child\" fn={orderFn} />\n              </Transition.Child>\n            </Disclosure.Panel>\n          </Transition>\n        </Disclosure>\n      )\n\n      // Verify the Disclosure is hidden\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Open the Disclosure component\n      await click(getDisclosureButton())\n\n      // Verify the Disclosure is visible\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      // Unmount the full tree\n      await click(getDisclosureButton())\n\n      // Wait for all transitions to finish\n      await nextFrame()\n      await nextFrame()\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Disclosure'],\n        ['Mounting - Transition'],\n        ['Mounting - Transition.Child'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Transition.Child'],\n      ])\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the Disclosure with Enter',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Contents</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Open disclosure\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n\n        // Close disclosure\n        await press(Keys.Enter)\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to open the disclosure with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button disabled>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Content</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Try to open the disclosure\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the disclosure with Enter when the disclosure is open',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Contents</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Open disclosure\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n\n        // Close disclosure\n        await press(Keys.Enter)\n\n        // Verify it is closed again\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the disclosure with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Contents</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Open disclosure\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n\n    it(\n      'should not be possible to open the disclosure with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button disabled>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Contents</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Try to open the disclosure\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the disclosure with Space when the disclosure is open',\n      suppressConsoleLogs(async () => {\n        render(\n          <Disclosure>\n            <Disclosure.Button>Trigger</Disclosure.Button>\n            <Disclosure.Panel>Contents</Disclosure.Panel>\n          </Disclosure>\n        )\n\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getDisclosureButton())\n\n        // Open disclosure\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n\n        // Close disclosure\n        await press(Keys.Space)\n\n        // Verify it is closed again\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to open a disclosure on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Open disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is open\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n    })\n  )\n\n  it(\n    'should not be possible to open a disclosure on right click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Open disclosure\n      await click(getDisclosureButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open a disclosure on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button disabled>Trigger</Disclosure.Button>\n          <Disclosure.Panel>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Try to open the disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is still closed\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a disclosure on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n          <Disclosure.Panel>Contents</Disclosure.Panel>\n        </Disclosure>\n      )\n\n      // Open disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is open\n      assertDisclosureButton({ state: DisclosureState.Visible })\n\n      // Click to close\n      await click(getDisclosureButton())\n\n      // Verify it is closed\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close the Disclosure by clicking on a Disclosure.Button inside a Disclosure.Panel',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <Disclosure.Button>Open</Disclosure.Button>\n          <Disclosure.Panel>\n            <Disclosure.Button>Close</Disclosure.Button>\n          </Disclosure.Panel>\n        </Disclosure>\n      )\n\n      // Open the disclosure\n      await click(getDisclosureButton())\n\n      let closeBtn = getByText('Close')\n\n      expect(closeBtn).not.toHaveAttribute('id')\n      expect(closeBtn).not.toHaveAttribute('aria-controls')\n      expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n      // The close button should close the disclosure\n      await click(closeBtn)\n\n      // Verify it is closed\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Verify we restored the Open button\n      assertActiveElement(getDisclosureButton())\n    })\n  )\n})\n\ndescribe('transitions', () => {\n  it(\n    'should be possible to close the Disclosure when using the `transition` prop',\n    suppressConsoleLogs(async () => {\n      render(\n        <Disclosure>\n          <DisclosureButton>Toggle</DisclosureButton>\n          <DisclosurePanel transition>Contents</DisclosurePanel>\n        </Disclosure>\n      )\n\n      // Focus the button\n      await focus(getDisclosureButton())\n\n      // Ensure the button is focused\n      assertActiveElement(getDisclosureButton())\n\n      // Open the disclosure\n      await click(getDisclosureButton())\n\n      // Ensure the disclosure is visible\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      // Close the disclosure\n      await click(getDisclosureButton())\n\n      // Wait for the transition to finish, and the disclosure to close\n      await waitFor(() => {\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n\n      // Ensure the button got the restored focus\n      assertActiveElement(getDisclosureButton())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/disclosure/disclosure.tsx",
    "content": "'use client'\n\n// WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  Fragment,\n  createContext,\n  useContext,\n  useEffect,\n  useMemo,\n  useReducer,\n  useRef,\n  useState,\n  type ContextType,\n  type Dispatch,\n  type ElementType,\n  type MutableRefObject,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useSlot } from '../../hooks/use-slot'\nimport { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { CloseProvider } from '../../internal/close-provider'\nimport {\n  OpenClosedProvider,\n  ResetOpenClosedProvider,\n  State,\n  useOpenClosed,\n} from '../../internal/open-closed'\nimport type { Props } from '../../types'\nimport { isDisabledReactIssue7711 } from '../../utils/bugs'\nimport * as DOM from '../../utils/dom'\nimport { match } from '../../utils/match'\nimport { getOwnerDocument } from '../../utils/owner'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  isFragment,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { startTransition } from '../../utils/start-transition'\nimport { Keys } from '../keyboard'\n\nenum DisclosureStates {\n  Open,\n  Closed,\n}\n\ninterface StateDefinition {\n  disclosureState: DisclosureStates\n\n  buttonElement: HTMLButtonElement | null\n  panelElement: HTMLElement | null\n\n  buttonId: string | null\n  panelId: string | null\n}\n\nenum ActionTypes {\n  ToggleDisclosure,\n  CloseDisclosure,\n\n  SetButtonId,\n  SetPanelId,\n\n  SetButtonElement,\n  SetPanelElement,\n}\n\ntype Actions =\n  | { type: ActionTypes.ToggleDisclosure }\n  | { type: ActionTypes.CloseDisclosure }\n  | { type: ActionTypes.SetButtonId; buttonId: string | null }\n  | { type: ActionTypes.SetPanelId; panelId: string | null }\n  | { type: ActionTypes.SetButtonElement; element: HTMLButtonElement | null }\n  | { type: ActionTypes.SetPanelElement; element: HTMLElement | null }\n\nlet reducers: {\n  [P in ActionTypes]: (\n    state: StateDefinition,\n    action: Extract<Actions, { type: P }>\n  ) => StateDefinition\n} = {\n  [ActionTypes.ToggleDisclosure]: (state) => ({\n    ...state,\n    disclosureState: match(state.disclosureState, {\n      [DisclosureStates.Open]: DisclosureStates.Closed,\n      [DisclosureStates.Closed]: DisclosureStates.Open,\n    }),\n  }),\n  [ActionTypes.CloseDisclosure]: (state) => {\n    if (state.disclosureState === DisclosureStates.Closed) return state\n    return { ...state, disclosureState: DisclosureStates.Closed }\n  },\n  [ActionTypes.SetButtonId](state, action) {\n    if (state.buttonId === action.buttonId) return state\n    return { ...state, buttonId: action.buttonId }\n  },\n  [ActionTypes.SetPanelId](state, action) {\n    if (state.panelId === action.panelId) return state\n    return { ...state, panelId: action.panelId }\n  },\n  [ActionTypes.SetButtonElement](state, action) {\n    if (state.buttonElement === action.element) return state\n    return { ...state, buttonElement: action.element }\n  },\n  [ActionTypes.SetPanelElement](state, action) {\n    if (state.panelElement === action.element) return state\n    return { ...state, panelElement: action.element }\n  },\n}\n\nlet DisclosureContext = createContext<[StateDefinition, Dispatch<Actions>] | null>(null)\nDisclosureContext.displayName = 'DisclosureContext'\n\nfunction useDisclosureContext(component: string) {\n  let context = useContext(DisclosureContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Disclosure /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureContext)\n    throw err\n  }\n  return context\n}\n\nlet DisclosureAPIContext = createContext<{\n  close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void\n} | null>(null)\nDisclosureAPIContext.displayName = 'DisclosureAPIContext'\n\nfunction useDisclosureAPIContext(component: string) {\n  let context = useContext(DisclosureAPIContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Disclosure /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureAPIContext)\n    throw err\n  }\n  return context\n}\n\nlet DisclosurePanelContext = createContext<string | null>(null)\nDisclosurePanelContext.displayName = 'DisclosurePanelContext'\n\nfunction useDisclosurePanelContext() {\n  return useContext(DisclosurePanelContext)\n}\n\nfunction stateReducer(state: StateDefinition, action: Actions) {\n  return match(action.type, reducers, state, action)\n}\n\n// ---\n\nlet DEFAULT_DISCLOSURE_TAG = Fragment\ntype DisclosureRenderPropArg = {\n  open: boolean\n  close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void\n}\ntype DisclosurePropsWeControl = never\n\nexport type DisclosureProps<TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG> = Props<\n  TTag,\n  DisclosureRenderPropArg,\n  DisclosurePropsWeControl,\n  {\n    defaultOpen?: boolean\n  }\n>\n\nfunction DisclosureFn<TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG>(\n  props: DisclosureProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { defaultOpen = false, ...theirProps } = props\n  let internalDisclosureRef = useRef<HTMLElement | null>(null)\n  let disclosureRef = useSyncRefs(\n    ref,\n    optionalRef(\n      (ref) => {\n        internalDisclosureRef.current = ref\n      },\n      props.as === undefined || isFragment(props.as)\n    )\n  )\n\n  let reducerBag = useReducer(stateReducer, {\n    disclosureState: defaultOpen ? DisclosureStates.Open : DisclosureStates.Closed,\n    buttonElement: null,\n    panelElement: null,\n    buttonId: null,\n    panelId: null,\n  } as StateDefinition)\n  let [{ disclosureState, buttonId }, dispatch] = reducerBag\n\n  let close = useEvent(\n    (focusableElement?: HTMLOrSVGElement | MutableRefObject<HTMLOrSVGElement | null>) => {\n      dispatch({ type: ActionTypes.CloseDisclosure })\n      let ownerDocument = getOwnerDocument(internalDisclosureRef.current)\n      if (!ownerDocument) return\n      if (!buttonId) return\n\n      let restoreElement = (() => {\n        if (!focusableElement) return ownerDocument.getElementById(buttonId)\n        if (DOM.isHTMLorSVGElement(focusableElement)) return focusableElement\n        if ('current' in focusableElement && DOM.isHTMLorSVGElement(focusableElement.current)) {\n          return focusableElement.current\n        }\n\n        return ownerDocument.getElementById(buttonId)\n      })()\n\n      restoreElement?.focus()\n    }\n  )\n\n  let api = useMemo<ContextType<typeof DisclosureAPIContext>>(() => ({ close }), [close])\n\n  let slot = useSlot<DisclosureRenderPropArg>({\n    open: disclosureState === DisclosureStates.Open,\n    close,\n  })\n\n  let ourProps = {\n    ref: disclosureRef,\n  }\n\n  let render = useRender()\n\n  return (\n    <DisclosureContext.Provider value={reducerBag}>\n      <DisclosureAPIContext.Provider value={api}>\n        <CloseProvider value={close}>\n          <OpenClosedProvider\n            value={match(disclosureState, {\n              [DisclosureStates.Open]: State.Open,\n              [DisclosureStates.Closed]: State.Closed,\n            })}\n          >\n            {render({\n              ourProps,\n              theirProps,\n              slot,\n              defaultTag: DEFAULT_DISCLOSURE_TAG,\n              name: 'Disclosure',\n            })}\n          </OpenClosedProvider>\n        </CloseProvider>\n      </DisclosureAPIContext.Provider>\n    </DisclosureContext.Provider>\n  )\n}\n\n// ---\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\ntype ButtonRenderPropArg = {\n  open: boolean\n  hover: boolean\n  active: boolean\n  disabled: boolean\n  focus: boolean\n  autofocus: boolean\n}\ntype ButtonPropsWeControl = 'aria-controls' | 'aria-expanded'\n\nexport type DisclosureButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    disabled?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: DisclosureButtonProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-disclosure-button-${internalId}`,\n    disabled = false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let [state, dispatch] = useDisclosureContext('Disclosure.Button')\n  let panelContext = useDisclosurePanelContext()\n  let isWithinPanel = panelContext === null ? false : panelContext === state.panelId\n\n  let internalButtonRef = useRef<HTMLButtonElement | null>(null)\n  let buttonRef = useSyncRefs(\n    internalButtonRef,\n    ref,\n    useEvent((element) => {\n      if (isWithinPanel) return\n      return dispatch({ type: ActionTypes.SetButtonElement, element })\n    })\n  )\n\n  useEffect(() => {\n    if (isWithinPanel) return\n\n    dispatch({ type: ActionTypes.SetButtonId, buttonId: id })\n    return () => {\n      dispatch({ type: ActionTypes.SetButtonId, buttonId: null })\n    }\n  }, [id, dispatch, isWithinPanel])\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    if (isWithinPanel) {\n      if (state.disclosureState === DisclosureStates.Closed) return\n\n      switch (event.key) {\n        case Keys.Space:\n        case Keys.Enter:\n          event.preventDefault()\n          event.stopPropagation()\n          dispatch({ type: ActionTypes.ToggleDisclosure })\n          state.buttonElement?.focus()\n          break\n      }\n    } else {\n      switch (event.key) {\n        case Keys.Space:\n        case Keys.Enter:\n          event.preventDefault()\n          event.stopPropagation()\n          dispatch({ type: ActionTypes.ToggleDisclosure })\n          break\n      }\n    }\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Space:\n        // Required for firefox, event.preventDefault() in handleKeyDown for\n        // the Space key doesn't cancel the handleKeyUp, which in turn\n        // triggers a *click*.\n        event.preventDefault()\n        break\n    }\n  })\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return\n    if (disabled) return\n\n    if (isWithinPanel) {\n      dispatch({ type: ActionTypes.ToggleDisclosure })\n      state.buttonElement?.focus()\n    } else {\n      dispatch({ type: ActionTypes.ToggleDisclosure })\n    }\n  })\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<ButtonRenderPropArg>({\n    open: state.disclosureState === DisclosureStates.Open,\n    hover,\n    active,\n    disabled,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let type = useResolveButtonType(props, state.buttonElement)\n  let ourProps = isWithinPanel\n    ? mergeProps(\n        {\n          ref: buttonRef,\n          type,\n          disabled: disabled || undefined,\n          autoFocus,\n          onKeyDown: handleKeyDown,\n          onClick: handleClick,\n        },\n        focusProps,\n        hoverProps,\n        pressProps\n      )\n    : mergeProps(\n        {\n          ref: buttonRef,\n          id,\n          type,\n          'aria-expanded': state.disclosureState === DisclosureStates.Open,\n          'aria-controls': state.panelElement ? state.panelId : undefined,\n          disabled: disabled || undefined,\n          autoFocus,\n          onKeyDown: handleKeyDown,\n          onKeyUp: handleKeyUp,\n          onClick: handleClick,\n        },\n        focusProps,\n        hoverProps,\n        pressProps\n      )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BUTTON_TAG,\n    name: 'Disclosure.Button',\n  })\n}\n\n// ---\n\nlet DEFAULT_PANEL_TAG = 'div' as const\ntype PanelRenderPropArg = {\n  open: boolean\n  close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void\n}\ntype DisclosurePanelPropsWeControl = never\n\nlet PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type DisclosurePanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = Props<\n  TTag,\n  PanelRenderPropArg,\n  DisclosurePanelPropsWeControl,\n  { transition?: boolean } & PropsForFeatures<typeof PanelRenderFeatures>\n>\n\nfunction PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n  props: DisclosurePanelProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-disclosure-panel-${internalId}`,\n    transition = false,\n    ...theirProps\n  } = props\n  let [state, dispatch] = useDisclosureContext('Disclosure.Panel')\n  let { close } = useDisclosureAPIContext('Disclosure.Panel')\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localPanelElement, setLocalPanelElement] = useState<HTMLElement | null>(null)\n\n  let panelRef = useSyncRefs(\n    ref,\n    useEvent((element) => {\n      startTransition(() => dispatch({ type: ActionTypes.SetPanelElement, element }))\n    }),\n    setLocalPanelElement\n  )\n\n  useEffect(() => {\n    dispatch({ type: ActionTypes.SetPanelId, panelId: id })\n    return () => {\n      dispatch({ type: ActionTypes.SetPanelId, panelId: null })\n    }\n  }, [id, dispatch])\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localPanelElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : state.disclosureState === DisclosureStates.Open\n  )\n\n  let slot = useSlot<PanelRenderPropArg>({\n    open: state.disclosureState === DisclosureStates.Open,\n    close,\n  })\n\n  let ourProps = {\n    ref: panelRef,\n    id,\n    ...transitionDataAttributes(transitionData),\n  }\n\n  let render = useRender()\n\n  return (\n    <ResetOpenClosedProvider>\n      <DisclosurePanelContext.Provider value={state.panelId}>\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_PANEL_TAG,\n          features: PanelRenderFeatures,\n          visible,\n          name: 'Disclosure.Panel',\n        })}\n      </DisclosurePanelContext.Provider>\n    </ResetOpenClosedProvider>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentDisclosure extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG>(\n    props: DisclosureProps<TTag> & RefProp<typeof DisclosureFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDisclosureButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: DisclosureButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentDisclosurePanel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n    props: DisclosurePanelProps<TTag> & RefProp<typeof PanelFn>\n  ): React.JSX.Element\n}\n\nlet DisclosureRoot = forwardRefWithAs(DisclosureFn) as _internal_ComponentDisclosure\nexport let DisclosureButton = forwardRefWithAs(ButtonFn) as _internal_ComponentDisclosureButton\nexport let DisclosurePanel = forwardRefWithAs(PanelFn) as _internal_ComponentDisclosurePanel\n\nexport let Disclosure = Object.assign(DisclosureRoot, {\n  /** @deprecated use `<DisclosureButton>` instead of `<Disclosure.Button>` */\n  Button: DisclosureButton,\n  /** @deprecated use `<DisclosurePanel>` instead of `<Disclosure.Panel>` */\n  Panel: DisclosurePanel,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/disclosure-button/disclosure-button.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../disclosure/disclosure'\nexport { DisclosureButton } from '../disclosure/disclosure'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/disclosure-panel/disclosure-panel.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../disclosure/disclosure'\nexport { DisclosurePanel } from '../disclosure/disclosure'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/field/field.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React from 'react'\nimport { Fieldset } from '../fieldset/fieldset'\nimport { Field } from './field'\n\ndescribe('Rendering', () => {\n  it('should render a `Field` component', async () => {\n    let { container } = render(\n      <Field>\n        <input />\n      </Field>\n    )\n\n    expect(container.firstChild).not.toHaveAttribute('aria-disabled', 'true')\n  })\n\n  it('should render a `Field` component with a render prop', async () => {\n    let { container } = render(\n      <Field>\n        {(slot) => {\n          return (\n            <div data-slot={JSON.stringify(slot)}>\n              <input />\n            </div>\n          )\n        }}\n      </Field>\n    )\n\n    expect(container.querySelector('[data-slot]')?.getAttribute('data-slot')).toEqual(\n      JSON.stringify({ disabled: false })\n    )\n    expect(container.firstChild).not.toHaveAttribute('aria-disabled', 'true')\n  })\n\n  it('should add `aria-disabled` when a `Field` is disabled', async () => {\n    let { container } = render(\n      <Field disabled>\n        <input />\n      </Field>\n    )\n\n    expect(container.firstChild).toHaveAttribute('aria-disabled', 'true')\n  })\n\n  it('should inherit the `disabled` state from a parent `Fieldset`', async () => {\n    let { container } = render(\n      <Fieldset disabled>\n        <Field>\n          <input />\n        </Field>\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n    let field = fieldset?.firstChild\n\n    expect(fieldset).toHaveAttribute('disabled')\n    expect(field).toHaveAttribute('aria-disabled', 'true')\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/field/field.tsx",
    "content": "'use client'\n\nimport React, { type ElementType, type Ref } from 'react'\nimport { useId } from '../../hooks/use-id'\nimport { useSlot } from '../../hooks/use-slot'\nimport { DisabledProvider, useDisabled } from '../../internal/disabled'\nimport { FormFieldsProvider } from '../../internal/form-fields'\nimport { IdProvider } from '../../internal/id'\nimport type { Props } from '../../types'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\nimport { useDescriptions } from '../description/description'\nimport { useLabels } from '../label/label'\n\nlet DEFAULT_FIELD_TAG = 'div' as const\n\ntype FieldRenderPropArg = {}\ntype FieldPropsWeControl = never\n\nexport type FieldProps<TTag extends ElementType = typeof DEFAULT_FIELD_TAG> = Props<\n  TTag,\n  FieldRenderPropArg,\n  FieldPropsWeControl,\n  {\n    disabled?: boolean\n  }\n>\n\nfunction FieldFn<TTag extends ElementType = typeof DEFAULT_FIELD_TAG>(\n  props: FieldProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let inputId = `headlessui-control-${useId()}`\n\n  let [labelledby, LabelProvider] = useLabels()\n  let [describedBy, DescriptionProvider] = useDescriptions()\n\n  let providedDisabled = useDisabled()\n  let { disabled = providedDisabled || false, ...theirProps } = props\n\n  let slot = useSlot<FieldRenderPropArg>({ disabled })\n\n  let ourProps = {\n    ref,\n    disabled: disabled || undefined,\n    'aria-disabled': disabled || undefined,\n  }\n\n  let render = useRender()\n\n  return (\n    <DisabledProvider value={disabled}>\n      <LabelProvider value={labelledby}>\n        <DescriptionProvider value={describedBy}>\n          <IdProvider id={inputId}>\n            {render({\n              ourProps,\n              theirProps: {\n                ...theirProps,\n                children: (\n                  <FormFieldsProvider>\n                    {typeof theirProps.children === 'function'\n                      ? theirProps.children(slot)\n                      : theirProps.children}\n                  </FormFieldsProvider>\n                ),\n              },\n              slot,\n              defaultTag: DEFAULT_FIELD_TAG,\n              name: 'Field',\n            })}\n          </IdProvider>\n        </DescriptionProvider>\n      </LabelProvider>\n    </DisabledProvider>\n  )\n}\n\nexport interface _internal_ComponentField extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_FIELD_TAG>(\n    props: FieldProps<TTag> & RefProp<typeof FieldFn>\n  ): React.JSX.Element\n}\n\nexport let Field = forwardRefWithAs(FieldFn) as _internal_ComponentField\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/fieldset/fieldset.test.tsx",
    "content": "import { render, screen } from '@testing-library/react'\nimport React from 'react'\nimport {\n  assertLinkedWithLabel,\n  assertNotLinkedWithLabel,\n  getControl,\n  getLabels,\n} from '../../test-utils/accessibility-assertions'\nimport { Field } from '../field/field'\nimport { Input } from '../input/input'\nimport { Label } from '../label/label'\nimport { Legend } from '../legend/legend'\nimport { Fieldset } from './fieldset'\n\ndescribe('Rendering', () => {\n  it('should render a `Fieldset` component', async () => {\n    let { container } = render(\n      <Fieldset>\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n\n    expect(fieldset).toBeInstanceOf(HTMLFieldSetElement)\n    expect(fieldset).not.toHaveAttribute('role', 'group')\n  })\n\n  it('should render a `Fieldset` using a custom component', async () => {\n    let { container } = render(\n      <Fieldset as=\"span\">\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n\n    expect(fieldset).toBeInstanceOf(HTMLSpanElement)\n    expect(fieldset).toHaveAttribute('role', 'group')\n  })\n\n  it('should forward the `disabled` attribute when disabling the `Fieldset`', async () => {\n    let { container } = render(\n      <Fieldset disabled>\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n\n    expect(fieldset).toHaveAttribute('disabled')\n  })\n\n  it('should add an `aria-disabled` attribute when disabling the `Fieldset` when using another element via the `as` prop', async () => {\n    let { container } = render(\n      <Fieldset as=\"span\" disabled>\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n\n    expect(fieldset).toHaveAttribute('aria-disabled', 'true')\n  })\n\n  it('should make nested inputs disabled when the fieldset is disabled', async () => {\n    let { container } = render(\n      <Fieldset disabled>\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild\n\n    expect(fieldset?.firstChild).toBeDisabled()\n  })\n\n  it('should link a `Fieldset` to a nested `Legend`', async () => {\n    let { container } = render(\n      <Fieldset>\n        <Legend>My Legend</Legend>\n        <input />\n      </Fieldset>\n    )\n\n    let fieldset = container.firstChild as HTMLElement\n\n    assertLinkedWithLabel(fieldset, getLabels())\n  })\n\n  it('should not link a `Label` inside a `Field` to the `Fieldset`', async () => {\n    render(\n      <Fieldset>\n        <Legend>My Legend</Legend>\n\n        <Field>\n          <Label>My Label</Label>\n          <Input />\n        </Field>\n      </Fieldset>\n    )\n\n    let legend = screen.getByText('My Legend')\n    let label = screen.getByText('My Label')\n\n    let fieldset = legend.parentElement\n    let field = label.parentElement\n\n    let input = getControl()\n\n    // The fieldset should be linked with the legend\n    assertLinkedWithLabel(fieldset, legend)\n\n    // The input/control should be linked with the label\n    assertLinkedWithLabel(input, label)\n\n    // The fieldset should not be linked with the label\n    assertNotLinkedWithLabel(fieldset, label)\n\n    // The input/control should not be linked with the legend\n    assertNotLinkedWithLabel(input, legend)\n\n    // The field should not be linked with anything\n    assertNotLinkedWithLabel(field, legend)\n    assertNotLinkedWithLabel(field, label)\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/fieldset/fieldset.tsx",
    "content": "'use client'\n\nimport React, { type ElementType, type Ref } from 'react'\nimport { useResolvedTag } from '../../hooks/use-resolved-tag'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { DisabledProvider, useDisabled } from '../../internal/disabled'\nimport type { Props } from '../../types'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\nimport { useLabels } from '../label/label'\n\nlet DEFAULT_FIELDSET_TAG = 'fieldset' as const\n\ntype FieldsetRenderPropArg = {}\ntype FieldsetPropsWeControl = 'aria-labelledby' | 'aria-disabled' | 'role'\n\nexport type FieldsetProps<TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG> = Props<\n  TTag,\n  FieldsetRenderPropArg,\n  FieldsetPropsWeControl,\n  {\n    disabled?: boolean\n  }\n>\n\nfunction FieldsetFn<TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG>(\n  props: FieldsetProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let providedDisabled = useDisabled()\n  let { disabled = providedDisabled || false, ...theirProps } = props\n\n  let [tag, resolveTag] = useResolvedTag(props.as ?? DEFAULT_FIELDSET_TAG)\n  let fieldsetRef = useSyncRefs(ref, resolveTag)\n\n  let [labelledBy, LabelProvider] = useLabels()\n\n  let slot = useSlot<FieldsetRenderPropArg>({ disabled })\n\n  let ourProps =\n    tag === 'fieldset'\n      ? {\n          ref: fieldsetRef,\n          'aria-labelledby': labelledBy,\n          disabled: disabled || undefined,\n        }\n      : {\n          ref: fieldsetRef,\n          role: 'group',\n          'aria-labelledby': labelledBy,\n          'aria-disabled': disabled || undefined,\n        }\n\n  let render = useRender()\n\n  return (\n    <DisabledProvider value={disabled}>\n      <LabelProvider>\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_FIELDSET_TAG,\n          name: 'Fieldset',\n        })}\n      </LabelProvider>\n    </DisabledProvider>\n  )\n}\n\nexport interface _internal_ComponentFieldset extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG>(\n    props: FieldsetProps<TTag> & RefProp<typeof FieldsetFn>\n  ): React.JSX.Element\n}\n\nexport let Fieldset = forwardRefWithAs(FieldsetFn) as _internal_ComponentFieldset\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/focus-trap/focus-trap.test.tsx",
    "content": "import { render, screen } from '@testing-library/react'\nimport React, { useRef, useState } from 'react'\nimport { assertActiveElement } from '../../test-utils/accessibility-assertions'\nimport { Keys, click, focus, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { FocusTrap } from './focus-trap'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nit('should focus the first focusable element inside the FocusTrap', async () => {\n  render(\n    <FocusTrap>\n      <button>Trigger</button>\n    </FocusTrap>\n  )\n\n  await nextFrame()\n\n  assertActiveElement(screen.getByText('Trigger'))\n})\n\nit('should focus the autoFocus element inside the FocusTrap if that exists', async () => {\n  render(\n    <FocusTrap>\n      <input id=\"a\" type=\"text\" />\n      <input id=\"b\" type=\"text\" autoFocus />\n      <input id=\"c\" type=\"text\" />\n    </FocusTrap>\n  )\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('b'))\n})\n\nit('should focus the initialFocus element inside the FocusTrap if that exists', async () => {\n  function Example() {\n    let initialFocusRef = useRef<HTMLInputElement | null>(null)\n\n    return (\n      <FocusTrap initialFocus={initialFocusRef}>\n        <input id=\"a\" type=\"text\" />\n        <input id=\"b\" type=\"text\" />\n        <input id=\"c\" type=\"text\" ref={initialFocusRef} />\n      </FocusTrap>\n    )\n  }\n\n  render(<Example />)\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('c'))\n})\n\nit('should focus the initialFocus element inside the FocusTrap even if another element has autoFocus', async () => {\n  function Example() {\n    let initialFocusRef = useRef<HTMLInputElement | null>(null)\n\n    return (\n      <FocusTrap initialFocus={initialFocusRef}>\n        <input id=\"a\" type=\"text\" />\n        <input id=\"b\" type=\"text\" autoFocus />\n        <input id=\"c\" type=\"text\" ref={initialFocusRef} />\n      </FocusTrap>\n    )\n  }\n\n  render(<Example />)\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('c'))\n})\n\nit('should warn when there is no focusable element inside the FocusTrap', async () => {\n  let spy = jest.spyOn(console, 'warn').mockImplementation(jest.fn())\n\n  function Example() {\n    return (\n      <FocusTrap>\n        <span>Nothing to see here...</span>\n      </FocusTrap>\n    )\n  }\n\n  render(<Example />)\n\n  await nextFrame()\n\n  expect(spy.mock.calls[0][0]).toBe('There are no focusable elements inside the <FocusTrap />')\n  spy.mockReset()\n})\n\n// TODO: Figure out once 2.0 alpha is released\nit.skip(\n  'should not be possible to programmatically escape the focus trap (if there is only 1 focusable element)',\n  suppressConsoleLogs(async () => {\n    function Example() {\n      return (\n        <>\n          <input id=\"a\" autoFocus />\n\n          <FocusTrap>\n            <input id=\"b\" />\n          </FocusTrap>\n        </>\n      )\n    }\n\n    render(<Example />)\n\n    await nextFrame()\n\n    let [a, b] = Array.from(document.querySelectorAll('input'))\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-b is still the active element\n    assertActiveElement(b)\n\n    // Try to move focus\n    await focus(a)\n\n    // Ensure that input-b is still the active element\n    assertActiveElement(b)\n\n    // Click on an element within the FocusTrap\n    await click(b)\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n  })\n)\n\n// TODO: Figure out once 2.0 alpha is released\nit.skip(\n  'should not be possible to programmatically escape the focus trap',\n  suppressConsoleLogs(async () => {\n    function Example() {\n      return (\n        <>\n          <input id=\"a\" autoFocus />\n\n          <FocusTrap>\n            <input id=\"b\" />\n            <input id=\"c\" />\n            <input id=\"d\" />\n          </FocusTrap>\n        </>\n      )\n    }\n\n    render(<Example />)\n\n    await nextFrame()\n\n    let [a, b, c, d] = Array.from(document.querySelectorAll('input'))\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Try to move focus\n    await focus(a)\n\n    // Ensure that input-c is still the active element\n    assertActiveElement(c)\n\n    // Click on an element within the FocusTrap\n    await click(b)\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Try to move focus again\n    await focus(a)\n\n    // Ensure that input-b is still the active element\n    assertActiveElement(b)\n\n    // Focus on an element within the FocusTrap\n    await focus(d)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Try to move focus again\n    await focus(a)\n\n    // Ensure that input-d is still the active element\n    assertActiveElement(d)\n  })\n)\n\nit('should restore the previously focused element, before entering the FocusTrap, after the FocusTrap unmounts', async () => {\n  function Example() {\n    let [visible, setVisible] = useState(false)\n\n    return (\n      <>\n        <input id=\"item-1\" autoFocus />\n        <button id=\"item-2\" onClick={() => setVisible(true)}>\n          Open modal\n        </button>\n\n        {visible && (\n          <FocusTrap>\n            <button id=\"item-3\" onClick={() => setVisible(false)}>\n              Close\n            </button>\n          </FocusTrap>\n        )}\n      </>\n    )\n  }\n\n  render(<Example />)\n\n  await nextFrame()\n\n  // The input should have focus by default because of the autoFocus prop\n  assertActiveElement(document.getElementById('item-1'))\n\n  // Open the modal\n  await click(document.getElementById('item-2')) // This will also focus this button\n\n  // Ensure that the first item inside the focus trap is focused\n  assertActiveElement(document.getElementById('item-3'))\n\n  // Close the modal\n  await click(document.getElementById('item-3'))\n\n  // Ensure that we restored focus correctly\n  assertActiveElement(document.getElementById('item-2'))\n})\n\nit('should stay in the FocusTrap when using `tab`, if there is only 1 focusable element', async () => {\n  render(\n    <>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should stay in the FocusTrap when using `shift+tab`, if there is only 1 focusable element', async () => {\n  render(\n    <>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous (loop around!)\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible tab to the next focusable element within the focus trap', async () => {\n  render(\n    <>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\">Item C</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-c'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible shift+tab to the previous focusable element within the focus trap', async () => {\n  render(\n    <>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\">Item C</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous (loop around!)\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-c'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should skip the initial \"hidden\" elements within the focus trap', async () => {\n  render(\n    <>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\" style={{ display: 'none' }}>\n          Item A\n        </button>\n        <button id=\"item-b\" style={{ display: 'none' }}>\n          Item B\n        </button>\n        <button id=\"item-c\">Item C</button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item C should be focused because the FocusTrap had to skip the first 2\n  assertActiveElement(document.getElementById('item-c'))\n})\n\nit('should be possible skip \"hidden\" elements within the focus trap', async () => {\n  render(\n    <>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\" style={{ display: 'none' }}>\n          Item C\n        </button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Notice that we skipped item-c\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-d'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible skip disabled elements within the focus trap', async () => {\n  render(\n    <>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\" disabled>\n          Item C\n        </button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </>\n  )\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Notice that we skipped item-c\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-d'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\n// TODO: Figure out once 2.0 alpha is released\nit.skip(\n  'should not be possible to programmatically escape the focus trap',\n  suppressConsoleLogs(async () => {\n    function Example() {\n      return (\n        <>\n          <input id=\"a\" autoFocus />\n\n          <FocusTrap>\n            <input id=\"b\" />\n            <input id=\"c\" />\n            <input id=\"d\" />\n          </FocusTrap>\n        </>\n      )\n    }\n\n    render(<Example />)\n\n    await nextFrame()\n\n    let [a, b, c, d] = Array.from(document.querySelectorAll('input'))\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Try to move focus\n    await focus(a)\n\n    // Ensure that input-c is still the active element\n    assertActiveElement(c)\n\n    // Click on an element within the FocusTrap\n    await click(b)\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Try to move focus again\n    await focus(a)\n\n    // Ensure that input-b is still the active element\n    assertActiveElement(b)\n\n    // Focus on an element within the FocusTrap\n    await focus(d)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Try to move focus again\n    await focus(a)\n\n    // Ensure that input-d is still the active element\n    assertActiveElement(d)\n  })\n)\n\nit(\n  'should not be possible to escape the FocusTrap due to strange tabIndex usage',\n  suppressConsoleLogs(async () => {\n    function Example() {\n      return (\n        <>\n          <div tabIndex={-1}>\n            <input tabIndex={2} id=\"a\" />\n            <input tabIndex={1} id=\"b\" />\n          </div>\n\n          <FocusTrap>\n            <input tabIndex={1} id=\"c\" />\n            <input id=\"d\" />\n          </FocusTrap>\n        </>\n      )\n    }\n\n    render(<Example />)\n\n    await nextFrame()\n\n    let [_a, _b, c, d] = Array.from(document.querySelectorAll('input'))\n\n    // First item in the FocusTrap should be the active one\n    assertActiveElement(c)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Let's go the other way\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n  })\n)\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx",
    "content": "'use client'\n\nimport React, {\n  useRef,\n  type ElementType,\n  type MutableRefObject,\n  type FocusEvent as ReactFocusEvent,\n  type Ref,\n} from 'react'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useEventListener } from '../../hooks/use-event-listener'\nimport { useIsMounted } from '../../hooks/use-is-mounted'\nimport { useIsTopLayer } from '../../hooks/use-is-top-layer'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { Direction as TabDirection, useTabDirection } from '../../hooks/use-tab-direction'\nimport { useWatch } from '../../hooks/use-watch'\nimport { Hidden, HiddenFeatures } from '../../internal/hidden'\nimport type { Props } from '../../types'\nimport { history } from '../../utils/active-element-history'\nimport * as DOM from '../../utils/dom'\nimport { Focus, FocusResult, focusElement, focusIn } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { isActiveElement } from '../../utils/owner'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\n\ntype Containers =\n  // Lazy resolved containers\n  | (() => Iterable<Element>)\n\n  // List of containers\n  | MutableRefObject<Set<MutableRefObject<Element | null>>>\n\nfunction resolveContainers(containers?: Containers): Set<Element> {\n  if (!containers) return new Set<HTMLElement>()\n  if (typeof containers === 'function') return new Set(containers())\n\n  let all = new Set<Element>()\n  for (let container of containers.current) {\n    if (DOM.isElement(container.current)) {\n      all.add(container.current)\n    }\n  }\n  return all\n}\n\nlet DEFAULT_FOCUS_TRAP_TAG = 'div' as const\n\nexport enum FocusTrapFeatures {\n  /** No features enabled for the focus trap. */\n  None = 0,\n\n  /** Ensure that we move focus initially into the container. */\n  InitialFocus = 1 << 0,\n\n  /** Ensure that pressing `Tab` and `Shift+Tab` is trapped within the container. */\n  TabLock = 1 << 1,\n\n  /** Ensure that programmatically moving focus outside of the container is disallowed. */\n  FocusLock = 1 << 2,\n\n  /** Ensure that we restore the focus when unmounting the focus trap. */\n  RestoreFocus = 1 << 3,\n\n  /** Initial focus should look for the `data-autofocus` */\n  AutoFocus = 1 << 4,\n}\n\ntype FocusTrapRenderPropArg = {}\ntype FocusTrapPropsWeControl = never\n\nexport type FocusTrapProps<TTag extends ElementType = typeof DEFAULT_FOCUS_TRAP_TAG> = Props<\n  TTag,\n  FocusTrapRenderPropArg,\n  FocusTrapPropsWeControl,\n  {\n    initialFocus?: MutableRefObject<HTMLElement | null>\n    // A fallback element to focus, but this element will be skipped when tabbing around. This is\n    // only done for focusing a fallback parent container (e.g.: A `Dialog`, but you want to tab\n    // *inside* the dialog excluding the dialog itself).\n    initialFocusFallback?: MutableRefObject<HTMLElement | null>\n    features?: FocusTrapFeatures\n    containers?: Containers\n  }\n>\n\nfunction FocusTrapFn<TTag extends ElementType = typeof DEFAULT_FOCUS_TRAP_TAG>(\n  props: FocusTrapProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let container = useRef<HTMLElement | null>(null)\n  let focusTrapRef = useSyncRefs(container, ref)\n  let {\n    initialFocus,\n    initialFocusFallback,\n    containers,\n    features = FocusTrapFeatures.InitialFocus |\n      FocusTrapFeatures.TabLock |\n      FocusTrapFeatures.FocusLock |\n      FocusTrapFeatures.RestoreFocus,\n    ...theirProps\n  } = props\n\n  if (!useServerHandoffComplete()) {\n    features = FocusTrapFeatures.None\n  }\n\n  let ownerDocument = useOwnerDocument(container.current)\n\n  useRestoreFocus(features, { ownerDocument })\n  let previousActiveElement = useInitialFocus(features, {\n    ownerDocument,\n    container,\n    initialFocus,\n    initialFocusFallback,\n  })\n\n  useFocusLock(features, { ownerDocument, container, containers, previousActiveElement })\n\n  let direction = useTabDirection()\n  let handleFocus = useEvent((e: ReactFocusEvent) => {\n    if (!DOM.isHTMLElement(container.current)) return\n    let el = container.current\n\n    // TODO: Cleanup once we are using real browser tests\n    let wrapper = process.env.NODE_ENV === 'test' ? microTask : (cb: Function) => cb()\n    wrapper(() => {\n      match(direction.current, {\n        [TabDirection.Forwards]: () => {\n          focusIn(el, Focus.First, {\n            skipElements: [e.relatedTarget, initialFocusFallback] as HTMLElement[],\n          })\n        },\n        [TabDirection.Backwards]: () => {\n          focusIn(el, Focus.Last, {\n            skipElements: [e.relatedTarget, initialFocusFallback] as HTMLElement[],\n          })\n        },\n      })\n    })\n  })\n\n  let tabLockEnabled = useIsTopLayer(\n    Boolean(features & FocusTrapFeatures.TabLock),\n    'focus-trap#tab-lock'\n  )\n\n  let d = useDisposables()\n  let recentlyUsedTabKey = useRef(false)\n  let ourProps = {\n    ref: focusTrapRef,\n    onKeyDown(e: KeyboardEvent) {\n      if (e.key == 'Tab') {\n        recentlyUsedTabKey.current = true\n        d.requestAnimationFrame(() => {\n          recentlyUsedTabKey.current = false\n        })\n      }\n    },\n    onBlur(e: ReactFocusEvent) {\n      if (!(features & FocusTrapFeatures.FocusLock)) return\n\n      let allContainers = resolveContainers(containers)\n      if (DOM.isHTMLElement(container.current)) allContainers.add(container.current)\n\n      let relatedTarget = e.relatedTarget\n      if (!DOM.isHTMLorSVGElement(relatedTarget)) return\n\n      // Known guards, leave them alone!\n      if (relatedTarget.dataset.headlessuiFocusGuard === 'true') {\n        return\n      }\n\n      // Blur is triggered due to focus on relatedTarget, and the relatedTarget is not inside any\n      // of the dialog containers. In other words, let's move focus back in!\n      if (!contains(allContainers, relatedTarget)) {\n        // Was the blur invoked via the keyboard? Redirect to the next in line.\n        if (recentlyUsedTabKey.current) {\n          focusIn(\n            container.current as HTMLElement,\n            match(direction.current, {\n              [TabDirection.Forwards]: () => Focus.Next,\n              [TabDirection.Backwards]: () => Focus.Previous,\n            }) | Focus.WrapAround,\n            { relativeTo: e.target as HTMLElement }\n          )\n        }\n\n        // It was invoked via something else (e.g.: click, programmatically, ...). Redirect to the\n        // previous active item in the FocusTrap\n        else if (DOM.isHTMLorSVGElement(e.target)) {\n          focusElement(e.target)\n        }\n      }\n    },\n  }\n\n  let render = useRender()\n\n  return (\n    <>\n      {tabLockEnabled && (\n        <Hidden\n          as=\"button\"\n          type=\"button\"\n          data-headlessui-focus-guard\n          onFocus={handleFocus}\n          features={HiddenFeatures.Focusable}\n        />\n      )}\n      {render({\n        ourProps,\n        theirProps,\n        defaultTag: DEFAULT_FOCUS_TRAP_TAG,\n        name: 'FocusTrap',\n      })}\n      {tabLockEnabled && (\n        <Hidden\n          as=\"button\"\n          type=\"button\"\n          data-headlessui-focus-guard\n          onFocus={handleFocus}\n          features={HiddenFeatures.Focusable}\n        />\n      )}\n    </>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentFocusTrap extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_FOCUS_TRAP_TAG>(\n    props: FocusTrapProps<TTag> & RefProp<typeof FocusTrapFn>\n  ): React.JSX.Element\n}\n\nlet FocusTrapRoot = forwardRefWithAs(FocusTrapFn) as _internal_ComponentFocusTrap\n\nexport let FocusTrap = Object.assign(FocusTrapRoot, {\n  /** @deprecated use `FocusTrapFeatures` instead of `FocusTrap.features` */\n  features: FocusTrapFeatures,\n})\n\n// ---\n\nfunction useRestoreElement(enabled: boolean = true) {\n  let localHistory = useRef(history.slice())\n\n  useWatch(\n    ([newEnabled], [oldEnabled]) => {\n      // We are disabling the restore element, so we need to clear it.\n      if (oldEnabled === true && newEnabled === false) {\n        // However, let's schedule it in a microTask, so that we can still read the value in the\n        // places where we are restoring the focus.\n        microTask(() => {\n          localHistory.current.splice(0)\n        })\n      }\n\n      // We are enabling the restore element, so we need to set it to the last \"focused\" element.\n      if (oldEnabled === false && newEnabled === true) {\n        localHistory.current = history.slice()\n      }\n    },\n    [enabled, history, localHistory]\n  )\n\n  // We want to return the last element that is still connected to the DOM, so we can restore the\n  // focus to it.\n  return useEvent(() => {\n    return localHistory.current.find((x) => x != null && x.isConnected) ?? null\n  })\n}\n\nfunction useRestoreFocus(\n  features: FocusTrapFeatures,\n  { ownerDocument }: { ownerDocument: Document | null }\n) {\n  let enabled = Boolean(features & FocusTrapFeatures.RestoreFocus)\n\n  let getRestoreElement = useRestoreElement(enabled)\n\n  // Restore the focus to the previous element when `enabled` becomes false again\n  useWatch(() => {\n    if (enabled) return\n\n    if (isActiveElement(ownerDocument?.body)) {\n      focusElement(getRestoreElement())\n    }\n  }, [enabled])\n\n  // Restore the focus to the previous element when the component is unmounted\n  useOnUnmount(() => {\n    if (!enabled) return\n\n    focusElement(getRestoreElement())\n  })\n}\n\nfunction useInitialFocus(\n  features: FocusTrapFeatures,\n  {\n    ownerDocument,\n    container,\n    initialFocus,\n    initialFocusFallback,\n  }: {\n    ownerDocument: Document | null\n    container: MutableRefObject<HTMLElement | null>\n    initialFocus?: MutableRefObject<HTMLElement | null>\n    initialFocusFallback?: MutableRefObject<HTMLElement | null>\n  }\n) {\n  let previousActiveElement = useRef<HTMLElement | null>(null)\n  let enabled = useIsTopLayer(\n    Boolean(features & FocusTrapFeatures.InitialFocus),\n    'focus-trap#initial-focus'\n  )\n\n  let mounted = useIsMounted()\n\n  // Handle initial focus\n  useWatch(() => {\n    // No focus management needed\n    if (features === FocusTrapFeatures.None) {\n      return\n    }\n\n    if (!enabled) {\n      // If we are disabling the initialFocus, then we should focus the fallback element if one is\n      // provided. This is needed to ensure _something_ is focused. Typically a wrapping element\n      // (e.g.: `Dialog` component).\n      //\n      // Note: we _don't_ want to move focus to the `initialFocus` ref, because the `InitialFocus`\n      // feature is disabled.\n      if (initialFocusFallback?.current) {\n        focusElement(initialFocusFallback.current)\n      }\n\n      return\n    }\n    let containerElement = container.current\n    if (!containerElement) return\n\n    // Delaying the focus to the next microtask ensures that a few conditions are true:\n    // - The container is rendered\n    // - Transitions could be started\n    // If we don't do this, then focusing an element will immediately cancel any transitions. This\n    // is not ideal because transitions will look broken.\n    // There is an additional issue with doing this immediately. The FocusTrap is used inside a\n    // Dialog, the Dialog is rendered inside of a Portal and the Portal is rendered at the end of\n    // the `document.body`. This means that the moment we call focus, the browser immediately\n    // tries to focus the element, which will still be at the bottom resulting in the page to\n    // scroll down. Delaying this will prevent the page to scroll down entirely.\n    microTask(() => {\n      if (!mounted.current) {\n        return\n      }\n\n      let activeElement = ownerDocument?.activeElement as HTMLElement\n\n      if (initialFocus?.current) {\n        if (initialFocus?.current === activeElement) {\n          previousActiveElement.current = activeElement\n          return // Initial focus ref is already the active element\n        }\n      } else if (containerElement!.contains(activeElement)) {\n        previousActiveElement.current = activeElement\n        return // Already focused within Dialog\n      }\n\n      // Try to focus the initialFocus ref\n      if (initialFocus?.current) {\n        focusElement(initialFocus.current)\n      } else {\n        if (features & FocusTrapFeatures.AutoFocus) {\n          // Try to focus the first focusable element with `Focus.AutoFocus` feature enabled\n          if (focusIn(containerElement!, Focus.First | Focus.AutoFocus) !== FocusResult.Error) {\n            return // Worked, bail\n          }\n        }\n\n        // Try to focus the first focusable element.\n        else if (focusIn(containerElement!, Focus.First) !== FocusResult.Error) {\n          return // Worked, bail\n        }\n\n        // Try the fallback\n        if (initialFocusFallback?.current) {\n          focusElement(initialFocusFallback.current)\n          if (ownerDocument?.activeElement === initialFocusFallback.current) {\n            return // Worked, bail\n          }\n        }\n\n        // Nothing worked\n        console.warn('There are no focusable elements inside the <FocusTrap />')\n      }\n\n      previousActiveElement.current = ownerDocument?.activeElement as HTMLElement\n    })\n  }, [initialFocusFallback, enabled, features])\n\n  return previousActiveElement\n}\n\nfunction useFocusLock(\n  features: FocusTrapFeatures,\n  {\n    ownerDocument,\n    container,\n    containers,\n    previousActiveElement,\n  }: {\n    ownerDocument: Document | null\n    container: MutableRefObject<HTMLElement | null>\n    containers?: Containers\n    previousActiveElement: MutableRefObject<HTMLOrSVGElement | null>\n  }\n) {\n  let mounted = useIsMounted()\n  let enabled = Boolean(features & FocusTrapFeatures.FocusLock)\n\n  // Prevent programmatically escaping the container\n  useEventListener(\n    ownerDocument?.defaultView,\n    'focus',\n    (event) => {\n      if (!enabled) return\n      if (!mounted.current) return\n\n      let allContainers = resolveContainers(containers)\n      if (DOM.isHTMLElement(container.current)) allContainers.add(container.current)\n\n      let previous = previousActiveElement.current\n      if (!previous) return\n\n      let toElement = event.target as HTMLElement | null\n\n      if (DOM.isHTMLElement(toElement)) {\n        if (!contains(allContainers, toElement)) {\n          event.preventDefault()\n          event.stopPropagation()\n          focusElement(previous)\n        } else {\n          previousActiveElement.current = toElement\n          focusElement(toElement)\n        }\n      } else {\n        focusElement(previousActiveElement.current)\n      }\n    },\n    true\n  )\n}\n\nfunction contains(containers: Set<Element>, element: Element) {\n  for (let container of containers) {\n    if (container.contains(element)) return true\n  }\n\n  return false\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/focus-trap-features/focus-trap-features.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../focus-trap/focus-trap'\nexport { FocusTrapFeatures } from '../focus-trap/focus-trap'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/input/input.test.tsx",
    "content": "import { getInput } from '../../test-utils/accessibility-assertions'\nimport { focus, type, word } from '../../test-utils/interactions'\nimport {\n  commonControlScenarios,\n  commonFormScenarios,\n  commonRenderingScenarios,\n} from '../../test-utils/scenarios'\nimport { Input } from './input'\n\ncommonRenderingScenarios(Input, { getElement: getInput })\ncommonControlScenarios(Input)\ncommonFormScenarios(Input, {\n  async performUserInteraction(input) {\n    await focus(input)\n    await type(word('alice'))\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/input/input.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { type ElementType, type Ref } from 'react'\nimport { useId } from '../../hooks/use-id'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useDisabled } from '../../internal/disabled'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { useLabelledBy } from '../label/label'\n\nlet DEFAULT_INPUT_TAG = 'input' as const\n\ntype InputRenderPropArg = {\n  disabled: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n  invalid: boolean\n}\ntype InputPropsWeControl = 'aria-labelledby' | 'aria-describedby'\n\nexport type InputProps<TTag extends ElementType = typeof DEFAULT_INPUT_TAG> = Props<\n  TTag,\n  InputRenderPropArg,\n  InputPropsWeControl,\n  {\n    disabled?: boolean\n    invalid?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction InputFn<TTag extends ElementType = typeof DEFAULT_INPUT_TAG>(\n  props: InputProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-input-${internalId}`,\n    disabled = providedDisabled || false,\n    autoFocus = false,\n    invalid = false,\n    ...theirProps\n  } = props\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let { isFocused: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let ourProps = mergeProps(\n    {\n      ref,\n      id,\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      'aria-invalid': invalid ? 'true' : undefined,\n      disabled: disabled || undefined,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps\n  )\n\n  let slot = useSlot<InputRenderPropArg>({ disabled, invalid, hover, focus, autofocus: autoFocus })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_INPUT_TAG,\n    name: 'Input',\n  })\n}\n\nexport interface _internal_ComponentInput extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_INPUT_TAG>(\n    props: InputProps<TTag> & RefProp<typeof InputFn>\n  ): React.JSX.Element\n}\n\nexport let Input = forwardRefWithAs(InputFn) as _internal_ComponentInput\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/keyboard.ts",
    "content": "// TODO: This must already exist somewhere, right? 🤔\n// Ref: https://www.w3.org/TR/uievents-key/#named-key-attribute-values\nexport enum Keys {\n  Space = ' ',\n  Enter = 'Enter',\n  Escape = 'Escape',\n  Backspace = 'Backspace',\n  Delete = 'Delete',\n\n  ArrowLeft = 'ArrowLeft',\n  ArrowUp = 'ArrowUp',\n  ArrowRight = 'ArrowRight',\n  ArrowDown = 'ArrowDown',\n\n  Home = 'Home',\n  End = 'End',\n\n  PageUp = 'PageUp',\n  PageDown = 'PageDown',\n\n  Tab = 'Tab',\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/label/__snapshots__/label.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to use a LabelProvider and a single Label, and have them linked 1`] = `\n<div\n  aria-labelledby=\"headlessui-label-1\"\n>\n  <div\n    data-headlessui-state=\"\"\n    id=\"headlessui-label-1\"\n  >\n    I am a label\n  </div>\n  <span>\n    Contents\n  </span>\n</div>\n`;\n\nexports[`should be possible to use a LabelProvider and multiple Label components, and have them linked 1`] = `\n<div\n  aria-labelledby=\"headlessui-label-1 headlessui-label-2\"\n>\n  <div\n    data-headlessui-state=\"\"\n    id=\"headlessui-label-1\"\n  >\n    I am a label\n  </div>\n  <span>\n    Contents\n  </span>\n  <div\n    data-headlessui-state=\"\"\n    id=\"headlessui-label-2\"\n  >\n    I am also a label\n  </div>\n</div>\n`;\n\nexports[`should be possible to use a LabelProvider without using a Label 1`] = `\n<div>\n  No label\n</div>\n`;\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/label/label.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { type ReactNode } from 'react'\nimport { Label, useLabels } from './label'\n\njest.mock('../../hooks/use-id')\n\nit('should be possible to use a LabelProvider without using a Label', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [labelledby, LabelProvider] = useLabels()\n\n    return (\n      <LabelProvider>\n        <div aria-labelledby={labelledby}>{props.children}</div>\n      </LabelProvider>\n    )\n  }\n\n  function Example() {\n    return <Component>No label</Component>\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n\nit('should be possible to use a LabelProvider and a single Label, and have them linked', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [labelledby, LabelProvider] = useLabels()\n\n    return (\n      <LabelProvider>\n        <div aria-labelledby={labelledby}>{props.children}</div>\n      </LabelProvider>\n    )\n  }\n\n  function Example() {\n    return (\n      <Component>\n        <Label>I am a label</Label>\n        <span>Contents</span>\n      </Component>\n    )\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n\nit('should be possible to use a LabelProvider and multiple Label components, and have them linked', async () => {\n  function Component(props: { children: ReactNode }) {\n    let [labelledby, LabelProvider] = useLabels()\n\n    return (\n      <LabelProvider>\n        <div aria-labelledby={labelledby}>{props.children}</div>\n      </LabelProvider>\n    )\n  }\n\n  function Example() {\n    return (\n      <Component>\n        <Label>I am a label</Label>\n        <span>Contents</span>\n        <Label>I am also a label</Label>\n      </Component>\n    )\n  }\n\n  let { container } = render(<Example />)\n  expect(container.firstChild).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/label/label.tsx",
    "content": "'use client'\n\nimport React, {\n  createContext,\n  useContext,\n  useMemo,\n  useState,\n  type ElementType,\n  type MouseEvent as ReactMouseEvent,\n  type ReactNode,\n  type Ref,\n} from 'react'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useDisabled } from '../../internal/disabled'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport * as DOM from '../../utils/dom'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\n\n// ---\n\ninterface SharedData {\n  slot?: {}\n  name?: string\n  props?: Record<string, any>\n}\n\nlet LabelContext = createContext<\n  ({ value: string | undefined; register(value: string): () => void } & SharedData) | null\n>(null)\nLabelContext.displayName = 'LabelContext'\n\nexport function useLabelContext() {\n  let context = useContext(LabelContext)\n  if (context === null) {\n    let err = new Error('You used a <Label /> component, but it is not inside a relevant parent.')\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useLabelContext)\n    throw err\n  }\n  return context\n}\n\nexport function useLabelledBy(alwaysAvailableIds?: (string | undefined | null)[]) {\n  let labelIds = useContext(LabelContext)?.value ?? undefined\n  if ((alwaysAvailableIds?.length ?? 0) > 0) {\n    return [labelIds, ...alwaysAvailableIds!].filter(Boolean).join(' ')\n  }\n  return labelIds\n}\n\ninterface LabelProviderProps extends SharedData {\n  children: ReactNode\n  value?: string | undefined\n}\n\nexport function useLabels({ inherit = false } = {}): [\n  string | undefined,\n  (props: LabelProviderProps & { inherit?: boolean }) => React.JSX.Element,\n] {\n  let parentLabelledBy = useLabelledBy()\n  let [labelIds, setLabelIds] = useState<string[]>([])\n\n  let allLabelIds = inherit ? [parentLabelledBy, ...labelIds].filter(Boolean) : labelIds\n\n  return [\n    // The actual id's as string or undefined.\n    allLabelIds.length > 0 ? allLabelIds.join(' ') : undefined,\n\n    // The provider component\n    useMemo(() => {\n      return function LabelProvider(props: LabelProviderProps) {\n        let register = useEvent((value: string) => {\n          setLabelIds((existing) => [...existing, value])\n\n          return () => {\n            return setLabelIds((existing) => {\n              let clone = existing.slice()\n              let idx = clone.indexOf(value)\n              if (idx !== -1) clone.splice(idx, 1)\n              return clone\n            })\n          }\n        })\n\n        let contextBag = useMemo(\n          () => ({\n            register,\n            slot: props.slot,\n            name: props.name,\n            props: props.props,\n            value: props.value,\n          }),\n          [register, props.slot, props.name, props.props, props.value]\n        )\n\n        return <LabelContext.Provider value={contextBag}>{props.children}</LabelContext.Provider>\n      }\n    }, [setLabelIds]),\n  ]\n}\n\n// ---\n\nlet DEFAULT_LABEL_TAG = 'label' as const\n\nexport type LabelProps<TTag extends ElementType = typeof DEFAULT_LABEL_TAG> = Props<TTag> & {\n  passive?: boolean\n  htmlFor?: string\n}\n\nfunction LabelFn<TTag extends ElementType = typeof DEFAULT_LABEL_TAG>(\n  props: LabelProps<TTag>,\n  ref: Ref<HTMLLabelElement>\n) {\n  let internalId = useId()\n  let context = useLabelContext()\n  let providedHtmlFor = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = `headlessui-label-${internalId}`,\n    htmlFor = providedHtmlFor ?? context.props?.htmlFor,\n    passive = false,\n    ...theirProps\n  } = props\n  let labelRef = useSyncRefs(ref)\n\n  useIsoMorphicEffect(() => context.register(id), [id, context.register])\n\n  let handleClick = useEvent((e: ReactMouseEvent) => {\n    let current = e.currentTarget\n\n    // If a click happens on an interactive element inside of the label, then we\n    // don't want to trigger the label behavior and let the browser handle the\n    // click event.\n    //\n    // In a situation like:\n    //\n    // ```html\n    // <label>\n    //   I accept the\n    //   <a href=\"#\">terms and agreement</a>\n    //   <input type=\"checkbox\" />\n    // </label>\n    // ```\n    //\n    // Clicking on the link, should not check the checkbox, but open the link\n    // instead.\n    if (e.target !== e.currentTarget && DOM.isInteractiveElement(e.target)) {\n      return\n    }\n\n    // Labels connected to 'real' controls will already click the element. But we don't know that\n    // ahead of time. This will prevent the default click, such that only a single click happens\n    // instead of two. Otherwise this results in a visual no-op.\n    if (DOM.isHTMLLabelElement(current)) {\n      e.preventDefault()\n    }\n\n    // Ensure `onClick` from context is called\n    if (\n      context.props &&\n      'onClick' in context.props &&\n      typeof context.props.onClick === 'function'\n    ) {\n      context.props.onClick(e)\n    }\n\n    if (DOM.isHTMLLabelElement(current)) {\n      let target = document.getElementById(current.htmlFor)\n      if (target) {\n        // Bail if the target element is disabled\n        let actuallyDisabled = target.getAttribute('disabled')\n        if (actuallyDisabled === 'true' || actuallyDisabled === '') {\n          return\n        }\n\n        let ariaDisabled = target.getAttribute('aria-disabled')\n        if (ariaDisabled === 'true' || ariaDisabled === '') {\n          return\n        }\n\n        // Ensure we click the element this label is bound to. This is necessary for elements that\n        // immediately require state changes, e.g.: Radio & Checkbox inputs need to be checked (or\n        // unchecked).\n        if (\n          (DOM.isHTMLInputElement(target) &&\n            (target.type === 'file' || target.type === 'radio' || target.type === 'checkbox')) ||\n          target.role === 'radio' ||\n          target.role === 'checkbox' ||\n          target.role === 'switch'\n        ) {\n          target.click()\n        }\n\n        // Move focus to the element, this allows you to start using keyboard shortcuts since the\n        // bound element is now focused.\n        target.focus({ preventScroll: true })\n      }\n    }\n  })\n\n  let slot = useSlot({ ...context.slot, disabled: providedDisabled || false })\n\n  let ourProps = {\n    ref: labelRef,\n    ...context.props,\n    id,\n    htmlFor,\n    onClick: handleClick,\n  }\n\n  if (passive) {\n    if ('onClick' in ourProps) {\n      delete (ourProps as any)['htmlFor']\n      delete (ourProps as any)['onClick']\n    }\n\n    if ('onClick' in theirProps) {\n      delete (theirProps as any)['onClick']\n    }\n  }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: htmlFor ? DEFAULT_LABEL_TAG : 'div',\n    name: context.name || 'Label',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentLabel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_LABEL_TAG>(\n    props: LabelProps<TTag> & RefProp<typeof LabelFn>\n  ): React.JSX.Element\n}\n\nlet LabelRoot = forwardRefWithAs(LabelFn) as _internal_ComponentLabel\n\nexport let Label = Object.assign(LabelRoot, {\n  //\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/legend/legend.tsx",
    "content": "'use client'\n\nimport React, { type ElementType, type Ref } from 'react'\nimport type { Props } from '../../types'\nimport { forwardRefWithAs, type HasDisplayName, type RefProp } from '../../utils/render'\nimport { Label } from '../label/label'\n\nlet DEFAULT_LEGEND_TAG = Label\n\ntype LegendRenderPropArg = {}\ntype LegendPropsWeControl = never\n\nexport type LegendProps<TTag extends ElementType = typeof DEFAULT_LEGEND_TAG> = Props<\n  TTag,\n  LegendRenderPropArg,\n  LegendPropsWeControl,\n  {}\n>\n\nfunction LegendFn<TTag extends ElementType = typeof DEFAULT_LEGEND_TAG>(\n  props: LegendProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  // @ts-expect-error The props can still contain an `as` prop, but we are already passing an as\n  // prop as `div` (as a default). Now the ref is inferred as the ref for a `div`, but it can still\n  // be anything.\n  return <Label as=\"div\" ref={ref} {...props} />\n}\n\nexport interface _internal_ComponentLegend extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_LEGEND_TAG>(\n    props: LegendProps<TTag> & RefProp<typeof LegendFn>\n  ): React.JSX.Element\n}\n\nexport let Legend = forwardRefWithAs(LegendFn) as _internal_ComponentLegend\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox/listbox-machine-glue.tsx",
    "content": "import { createContext, useContext, useMemo } from 'react'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { ListboxMachine } from './listbox-machine'\n\nexport const ListboxContext = createContext<ListboxMachine<unknown> | null>(null)\nexport function useListboxMachineContext<T>(component: string) {\n  let context = useContext(ListboxContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Listbox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxMachine)\n    throw err\n  }\n  return context as ListboxMachine<T>\n}\n\nexport function useListboxMachine({\n  id,\n  __demoMode = false,\n}: {\n  id: string\n  __demoMode?: boolean\n}) {\n  let machine = useMemo(() => ListboxMachine.new({ id, __demoMode }), [])\n  useOnUnmount(() => machine.dispose())\n  return machine\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox/listbox-machine.ts",
    "content": "import { Machine, batch } from '../../machine'\nimport { ActionTypes as StackActionTypes, stackMachines } from '../../machines/stack-machine'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport {\n  ElementPositionState,\n  computeVisualPosition,\n  detectMovement,\n} from '../../utils/element-movement'\nimport { sortByDomNode } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\n\ninterface MutableRefObject<T> {\n  current: T\n}\n\nexport enum ListboxStates {\n  Open,\n  Closed,\n}\n\nexport enum ValueMode {\n  Single,\n  Multi,\n}\n\nexport enum ActivationTrigger {\n  Pointer,\n  Other,\n}\n\ntype ListboxOptionDataRef<T> = MutableRefObject<{\n  textValue?: string\n  disabled: boolean\n  value: T\n  domRef: MutableRefObject<HTMLElement | null>\n}>\n\ninterface State<T> {\n  id: string\n\n  __demoMode: boolean\n\n  dataRef: MutableRefObject<{\n    value: unknown\n    disabled: boolean\n    invalid: boolean\n    mode: ValueMode\n    orientation: 'horizontal' | 'vertical'\n    onChange: (value: T) => void\n    compare(a: unknown, z: unknown): boolean\n    isSelected(value: unknown): boolean\n\n    optionsPropsRef: MutableRefObject<{\n      static: boolean\n      hold: boolean\n    }>\n\n    listRef: MutableRefObject<Map<string, HTMLElement | null>>\n  }>\n\n  listboxState: ListboxStates\n\n  options: { id: string; dataRef: ListboxOptionDataRef<T> }[]\n  searchQuery: string\n  activeOptionIndex: number | null\n  activationTrigger: ActivationTrigger\n\n  frozenValue: boolean\n\n  buttonElement: HTMLButtonElement | null\n  optionsElement: HTMLElement | null\n\n  pendingShouldSort: boolean\n  pendingFocus: { focus: Exclude<Focus, Focus.Specific> } | { focus: Focus.Specific; id: string }\n\n  // Track button to determine if it moved\n  buttonPositionState: ElementPositionState\n}\n\nexport enum ActionTypes {\n  OpenListbox,\n  CloseListbox,\n\n  GoToOption,\n  Search,\n  ClearSearch,\n  SelectOption,\n\n  RegisterOptions,\n  UnregisterOptions,\n\n  SetButtonElement,\n  SetOptionsElement,\n\n  SortOptions,\n\n  MarkButtonAsMoved,\n}\n\nfunction adjustOrderedState<T>(\n  state: State<T>,\n  adjustment: (options: State<T>['options']) => State<T>['options'] = (i) => i\n) {\n  let currentActiveOption =\n    state.activeOptionIndex !== null ? state.options[state.activeOptionIndex] : null\n\n  let sortedOptions = sortByDomNode(\n    adjustment(state.options.slice()),\n    (option) => option.dataRef.current.domRef.current\n  )\n\n  // If we inserted an option before the current active option then the active option index\n  // would be wrong. To fix this, we will re-lookup the correct index.\n  let adjustedActiveOptionIndex = currentActiveOption\n    ? sortedOptions.indexOf(currentActiveOption)\n    : null\n\n  // Reset to `null` in case the currentActiveOption was removed.\n  if (adjustedActiveOptionIndex === -1) {\n    adjustedActiveOptionIndex = null\n  }\n\n  return {\n    options: sortedOptions,\n    activeOptionIndex: adjustedActiveOptionIndex,\n  }\n}\n\ntype Actions<T> =\n  | { type: ActionTypes.CloseListbox }\n  | {\n      type: ActionTypes.OpenListbox\n      focus: { focus: Exclude<Focus, Focus.Specific> } | { focus: Focus.Specific; id: string }\n    }\n  | { type: ActionTypes.GoToOption; focus: Focus.Specific; id: string; trigger?: ActivationTrigger }\n  | {\n      type: ActionTypes.GoToOption\n      focus: Exclude<Focus, Focus.Specific>\n      trigger?: ActivationTrigger\n    }\n  | { type: ActionTypes.Search; value: string }\n  | { type: ActionTypes.ClearSearch }\n  | { type: ActionTypes.SelectOption; value: T }\n  | {\n      type: ActionTypes.RegisterOptions\n      options: { id: string; dataRef: ListboxOptionDataRef<T> }[]\n    }\n  | { type: ActionTypes.UnregisterOptions; options: string[] }\n  | { type: ActionTypes.SetButtonElement; element: HTMLButtonElement | null }\n  | { type: ActionTypes.SetOptionsElement; element: HTMLElement | null }\n  | { type: ActionTypes.SortOptions }\n  | { type: ActionTypes.MarkButtonAsMoved }\n\nlet reducers: {\n  [P in ActionTypes]: <T>(state: State<T>, action: Extract<Actions<T>, { type: P }>) => State<T>\n} = {\n  [ActionTypes.CloseListbox](state) {\n    if (state.dataRef.current.disabled) return state\n    if (state.listboxState === ListboxStates.Closed) return state\n    let buttonPositionState = state.buttonElement\n      ? ElementPositionState.Tracked(computeVisualPosition(state.buttonElement))\n      : state.buttonPositionState\n\n    return {\n      ...state,\n      activeOptionIndex: null,\n      pendingFocus: { focus: Focus.Nothing },\n      listboxState: ListboxStates.Closed,\n      __demoMode: false,\n      buttonPositionState,\n    }\n  },\n  [ActionTypes.OpenListbox](state, action) {\n    if (state.dataRef.current.disabled) return state\n    if (state.listboxState === ListboxStates.Open) return state\n\n    // Check if we have a selected value that we can make active\n    let activeOptionIndex = state.activeOptionIndex\n    let { isSelected } = state.dataRef.current\n    let optionIdx = state.options.findIndex((option) => isSelected(option.dataRef.current.value))\n\n    if (optionIdx !== -1) {\n      activeOptionIndex = optionIdx\n    }\n\n    return {\n      ...state,\n      frozenValue: false,\n      pendingFocus: action.focus,\n      listboxState: ListboxStates.Open,\n      activeOptionIndex,\n      __demoMode: false,\n      buttonPositionState: ElementPositionState.Idle,\n    }\n  },\n  [ActionTypes.GoToOption](state, action) {\n    if (state.dataRef.current.disabled) return state\n    if (state.listboxState === ListboxStates.Closed) return state\n\n    let base = {\n      ...state,\n      searchQuery: '',\n      activationTrigger: action.trigger ?? ActivationTrigger.Other,\n      __demoMode: false,\n    }\n\n    // Optimization:\n    //\n    // There is no need to sort the DOM nodes if we know that we don't want to focus anything\n    if (action.focus === Focus.Nothing) {\n      return {\n        ...base,\n        activeOptionIndex: null,\n      }\n    }\n\n    // Optimization:\n    //\n    // There is no need to sort the DOM nodes if we know exactly where to go\n    if (action.focus === Focus.Specific) {\n      return {\n        ...base,\n        activeOptionIndex: state.options.findIndex((o) => o.id === action.id),\n      }\n    }\n\n    // Optimization:\n    //\n    // If the current DOM node and the previous DOM node are next to each other,\n    // or if the previous DOM node is already the first DOM node, then we don't\n    // have to sort all the DOM nodes.\n    else if (action.focus === Focus.Previous) {\n      let activeOptionIdx = state.activeOptionIndex\n      if (activeOptionIdx !== null) {\n        let currentDom = state.options[activeOptionIdx].dataRef.current.domRef\n        let previousOptionIndex = calculateActiveIndex(action, {\n          resolveItems: () => state.options,\n          resolveActiveIndex: () => state.activeOptionIndex,\n          resolveId: (option) => option.id,\n          resolveDisabled: (option) => option.dataRef.current.disabled,\n        })\n        if (previousOptionIndex !== null) {\n          let previousDom = state.options[previousOptionIndex].dataRef.current.domRef\n          if (\n            // Next to each other\n            currentDom.current?.previousElementSibling === previousDom.current ||\n            // Or already the first element\n            previousDom.current?.previousElementSibling === null\n          ) {\n            return {\n              ...base,\n              activeOptionIndex: previousOptionIndex,\n            }\n          }\n        }\n      }\n    }\n\n    // Optimization:\n    //\n    // If the current DOM node and the next DOM node are next to each other, or\n    // if the next DOM node is already the last DOM node, then we don't have to\n    // sort all the DOM nodes.\n    else if (action.focus === Focus.Next) {\n      let activeOptionIdx = state.activeOptionIndex\n      if (activeOptionIdx !== null) {\n        let currentDom = state.options[activeOptionIdx].dataRef.current.domRef\n        let nextOptionIndex = calculateActiveIndex(action, {\n          resolveItems: () => state.options,\n          resolveActiveIndex: () => state.activeOptionIndex,\n          resolveId: (option) => option.id,\n          resolveDisabled: (option) => option.dataRef.current.disabled,\n        })\n        if (nextOptionIndex !== null) {\n          let nextDom = state.options[nextOptionIndex].dataRef.current.domRef\n          if (\n            // Next to each other\n            currentDom.current?.nextElementSibling === nextDom.current ||\n            // Or already the last element\n            nextDom.current?.nextElementSibling === null\n          ) {\n            return {\n              ...base,\n              activeOptionIndex: nextOptionIndex,\n            }\n          }\n        }\n      }\n    }\n\n    // Slow path:\n    //\n    // Ensure all the options are correctly sorted according to DOM position\n    let adjustedState = adjustOrderedState(state)\n    let activeOptionIndex = calculateActiveIndex(action, {\n      resolveItems: () => adjustedState.options,\n      resolveActiveIndex: () => adjustedState.activeOptionIndex,\n      resolveId: (option) => option.id,\n      resolveDisabled: (option) => option.dataRef.current.disabled,\n    })\n\n    return {\n      ...base,\n      ...adjustedState,\n      activeOptionIndex,\n    }\n  },\n  [ActionTypes.Search]: (state, action) => {\n    if (state.dataRef.current.disabled) return state\n    if (state.listboxState === ListboxStates.Closed) return state\n\n    let wasAlreadySearching = state.searchQuery !== ''\n    let offset = wasAlreadySearching ? 0 : 1\n\n    let searchQuery = state.searchQuery + action.value.toLowerCase()\n\n    let reOrderedOptions =\n      state.activeOptionIndex !== null\n        ? state.options\n            .slice(state.activeOptionIndex + offset)\n            .concat(state.options.slice(0, state.activeOptionIndex + offset))\n        : state.options\n\n    let matchingOption = reOrderedOptions.find(\n      (option) =>\n        !option.dataRef.current.disabled &&\n        option.dataRef.current.textValue?.startsWith(searchQuery)\n    )\n\n    let matchIdx = matchingOption ? state.options.indexOf(matchingOption) : -1\n\n    if (matchIdx === -1 || matchIdx === state.activeOptionIndex) return { ...state, searchQuery }\n    return {\n      ...state,\n      searchQuery,\n      activeOptionIndex: matchIdx,\n      activationTrigger: ActivationTrigger.Other,\n    }\n  },\n  [ActionTypes.ClearSearch](state) {\n    if (state.dataRef.current.disabled) return state\n    if (state.listboxState === ListboxStates.Closed) return state\n    if (state.searchQuery === '') return state\n    return { ...state, searchQuery: '' }\n  },\n  [ActionTypes.SelectOption](state) {\n    if (state.dataRef.current.mode === ValueMode.Single) {\n      // The moment you select a value in single value mode, we want to close\n      // the listbox and freeze the value to prevent UI flicker.\n      return { ...state, frozenValue: true }\n    }\n\n    // We have an event listener for `SelectOption`, but that will only be\n    // called when the state changes. In multi-value mode we don't have a state\n    // change but we still want to trigger the event listener. Therefore we\n    // return a new object to trigger that event.\n    //\n    // Not the cleanest, but that's why we have this, instead of just returning\n    // `state`.\n    return { ...state }\n  },\n  [ActionTypes.RegisterOptions]: (state, action) => {\n    let options = state.options.concat(action.options)\n\n    let activeOptionIndex = state.activeOptionIndex\n    if (state.pendingFocus.focus !== Focus.Nothing) {\n      activeOptionIndex = calculateActiveIndex(state.pendingFocus, {\n        resolveItems: () => options,\n        resolveActiveIndex: () => state.activeOptionIndex,\n        resolveId: (item) => item.id,\n        resolveDisabled: (item) => item.dataRef.current.disabled,\n      })\n    }\n\n    // Check if we need to make the newly registered option active.\n    if (state.activeOptionIndex === null) {\n      let { isSelected } = state.dataRef.current\n      if (isSelected) {\n        let idx = options.findIndex((option) => isSelected?.(option.dataRef.current.value))\n        if (idx !== -1) activeOptionIndex = idx\n      }\n    }\n\n    return {\n      ...state,\n      options,\n      activeOptionIndex,\n      pendingFocus: { focus: Focus.Nothing },\n      pendingShouldSort: true,\n    }\n  },\n  [ActionTypes.UnregisterOptions]: (state, action) => {\n    let options = state.options\n\n    let idxs = []\n    let ids = new Set(action.options)\n    for (let [idx, option] of options.entries()) {\n      if (ids.has(option.id)) {\n        idxs.push(idx)\n        ids.delete(option.id)\n        if (ids.size === 0) break\n      }\n    }\n\n    if (idxs.length > 0) {\n      options = options.slice()\n      for (let idx of idxs.reverse()) {\n        options.splice(idx, 1)\n      }\n    }\n\n    return {\n      ...state,\n      options,\n      activationTrigger: ActivationTrigger.Other,\n    }\n  },\n  [ActionTypes.SetButtonElement]: (state, action) => {\n    if (state.buttonElement === action.element) return state\n    return { ...state, buttonElement: action.element }\n  },\n  [ActionTypes.SetOptionsElement]: (state, action) => {\n    if (state.optionsElement === action.element) return state\n    return { ...state, optionsElement: action.element }\n  },\n  [ActionTypes.SortOptions]: (state) => {\n    if (!state.pendingShouldSort) return state\n\n    return {\n      ...state,\n      ...adjustOrderedState(state),\n      pendingShouldSort: false,\n    }\n  },\n  [ActionTypes.MarkButtonAsMoved](state) {\n    if (state.buttonPositionState.kind !== 'Tracked') return state\n\n    return {\n      ...state,\n      buttonPositionState: ElementPositionState.Moved,\n    }\n  },\n}\n\nexport class ListboxMachine<T> extends Machine<State<T>, Actions<T>> {\n  static new({ id, __demoMode = false }: { id: string; __demoMode?: boolean }) {\n    return new ListboxMachine({\n      id,\n      // @ts-expect-error TODO: Re-structure such that we don't need to ignore this\n      dataRef: { current: {} },\n      listboxState: __demoMode ? ListboxStates.Open : ListboxStates.Closed,\n      options: [],\n      searchQuery: '',\n      activeOptionIndex: null,\n      activationTrigger: ActivationTrigger.Other,\n      buttonElement: null,\n      optionsElement: null,\n      pendingShouldSort: false,\n      pendingFocus: { focus: Focus.Nothing },\n      frozenValue: false,\n      __demoMode,\n      buttonPositionState: ElementPositionState.Idle,\n    })\n  }\n\n  constructor(initialState: State<T>) {\n    super(initialState)\n\n    this.on(ActionTypes.RegisterOptions, () => {\n      // Schedule a sort of the options when the DOM is ready. This doesn't\n      // change anything rendering wise, but the sorted options are used when\n      // using arrow keys so we can jump to previous / next options.\n      requestAnimationFrame(() => {\n        this.send({ type: ActionTypes.SortOptions })\n      })\n    })\n\n    // When the listbox is open, and it's not on the top of the hierarchy, we\n    // should close it again.\n    {\n      let id = this.state.id\n      let stackMachine = stackMachines.get(null)\n\n      this.disposables.add(\n        stackMachine.on(StackActionTypes.Push, (state) => {\n          if (\n            !stackMachine.selectors.isTop(state, id) &&\n            this.state.listboxState === ListboxStates.Open\n          ) {\n            this.actions.closeListbox()\n          }\n        })\n      )\n\n      this.on(ActionTypes.OpenListbox, () => stackMachine.actions.push(id))\n      this.on(ActionTypes.CloseListbox, () => stackMachine.actions.pop(id))\n    }\n\n    // Track whether the button moved or not\n    this.disposables.group((d) => {\n      this.on(ActionTypes.CloseListbox, (state) => {\n        if (!state.buttonElement) return\n\n        d.dispose()\n        d.add(\n          detectMovement(state.buttonElement, state.buttonPositionState, () => {\n            this.send({ type: ActionTypes.MarkButtonAsMoved })\n          })\n        )\n      })\n    })\n\n    this.on(ActionTypes.SelectOption, (_, action) => {\n      this.actions.onChange(action.value)\n\n      if (this.state.dataRef.current.mode === ValueMode.Single) {\n        this.actions.closeListbox()\n        this.state.buttonElement?.focus({ preventScroll: true })\n      }\n    })\n  }\n\n  actions = {\n    onChange: (newValue: T) => {\n      let { onChange, compare, mode, value } = this.state.dataRef.current\n\n      return match(mode, {\n        [ValueMode.Single]: () => {\n          return onChange?.(newValue)\n        },\n        [ValueMode.Multi]: () => {\n          let copy = (value as T[]).slice()\n\n          let idx = copy.findIndex((item) => compare(item, newValue))\n          if (idx === -1) {\n            copy.push(newValue)\n          } else {\n            copy.splice(idx, 1)\n          }\n\n          return onChange?.(copy as T)\n        },\n      })\n    },\n    registerOption: batch(() => {\n      let options: { id: string; dataRef: ListboxOptionDataRef<T> }[] = []\n      let seen = new Set<ListboxOptionDataRef<T>>()\n\n      return [\n        (id: string, dataRef: ListboxOptionDataRef<T>) => {\n          if (seen.has(dataRef)) return\n          seen.add(dataRef)\n          options.push({ id, dataRef })\n        },\n        () => {\n          seen.clear()\n          return this.send({ type: ActionTypes.RegisterOptions, options: options.splice(0) })\n        },\n      ]\n    }),\n    unregisterOption: batch(() => {\n      let options: string[] = []\n      return [\n        (id: string) => options.push(id),\n        () => {\n          this.send({ type: ActionTypes.UnregisterOptions, options: options.splice(0) })\n        },\n      ]\n    }),\n    goToOption: batch(() => {\n      let last: Extract<Actions<unknown>, { type: ActionTypes.GoToOption }> | null = null\n      return [\n        (\n          focus: { focus: Focus.Specific; id: string } | { focus: Exclude<Focus, Focus.Specific> },\n          trigger?: ActivationTrigger\n        ) => {\n          last = { type: ActionTypes.GoToOption, ...focus, trigger }\n        },\n        () => last && this.send(last),\n      ]\n    }),\n    closeListbox: () => {\n      this.send({ type: ActionTypes.CloseListbox })\n    },\n    openListbox: (\n      focus: { focus: Exclude<Focus, Focus.Specific> } | { focus: Focus.Specific; id: string }\n    ) => {\n      this.send({ type: ActionTypes.OpenListbox, focus })\n    },\n\n    selectActiveOption: () => {\n      if (this.state.activeOptionIndex !== null) {\n        let { dataRef } = this.state.options[this.state.activeOptionIndex]\n        this.actions.selectOption(dataRef.current.value)\n      } else if (this.state.dataRef.current.mode === ValueMode.Single) {\n        this.actions.closeListbox()\n        this.state.buttonElement?.focus({ preventScroll: true })\n      }\n    },\n\n    selectOption: (value: T) => {\n      this.send({ type: ActionTypes.SelectOption, value })\n    },\n\n    search: (value: string) => {\n      this.send({ type: ActionTypes.Search, value })\n    },\n    clearSearch: () => {\n      this.send({ type: ActionTypes.ClearSearch })\n    },\n    setButtonElement: (element: HTMLButtonElement | null) => {\n      this.send({ type: ActionTypes.SetButtonElement, element })\n    },\n    setOptionsElement: (element: HTMLElement | null) => {\n      this.send({ type: ActionTypes.SetOptionsElement, element })\n    },\n  }\n\n  selectors = {\n    activeDescendantId(state: State<T>) {\n      let activeOptionIndex = state.activeOptionIndex\n      let options = state.options\n      return activeOptionIndex === null ? undefined : options[activeOptionIndex]?.id\n    },\n\n    isActive(state: State<T>, id: string) {\n      let activeOptionIndex = state.activeOptionIndex\n      let options = state.options\n\n      return activeOptionIndex !== null ? options[activeOptionIndex]?.id === id : false\n    },\n\n    hasFrozenValue(state: State<T>) {\n      return state.frozenValue\n    },\n\n    shouldScrollIntoView(state: State<T>, id: string) {\n      if (state.__demoMode) return false\n      if (state.listboxState !== ListboxStates.Open) return false\n      if (state.activationTrigger === ActivationTrigger.Pointer) return false\n      return this.isActive(state, id)\n    },\n\n    didButtonMove(state: State<T>) {\n      return state.buttonPositionState.kind === 'Moved'\n    },\n  }\n\n  reduce(state: Readonly<State<T>>, action: Actions<T>): State<T> {\n    return match(action.type, reducers, state, action) as State<T>\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox/listbox.test.tsx",
    "content": "import { act, render, waitFor } from '@testing-library/react'\nimport React, { Fragment, createElement, createRef, useEffect, useState } from 'react'\nimport {\n  ListboxMode,\n  ListboxState,\n  assertActiveElement,\n  assertActiveListboxOption,\n  assertListbox,\n  assertListboxButton,\n  assertListboxButtonLinkedWithListbox,\n  assertListboxButtonLinkedWithListboxLabel,\n  assertListboxLabel,\n  assertListboxOption,\n  assertNoActiveListboxOption,\n  getByText,\n  getListbox,\n  getListboxButton,\n  getListboxButtons,\n  getListboxLabel,\n  getListboxOptions,\n  getListboxes,\n} from '../../test-utils/accessibility-assertions'\nimport {\n  Keys,\n  MouseButton,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  rawClick,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Transition } from '../transition/transition'\nimport { Listbox, ListboxButton, ListboxOption, ListboxOptions } from './listbox'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\ndescribe('safeguards', () => {\n  it.each([\n    ['Listbox.Button', Listbox.Button],\n    ['Listbox.Label', Listbox.Label],\n    ['Listbox.Options', Listbox.Options],\n    ['Listbox.Option', Listbox.Option],\n  ])(\n    'should error when we are using a <%s /> without a parent <Listbox />',\n    suppressConsoleLogs((name, Component) => {\n      if (name === 'Listbox.Label') {\n        // @ts-expect-error This is fine\n        expect(() => render(createElement(Component))).toThrow(\n          'You used a <Label /> component, but it is not inside a relevant parent.'\n        )\n      } else {\n        // @ts-expect-error This is fine\n        expect(() => render(createElement(Component))).toThrow(\n          `<${name} /> is missing a parent <Listbox /> component.`\n        )\n      }\n    })\n  )\n\n  it(\n    'should be possible to render a Listbox without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Listbox', () => {\n    it(\n      'should be possible to render a Listbox using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            {({ open }) => (\n              <>\n                <Listbox.Button>Trigger</Listbox.Button>\n                {open && (\n                  <Listbox.Options>\n                    <Listbox.Option value=\"a\">Option A</Listbox.Option>\n                    <Listbox.Option value=\"b\">Option B</Listbox.Option>\n                    <Listbox.Option value=\"c\">Option C</Listbox.Option>\n                  </Listbox.Options>\n                )}\n              </>\n            )}\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to disable a Listbox',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} disabled>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await press(Keys.Enter, getListboxButton())\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not crash in multiple mode',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox multiple name=\"abc\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value={{ id: 1, name: 'alice' }}>alice</Listbox.Option>\n              <Listbox.Option value={{ id: 2, name: 'bob' }}>bob</Listbox.Option>\n              <Listbox.Option value={{ id: 3, name: 'charlie' }}>charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        await click(getListboxButton())\n        let [alice, bob, charlie] = getListboxOptions()\n\n        await click(alice)\n        await click(bob)\n        await click(charlie)\n      })\n    )\n\n    describe('Equality', () => {\n      let options = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n\n      it(\n        'should use object equality by default',\n        suppressConsoleLogs(async () => {\n          render(\n            <Listbox value={options[1]} onChange={(x) => console.log(x)}>\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                {options.map((option) => (\n                  <Listbox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Listbox.Option>\n                ))}\n              </Listbox.Options>\n            </Listbox>\n          )\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: true,\n              disabled: false,\n              selectedOption: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a field',\n        suppressConsoleLogs(async () => {\n          render(\n            <Listbox value={{ id: 2, name: 'Bob' }} onChange={(x) => console.log(x)} by=\"id\">\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                {options.map((option) => (\n                  <Listbox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Listbox.Option>\n                ))}\n              </Listbox.Options>\n            </Listbox>\n          )\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: true,\n              disabled: false,\n              selectedOption: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a comparator function',\n        suppressConsoleLogs(async () => {\n          render(\n            <Listbox\n              value={{ id: 2, name: 'Bob' }}\n              onChange={(x) => console.log(x)}\n              by={(a, z) => a.id === z.id}\n            >\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                {options.map((option) => (\n                  <Listbox.Option\n                    key={option.id}\n                    value={option}\n                    className={(info) => JSON.stringify(info)}\n                  >\n                    {option.name}\n                  </Listbox.Option>\n                ))}\n              </Listbox.Options>\n            </Listbox>\n          )\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveAttribute(\n            'class',\n            JSON.stringify({\n              active: true,\n              focus: true,\n              selected: true,\n              disabled: false,\n              selectedOption: false,\n            })\n          )\n        })\n      )\n\n      it(\n        'should be possible to use the by prop (as a string) with a null initial value',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState(null)\n\n            return (\n              <Listbox value={value} onChange={setValue} by=\"id\">\n                <Listbox.Button>Trigger</Listbox.Button>\n                <Listbox.Options>\n                  <Listbox.Option value={{ id: 1, name: 'alice' }}>alice</Listbox.Option>\n                  <Listbox.Option value={{ id: 2, name: 'bob' }}>bob</Listbox.Option>\n                  <Listbox.Option value={{ id: 3, name: 'charlie' }}>charlie</Listbox.Option>\n                </Listbox.Options>\n              </Listbox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getListboxButton())\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[1])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      // TODO: Does this test prove anything useful?\n      it(\n        'should be possible to use the by prop (as a string) with a null listbox option',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState(null)\n\n            return (\n              <Listbox value={value} onChange={setValue} by=\"id\">\n                <Listbox.Button>Trigger</Listbox.Button>\n                <Listbox.Options>\n                  <Listbox.Option value={null} disabled>\n                    Please select an option\n                  </Listbox.Option>\n                  <Listbox.Option value={{ id: 1, name: 'alice' }}>alice</Listbox.Option>\n                  <Listbox.Option value={{ id: 2, name: 'bob' }}>bob</Listbox.Option>\n                  <Listbox.Option value={{ id: 3, name: 'charlie' }}>charlie</Listbox.Option>\n                </Listbox.Options>\n              </Listbox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getListboxButton())\n          let [disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'true')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[3])\n          await click(getListboxButton())\n          ;[disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'false')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'false')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (single mode)',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState({ id: 2, name: 'Bob' })\n\n            return (\n              <Listbox value={value} onChange={setValue} by=\"id\">\n                <Listbox.Button>Trigger</Listbox.Button>\n                <Listbox.Options>\n                  <Listbox.Option value={{ id: 1, name: 'alice' }}>alice</Listbox.Option>\n                  <Listbox.Option value={{ id: 2, name: 'bob' }}>bob</Listbox.Option>\n                  <Listbox.Option value={{ id: 3, name: 'charlie' }}>charlie</Listbox.Option>\n                </Listbox.Options>\n              </Listbox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getListboxButton())\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[1])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (multiple mode)',\n        suppressConsoleLogs(async () => {\n          function Example() {\n            let [value, setValue] = useState([{ id: 2, name: 'Bob' }])\n\n            return (\n              <Listbox value={value} onChange={setValue} by=\"id\" multiple>\n                <Listbox.Button>Trigger</Listbox.Button>\n                <Listbox.Options>\n                  <Listbox.Option value={{ id: 1, name: 'alice' }}>alice</Listbox.Option>\n                  <Listbox.Option value={{ id: 2, name: 'bob' }}>bob</Listbox.Option>\n                  <Listbox.Option value={{ id: 3, name: 'charlie' }}>charlie</Listbox.Option>\n                </Listbox.Options>\n              </Listbox>\n            )\n          }\n\n          render(<Example />)\n\n          await click(getListboxButton())\n\n          await click(getListboxOptions()[2])\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[2])\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n    })\n\n    it(\n      'null should be a valid value for the Listbox',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n  })\n\n  describe('Listbox.Label', () => {\n    it(\n      'should be possible to render a Listbox.Label using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Label>{(slot) => <>{JSON.stringify(slot)}</>}</Listbox.Label>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListboxLabel({ textContent: JSON.stringify({ open: false, disabled: false }) })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxLabel({ textContent: JSON.stringify({ open: true, disabled: false }) })\n        assertListbox({ state: ListboxState.Visible })\n        assertListboxButtonLinkedWithListboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to render a Listbox.Label using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Label as=\"p\">{(slot) => <>{JSON.stringify(slot)}</>}</Listbox.Label>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxLabel({\n          textContent: JSON.stringify({ open: false, disabled: false }),\n          tag: 'p',\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n        assertListboxLabel({\n          textContent: JSON.stringify({ open: true, disabled: false }),\n          tag: 'p',\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n  })\n\n  describe('Listbox.Button', () => {\n    it(\n      'should be possible to render a Listbox.Button using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>{(slot) => <>{JSON.stringify(slot)}</>}</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            invalid: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            invalid: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Listbox.Button using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button as=\"div\" role=\"button\">\n              {(slot) => <>{JSON.stringify(slot)}</>}\n            </Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            invalid: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            invalid: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Listbox.Button and a Listbox.Label and see them linked together',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Label>Label</Listbox.Label>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // TODO: Needed to make it similar to vue test implementation?\n        // await new Promise(requestAnimationFrame)\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n        assertListboxButtonLinkedWithListboxLabel()\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n          </Listbox>\n        )\n\n        expect(getListboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button type=\"submit\">Trigger</Listbox.Button>\n          </Listbox>\n        )\n\n        expect(getListboxButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n        let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n          <button ref={ref} {...props} />\n        ))\n\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button as={CustomButton}>Trigger</Listbox.Button>\n          </Listbox>\n        )\n\n        expect(getListboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button as=\"div\">Trigger</Listbox.Button>\n          </Listbox>\n        )\n\n        expect(getListboxButton()).not.toHaveAttribute('type')\n      })\n\n      it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n        let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n          <div ref={ref} {...props} />\n        ))\n\n        render(\n          <Listbox value={null} onChange={(x) => console.log(x)}>\n            <Listbox.Button as={CustomButton}>Trigger</Listbox.Button>\n          </Listbox>\n        )\n\n        expect(getListboxButton()).not.toHaveAttribute('type')\n      })\n    })\n\n    it(\n      'should be possible to render a ListboxButton using as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox>\n            <ListboxButton as={Fragment}>\n              <button>Toggle</button>\n            </ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n  })\n\n  describe('Listbox.Options', () => {\n    it(\n      'should be possible to render Listbox.Options using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              {(data) => (\n                <>\n                  <Listbox.Option value=\"a\">{JSON.stringify(data)}</Listbox.Option>\n                </>\n              )}\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible, textContent: JSON.stringify({ open: true }) })\n        assertActiveElement(getListbox())\n      })\n    )\n\n    it('should be possible to always render the Listbox.Options if we provide it a `static` prop', () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options static>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Let's verify that the Listbox is already there\n      expect(getListbox()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the Listbox.Options', async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options unmount={false}>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListbox({ state: ListboxState.InvisibleHidden })\n\n      // Let's open the Listbox, to see if it is not hidden anymore\n      await click(getListboxButton())\n\n      assertListbox({ state: ListboxState.Visible })\n    })\n  })\n\n  describe('Listbox.Option', () => {\n    it(\n      'should be possible to render a Listbox.Option using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">{(slot) => <>{JSON.stringify(slot)}</>}</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          textContent: JSON.stringify({\n            active: false,\n            focus: false,\n            selected: false,\n            disabled: false,\n            selectedOption: false,\n          }),\n        })\n      })\n    )\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    function Example({ hide = false }) {\n      return (\n        <>\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option 1</Listbox.Option>\n              {!hide && <Listbox.Option value=\"b\">Option 2</Listbox.Option>}\n              <Listbox.Option value=\"c\">Option 3</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        </>\n      )\n    }\n\n    let { rerender } = render(<Example />)\n\n    // Open the Listbox\n    await click(getByText('Trigger'))\n\n    rerender(<Example hide={true} />) // Remove Listbox.Option 2\n    rerender(<Example hide={false} />) // Re-add Listbox.Option 2\n\n    assertListbox({ state: ListboxState.Visible })\n\n    let options = getListboxOptions()\n\n    // Focus the first item\n    await press(Keys.ArrowDown)\n\n    // Verify that the first menu item is active\n    assertActiveListboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n    // Verify that the second menu item is active\n    assertActiveListboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n    // Verify that the third menu item is active\n    assertActiveListboxOption(options[2])\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">Alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">Bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">Charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose charlie\n      await click(getListboxOptions()[2])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should expose the value via the render prop', async () => {\n      let handleSubmission = jest.fn()\n\n      let { getByTestId } = render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\">\n            {({ value }) => (\n              <>\n                <div data-testid=\"value\">{value}</div>\n                <Listbox.Button>\n                  {({ value }) => (\n                    <>\n                      Trigger\n                      <div data-testid=\"value-2\">{value}</div>\n                    </>\n                  )}\n                </Listbox.Button>\n                <Listbox.Options>\n                  <Listbox.Option value=\"alice\">Alice</Listbox.Option>\n                  <Listbox.Option value=\"bob\">Bob</Listbox.Option>\n                  <Listbox.Option value=\"charlie\">Charlie</Listbox.Option>\n                </Listbox.Options>\n              </>\n            )}\n          </Listbox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n      expect(getByTestId('value')).toHaveTextContent('alice')\n      expect(getByTestId('value-2')).toHaveTextContent('alice')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose charlie\n      await click(getListboxOptions()[2])\n      expect(getByTestId('value')).toHaveTextContent('charlie')\n      expect(getByTestId('value-2')).toHaveTextContent('charlie')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\" defaultValue=\"bob\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">Alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">Bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">Charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n    })\n\n    it('should be possible to reset to the default value if the form is reset', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\" defaultValue=\"bob\">\n            <Listbox.Button>{({ value }) => value ?? 'Trigger'}</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">Alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">Bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">Charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // The listbox should be reset to bob\n      expect(getListboxButton()).toHaveTextContent('bob')\n\n      // Open listbox\n      await click(getListboxButton())\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n\n    it('should be possible to reset to the default value if the form is reset (using objects)', async () => {\n      let handleSubmission = jest.fn()\n\n      let data = [\n        { id: 1, name: 'alice', label: 'Alice' },\n        { id: 2, name: 'bob', label: 'Bob' },\n        { id: 3, name: 'charlie', label: 'Charlie' },\n      ]\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\" defaultValue={{ id: 2, name: 'bob', label: 'Bob' }} by=\"id\">\n            <Listbox.Button>{({ value }) => value?.name ?? 'Trigger'}</Listbox.Button>\n            <Listbox.Options>\n              {data.map((person) => (\n                <Listbox.Option key={person.id} value={person}>\n                  {person.label}\n                </Listbox.Option>\n              ))}\n            </Listbox.Options>\n          </Listbox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[id]': '2',\n        'assignee[name]': 'bob',\n        'assignee[label]': 'Bob',\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // The listbox should be reset to bob\n      expect(getListboxButton()).toHaveTextContent('bob')\n\n      // Open listbox\n      await click(getListboxButton())\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n\n    it('should be possible to reset to the default value in multiple mode', async () => {\n      let handleSubmission = jest.fn()\n      let data = ['alice', 'bob', 'charlie']\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Listbox name=\"assignee\" defaultValue={['bob'] as string[]} multiple>\n            <Listbox.Button>{({ value }) => value.join(', ') || 'Trigger'}</Listbox.Button>\n            <Listbox.Options>\n              {data.map((person) => (\n                <Listbox.Option key={person} value={person}>\n                  {person}\n                </Listbox.Option>\n              ))}\n            </Listbox.Options>\n          </Listbox>\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n\n      await click(document.getElementById('reset'))\n      await click(document.getElementById('submit'))\n\n      // Bob is still the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      render(\n        <Listbox name=\"assignee\" onChange={handleChange}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">Alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">Bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">Charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose bob\n      await click(getListboxOptions()[1])\n\n      // Change handler should have been called twice\n      expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n      expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n    })\n  })\n\n  it(\n    'should be possible to open a listbox programmatically via .click()',\n    suppressConsoleLogs(async () => {\n      let btnRef = createRef<HTMLButtonElement>()\n\n      render(\n        <Listbox>\n          <ListboxButton ref={btnRef}>Trigger</ListboxButton>\n          <ListboxOptions>\n            <ListboxOption value=\"a\">Option A</ListboxOption>\n            <ListboxOption value=\"b\">Option B</ListboxOption>\n            <ListboxOption value=\"c\">Option C</ListboxOption>\n          </ListboxOptions>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open listbox\n      act(() => btnRef.current?.click())\n\n      // Verify it is open\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({ state: ListboxState.Visible })\n      assertListboxButtonLinkedWithListbox()\n\n      // Verify we have listbox options\n      let options = getListboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertListboxOption(option))\n    })\n  )\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to conditionally render classNames (aka className can be a function?!)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\" className={(bag) => JSON.stringify(bag)}>\n              Option A\n            </Listbox.Option>\n            <Listbox.Option value=\"b\" disabled className={(bag) => JSON.stringify(bag)}>\n              Option B\n            </Listbox.Option>\n            <Listbox.Option value=\"c\" className=\"no-special-treatment\">\n              Option C\n            </Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open Listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // Verify correct classNames\n      expect('' + options[0].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: false,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[1].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: true,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[2].classList).toEqual('no-special-treatment')\n\n      // Double check that nothing is active\n      assertNoActiveListboxOption(getListbox())\n\n      // Make the first option active\n      await press(Keys.ArrowDown)\n\n      // Verify the classNames\n      expect('' + options[0].classList).toEqual(\n        JSON.stringify({\n          active: true,\n          focus: true,\n          selected: false,\n          disabled: false,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[1].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: true,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[2].classList).toEqual('no-special-treatment')\n\n      // Double check that the first option is the active one\n      assertActiveListboxOption(options[0])\n\n      // Let's go down, this should go to the third option since the second option is disabled!\n      await press(Keys.ArrowDown)\n\n      // Verify the classNames\n      expect('' + options[0].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: false,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[1].classList).toEqual(\n        JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: true,\n          selectedOption: false,\n        })\n      )\n      expect('' + options[2].classList).toEqual('no-special-treatment')\n\n      // Double check that the last option is the active one\n      assertActiveListboxOption(options[2])\n    })\n  )\n\n  it(\n    'should be possible to swap the Listbox option with a button for example',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option as=\"button\" value=\"a\">\n              Option A\n            </Listbox.Option>\n            <Listbox.Option as=\"button\" value=\"b\">\n              Option B\n            </Listbox.Option>\n            <Listbox.Option as=\"button\" value=\"c\">\n              Option C\n            </Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open Listbox\n      await click(getListboxButton())\n\n      // Verify options are buttons now\n      getListboxOptions().forEach((option) => assertListboxOption(option, { tag: 'button' }))\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {\n    useEffect(() => {\n      fn(`Mounting - ${name}`)\n      return () => {\n        fn(`Unmounting - ${name}`)\n      }\n    }, [fn, name])\n    return null\n  }\n\n  it(\n    'should be possible to wrap the Listbox.Options with a Transition component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Debug name=\"Listbox\" fn={orderFn} />\n          <Transition>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">\n                {(data) => (\n                  <>\n                    {JSON.stringify(data)}\n                    <Debug name=\"Listbox.Option\" fn={orderFn} />\n                  </>\n                )}\n              </Listbox.Option>\n            </Listbox.Options>\n          </Transition>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      await rawClick(getListboxButton())\n\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({\n        state: ListboxState.Visible,\n        textContent: JSON.stringify({\n          active: false,\n          focus: false,\n          selected: false,\n          disabled: false,\n          selectedOption: false,\n        }),\n      })\n\n      await rawClick(getListboxButton())\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Listbox'],\n        ['Mounting - Transition'],\n        ['Mounting - Listbox.Option'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Listbox.Option'],\n      ])\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to close the listbox with Enter when there is no active listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Close listbox\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n      })\n    )\n\n    it(\n      'should be possible to close the listbox with Enter and choose the active listbox option',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        function Example() {\n          let [value, setValue] = useState(undefined)\n\n          return (\n            <Listbox\n              value={value}\n              onChange={(value) => {\n                setValue(value)\n                handleChange(value)\n              }}\n            >\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                <Listbox.Option value=\"a\">Option A</Listbox.Option>\n                <Listbox.Option value=\"b\">Option B</Listbox.Option>\n                <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              </Listbox.Options>\n            </Listbox>\n          )\n        }\n\n        render(<Example />)\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Activate the first listbox option\n        let options = getListboxOptions()\n        await mouseMove(options[0])\n\n        // Choose option, and close listbox\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify we got the change event\n        expect(handleChange).toHaveBeenCalledTimes(1)\n        expect(handleChange).toHaveBeenCalledWith('a')\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n\n        // Open listbox again\n        await click(getListboxButton())\n\n        // Verify the active option is the previously selected one\n        assertActiveListboxOption(getListboxOptions()[0])\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the listbox with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} disabled>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Try to open the listbox\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with Space, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value=\"b\" onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options />\n          </Listbox>\n        )\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Space (jump over multiple disabled ones)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon Space key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to close the listbox with Space and choose the active listbox option',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        function Example() {\n          let [value, setValue] = useState(undefined)\n\n          return (\n            <Listbox\n              value={value}\n              onChange={(value) => {\n                setValue(value)\n                handleChange(value)\n              }}\n            >\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                <Listbox.Option value=\"a\">Option A</Listbox.Option>\n                <Listbox.Option value=\"b\">Option B</Listbox.Option>\n                <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              </Listbox.Options>\n            </Listbox>\n          )\n        }\n\n        render(<Example />)\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Activate the first listbox option\n        let options = getListboxOptions()\n        await mouseMove(options[0])\n\n        // Choose option, and close listbox\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify we got the change event\n        expect(handleChange).toHaveBeenCalledTimes(1)\n        expect(handleChange).toHaveBeenCalledWith('a')\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n\n        // Open listbox again\n        await click(getListboxButton())\n\n        // Verify the active option is the previously selected one\n        assertActiveListboxOption(getListboxOptions()[0])\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it(\n      'should be possible to close an open listbox with Escape',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Close listbox\n        await press(Keys.Escape)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should not focus trap when we use Tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Listbox value={undefined} onChange={(x) => console.log(x)}>\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                <Listbox.Option value=\"a\">Option A</Listbox.Option>\n                <Listbox.Option value=\"b\">Option B</Listbox.Option>\n                <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              </Listbox.Options>\n            </Listbox>\n            <a href=\"#\">After</a>\n          </>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // Tab to the next element\n        await press(Keys.Tab)\n\n        // Verify the listbox is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n        assertActiveElement(getByText('After'))\n      })\n    )\n\n    it(\n      'should not focus trap when we use Shift+Tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"#\">Before</a>\n            <Listbox value={undefined} onChange={(x) => console.log(x)}>\n              <Listbox.Button>Trigger</Listbox.Button>\n              <Listbox.Options>\n                <Listbox.Option value=\"a\">Option A</Listbox.Option>\n                <Listbox.Option value=\"b\">Option B</Listbox.Option>\n                <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              </Listbox.Options>\n            </Listbox>\n          </>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // Try to Shift+Tab\n        await press(shift(Keys.Tab))\n\n        // Verify the listbox is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n        assertActiveElement(getByText('Before'))\n      })\n    )\n  })\n\n  describe('`ArrowDown` key', () => {\n    it(\n      'should be possible to open the listbox with ArrowDown',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n\n        // Verify that the first listbox option is active\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with ArrowDown when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} disabled>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Try to open the listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is still closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with ArrowDown, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value=\"b\" onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options />\n          </Listbox>\n        )\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n\n        // We should NOT be able to go down again (because last option). Current implementation won't go around.\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options and skip the first disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n      })\n    )\n  })\n\n  describe('`ArrowRight` key', () => {\n    it(\n      'should be possible to use ArrowRight to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} horizontal>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go right once\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go right again\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[2])\n\n        // We should NOT be able to go right again (because last option). Current implementation won't go around.\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[2])\n      })\n    )\n  })\n\n  describe('`ArrowUp` key', () => {\n    it(\n      'should be possible to open the listbox with ArrowUp and the last option should be active',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n\n        // ! ALERT: The LAST option should now be active\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with ArrowUp and the last option should be active when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} disabled>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Try to open the listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is still closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with ArrowUp, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value=\"b\" onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options />\n          </Listbox>\n        )\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the listbox options and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to navigate up or down if there is only a single non-disabled option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go up (because those are disabled)\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go down (because this is the last option)\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go down once\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[0])\n\n        // We should NOT be able to go up again (because first option). Current implementation won't go around.\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[0])\n      })\n    )\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it(\n      'should be possible to use ArrowLeft to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)} horizontal>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible, orientation: 'horizontal' })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go left once\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go left again\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[0])\n\n        // We should NOT be able to go left again (because first option). Current implementation won't go around.\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[0])\n      })\n    )\n  })\n\n  describe('`End` key', () => {\n    it(\n      'should be possible to use the End key to go to the last listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await press(Keys.End)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the last non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last non-disabled option\n        await press(Keys.End)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the first listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon End key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`PageDown` key', () => {\n    it(\n      'should be possible to use the PageDown key to go to the last listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await press(Keys.PageDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the last non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last non-disabled option\n        await press(Keys.PageDown)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the first listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon PageDown key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`Home` key', () => {\n    it(\n      'should be possible to use the Home key to go to the first listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the first option\n        await press(Keys.Home)\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the first non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              <Listbox.Option value=\"d\">Option D</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let options = getListboxOptions()\n\n        // We should be on the first non-disabled option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the last listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option value=\"d\">Option D</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon Home key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`PageUp` key', () => {\n    it(\n      'should be possible to use the PageUp key to go to the first listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">Option A</Listbox.Option>\n              <Listbox.Option value=\"b\">Option B</Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the first option\n        await press(Keys.PageUp)\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the first non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option value=\"c\">Option C</Listbox.Option>\n              <Listbox.Option value=\"d\">Option D</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the first non-disabled option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the last listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option value=\"d\">Option D</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon PageUp key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option disabled value=\"a\">\n                Option A\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"b\">\n                Option B\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"c\">\n                Option C\n              </Listbox.Option>\n              <Listbox.Option disabled value=\"d\">\n                Option D\n              </Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`Any` key aka search', () => {\n    it(\n      'should be possible to type a full word that has a perfect match',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // We should be able to go to the second option\n        await type(word('bob'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('alice'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('charlie'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to type a partial of a word',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the second option\n        await type(word('bo'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('ali'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('char'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to type words with spaces',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">value a</Listbox.Option>\n              <Listbox.Option value=\"b\">value b</Listbox.Option>\n              <Listbox.Option value=\"c\">value c</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the second option\n        await type(word('value b'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('value a'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('value c'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should not be possible to search for a disabled option',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option disabled value=\"bob\">\n                bob\n              </Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go to the disabled option\n        await type(word('bo'))\n\n        // We should still be on the last option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to search for a word (case insensitive)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Focus the button\n        await focus(getListboxButton())\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // Search for bob in a different casing\n        await type(word('BO'))\n\n        // We should be on `bob`\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to search for the next occurrence',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">alice</Listbox.Option>\n              <Listbox.Option value=\"b\">bob</Listbox.Option>\n              <Listbox.Option value=\"c\">charlie</Listbox.Option>\n              <Listbox.Option value=\"d\">bob</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // Search for bob\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for bob again\n        await type(word('b'))\n\n        // We should be on the second `bob`\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should stay on the same item while keystrokes still match',\n      suppressConsoleLogs(async () => {\n        render(\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"a\">alice</Listbox.Option>\n              <Listbox.Option value=\"b\">bob</Listbox.Option>\n              <Listbox.Option value=\"c\">charlie</Listbox.Option>\n              <Listbox.Option value=\"d\">bob</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"b\" in \"bob\"\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"b\" in \"bob\" again\n        await type(word('b'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"bo\" in \"bob\"\n        await type(word('bo'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"bo\" in \"bob\" again\n        await type(word('bo'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"bob\" in \"bob\"\n        await type(word('bob'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"bob\" in \"bob\" again\n        await type(word('bob'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should focus the Listbox.Button when we click the Listbox.Label',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Label>Label</Listbox.Label>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getListboxLabel())\n\n      // Ensure that the actual button is focused instead\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should not focus the Listbox.Button when we right click the Listbox.Label',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Label>Label</Listbox.Label>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getListboxLabel(), MouseButton.Right)\n\n      // Ensure that the body is still active\n      assertActiveElement(document.body)\n    })\n  )\n\n  it(\n    'should be possible to open the listbox on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is visible\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n      assertListboxButtonLinkedWithListbox()\n\n      // Verify we have listbox options\n      let options = getListboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertListboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the listbox on right click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Item A</Listbox.Option>\n            <Listbox.Option value=\"b\">Item B</Listbox.Option>\n            <Listbox.Option value=\"c\">Item C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Try to open the listbox\n      await click(getListboxButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open the listbox on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)} disabled>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Try to open the listbox\n      await click(getListboxButton())\n\n      // Verify it is still closed\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to open the listbox on click, and focus the selected option',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value=\"b\" onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is visible\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n      assertListboxButtonLinkedWithListbox()\n\n      // Verify we have listbox options\n      let options = getListboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n      // Verify that the second listbox option is active (because it is already selected)\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to close a listbox on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"a\">Option A</Listbox.Option>\n            <Listbox.Option value=\"b\">Option B</Listbox.Option>\n            <Listbox.Option value=\"c\">Option C</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is visible\n      assertListboxButton({ state: ListboxState.Visible })\n\n      // Click to close\n      await click(getListboxButton())\n\n      // Verify it is closed\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be a no-op when we click outside of a closed listbox',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Verify that the window is closed\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Click something that is not related to the listbox\n      await click(document.body)\n\n      // Should still be closed\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox which should close the listbox',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      // Click something that is not related to the listbox\n      await click(document.body)\n\n      // Should be closed now\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox on another listbox button which should close the current listbox and open the new listbox',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        </div>\n      )\n\n      let [button1, button2] = getListboxButtons()\n\n      // Click the first listbox button\n      await click(button1)\n      expect(getListboxes()).toHaveLength(1) // Only 1 listbox should be visible\n\n      // Ensure the open listbox is linked to the first button\n      assertListboxButtonLinkedWithListbox(button1, getListbox())\n\n      // Click the second listbox button\n      await click(button2)\n\n      expect(getListboxes()).toHaveLength(1) // Only 1 listbox should be visible\n\n      // Ensure the open listbox is linked to the second button\n      assertListboxButtonLinkedWithListbox(button2, getListbox())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox which should close the listbox (even if we press the listbox button)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      // Click the listbox button again\n      await click(getListboxButton())\n\n      // Should be closed now\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox, on an element which is within a focusable element, which closes the listbox',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <Listbox value={undefined} onChange={(x) => console.log(x)}>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n\n          <button id=\"btn\">\n            <span>Next</span>\n          </button>\n        </div>\n      )\n\n      // Click the listbox button\n      await click(getListboxButton())\n\n      // Ensure the listbox is open\n      assertListbox({ state: ListboxState.Visible })\n\n      // Click the span inside the button\n      await click(getByText('Next'))\n\n      // Ensure the listbox is closed\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Ensure the outside button is focused\n      assertActiveElement(document.getElementById('btn'))\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveListboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveListboxOption(options[2])\n    })\n  )\n\n  it(\n    'should make a listbox option active when you move the mouse over it',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the listbox option is already active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      await mouseMove(options[1])\n\n      // Nothing should be changed\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the listbox option is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option disabled value=\"bob\">\n              bob\n            </Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      await mouseMove(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should not be possible to hover an option that is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option disabled value=\"bob\">\n              bob\n            </Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n\n      // We should not have an active option now\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave an option and make it inactive',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value=\"bob\" onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNoActiveListboxOption()\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveListboxOption(options[0])\n\n      await mouseLeave(options[0])\n      assertNoActiveListboxOption()\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveListboxOption(options[2])\n\n      await mouseLeave(options[2])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave a disabled option and be a no-op',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option disabled value=\"bob\">\n              bob\n            </Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n      assertNoActiveListboxOption()\n\n      await mouseLeave(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to click a listbox option, which closes the listbox',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      function Example() {\n        let [value, setValue] = useState(undefined)\n\n        return (\n          <Listbox\n            value={value}\n            onChange={(value) => {\n              setValue(value)\n              handleChange(value)\n            }}\n          >\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n      expect(handleChange).toHaveBeenCalledTimes(1)\n      expect(handleChange).toHaveBeenCalledWith('bob')\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n\n      // Open listbox again\n      await click(getListboxButton())\n\n      // Verify the active option is the previously selected one\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n  )\n\n  it(\n    'should be possible to click a disabled listbox option, which is a no-op',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      function Example() {\n        let [value, setValue] = useState(undefined)\n\n        return (\n          <Listbox\n            value={value}\n            onChange={(value) => {\n              setValue(value)\n              handleChange(value)\n            }}\n          >\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option disabled value=\"bob\">\n                bob\n              </Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n      expect(handleChange).toHaveBeenCalledTimes(0)\n\n      // Close the listbox\n      await click(getListboxButton())\n\n      // Open listbox again\n      await click(getListboxButton())\n\n      // Verify the active option is non existing\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible focus a listbox option, so that it becomes active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option value=\"bob\">bob</Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // Verify that nothing is active yet\n      assertNoActiveListboxOption()\n\n      // We should be able to focus the first option\n      await focus(options[1])\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to focus a listbox option which is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox value={undefined} onChange={(x) => console.log(x)}>\n          <Listbox.Button>Trigger</Listbox.Button>\n          <Listbox.Options>\n            <Listbox.Option value=\"alice\">alice</Listbox.Option>\n            <Listbox.Option disabled value=\"bob\">\n              bob\n            </Listbox.Option>\n            <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n          </Listbox.Options>\n        </Listbox>\n      )\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should not be able to focus the first option\n      await focus(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n})\n\ndescribe('Multi-select', () => {\n  it(\n    'should be possible to pass multiple values to the Listbox component',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Listbox value={value} onChange={setValue} multiple>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify that we have an open listbox with multiple mode\n      assertListbox({ state: ListboxState.Visible, mode: ListboxMode.Multiple })\n\n      // Verify that we have multiple selected listbox options\n      let options = getListboxOptions()\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should make the first selected option the active item',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Listbox value={value} onChange={setValue} multiple>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify that bob is the active option\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n  )\n\n  it(\n    'should keep the listbox open when selecting an item via the keyboard',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Listbox value={value} onChange={setValue} multiple>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n\n      // Verify that bob is the active option\n      await click(getListboxOptions()[0])\n\n      // Verify that the listbox is still open\n      assertListbox({ state: ListboxState.Visible })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState<string[]>(['bob', 'charlie'])\n\n        return (\n          <Listbox value={value} onChange={setValue} multiple>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Options>\n              <Listbox.Option value=\"alice\">alice</Listbox.Option>\n              <Listbox.Option value=\"bob\">bob</Listbox.Option>\n              <Listbox.Option value=\"charlie\">charlie</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n        )\n      }\n\n      render(<Example />)\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n\n      let options = getListboxOptions()\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: false })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n    })\n  )\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState(null)\n      return (\n        <div>\n          <Listbox form=\"my-form\" value={value} onChange={setValue} name=\"delivery\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Label>Pizza Delivery</Listbox.Label>\n            <Listbox.Options>\n              <Listbox.Option value=\"pickup\">Pickup</Listbox.Option>\n              <Listbox.Option value=\"home-delivery\">Home delivery</Listbox.Option>\n              <Listbox.Option value=\"dine-in\">Dine in</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n\n          <form\n            id=\"my-form\"\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <button>Submit</button>\n          </form>\n        </div>\n      )\n    }\n\n    render(<Example />)\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should be possible to submit a form by pressing enter', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState(null)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Listbox value={value} onChange={setValue} name=\"delivery\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Label>Pizza Delivery</Listbox.Label>\n            <Listbox.Options>\n              <Listbox.Option value=\"pickup\">Pickup</Listbox.Option>\n              <Listbox.Option value=\"home-delivery\">Home delivery</Listbox.Option>\n              <Listbox.Option value=\"dine-in\">Dine in</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Focus the listbox\n    await focus(getListboxButton())\n\n    // Submit the form by pressing enter\n    await press(Keys.Enter)\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Focus the listbox\n    await focus(getListboxButton())\n\n    // Submit the form by pressing enter\n    await press(Keys.Enter)\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Focus the listbox\n    await focus(getListboxButton())\n\n    // Submit the form by pressing enter\n    await press(Keys.Enter)\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should be possible to submit a form with a value', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState(null)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Listbox value={value} onChange={setValue} name=\"delivery\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Label>Pizza Delivery</Listbox.Label>\n            <Listbox.Options>\n              <Listbox.Option value=\"pickup\">Pickup</Listbox.Option>\n              <Listbox.Option value=\"home-delivery\">Home delivery</Listbox.Option>\n              <Listbox.Option value=\"dine-in\">Dine in</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should not submit the data if the Listbox is disabled', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState('home-delivery')\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <Listbox value={value} onChange={setValue} name=\"delivery\" disabled>\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Label>Pizza Delivery</Listbox.Label>\n            <Listbox.Options>\n              <Listbox.Option value=\"pickup\">Pickup</Listbox.Option>\n              <Listbox.Option value=\"home-delivery\">Home delivery</Listbox.Option>\n              <Listbox.Option value=\"dine-in\">Dine in</Listbox.Option>\n            </Listbox.Options>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it('should be possible to submit a form with a complex value object', async () => {\n    let submits = jest.fn()\n    let options = [\n      {\n        id: 1,\n        value: 'pickup',\n        label: 'Pickup',\n        extra: { info: 'Some extra info' },\n      },\n      {\n        id: 2,\n        value: 'home-delivery',\n        label: 'Home delivery',\n        extra: { info: 'Some extra info' },\n      },\n      {\n        id: 3,\n        value: 'dine-in',\n        label: 'Dine in',\n        extra: { info: 'Some extra info' },\n      },\n    ]\n\n    function Example() {\n      let [value, setValue] = useState(options[0])\n\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Listbox value={value} onChange={setValue} name=\"delivery\">\n            <Listbox.Button>Trigger</Listbox.Button>\n            <Listbox.Label>Pizza Delivery</Listbox.Label>\n            <Listbox.Options>\n              {options.map((option) => (\n                <Listbox.Option key={option.id} value={option}>\n                  {option.label}\n                </Listbox.Option>\n              ))}\n            </Listbox.Options>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '2'],\n      ['delivery[value]', 'home-delivery'],\n      ['delivery[label]', 'Home delivery'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n  })\n})\n\ndescribe('transitions', () => {\n  it(\n    'should be possible to close the Listbox when using the `transition` prop',\n    suppressConsoleLogs(async () => {\n      render(\n        <Listbox>\n          <ListboxButton>Toggle</ListboxButton>\n          <ListboxOptions transition>\n            <ListboxOption value=\"alice\">Alice</ListboxOption>\n            <ListboxOption value=\"bob\">Bob</ListboxOption>\n            <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n          </ListboxOptions>\n        </Listbox>\n      )\n\n      // Focus the button\n      await focus(getListboxButton())\n\n      // Ensure the button is focused\n      assertActiveElement(getListboxButton())\n\n      // Open the listbox\n      await click(getListboxButton())\n\n      // Ensure the listbox is visible\n      assertListbox({ state: ListboxState.Visible })\n\n      // Close the listbox\n      await click(getListboxButton())\n\n      // Wait for the transition to finish, and the listbox to close\n      await waitFor(() => {\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n\n      // Ensure the button got the restored focus\n      assertActiveElement(getListboxButton())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox/listbox.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  Fragment,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ElementType,\n  type MutableRefObject,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type Ref,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useByComparator, type ByComparator } from '../../hooks/use-by-comparator'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useDefaultValue } from '../../hooks/use-default-value'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useElementSize } from '../../hooks/use-element-size'\nimport { useEvent } from '../../hooks/use-event'\nimport { useHandleToggle } from '../../hooks/use-handle-toggle'\nimport { useId } from '../../hooks/use-id'\nimport { useInertOthers } from '../../hooks/use-inert-others'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useOnDisappear } from '../../hooks/use-on-disappear'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport { Action as QuickReleaseAction, useQuickRelease } from '../../hooks/use-quick-release'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useScrollLock } from '../../hooks/use-scroll-lock'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useTextValue } from '../../hooks/use-text-value'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { useDisabled } from '../../internal/disabled'\nimport {\n  FloatingProvider,\n  useFloatingPanel,\n  useFloatingPanelProps,\n  useFloatingReference,\n  useFloatingReferenceProps,\n  useResolvedAnchor,\n  type AnchorPropsWithSelection,\n} from '../../internal/floating'\nimport { FormFields } from '../../internal/form-fields'\nimport { useFrozenData } from '../../internal/frozen'\nimport { useProvidedId } from '../../internal/id'\nimport { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'\nimport { stackMachines } from '../../machines/stack-machine'\nimport { useSlice } from '../../react-glue'\nimport type { EnsureArray, Props } from '../../types'\nimport { Focus } from '../../utils/calculate-active-index'\nimport { disposables } from '../../utils/disposables'\nimport * as DOM from '../../utils/dom'\nimport {\n  Focus as FocusManagementFocus,\n  FocusableMode,\n  focusFrom,\n  isFocusableElement,\n} from '../../utils/focus-management'\nimport { attemptSubmit } from '../../utils/form'\nimport { match } from '../../utils/match'\nimport { isActiveElement } from '../../utils/owner'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { Keys } from '../keyboard'\nimport { Label, useLabelledBy, useLabels, type _internal_ComponentLabel } from '../label/label'\nimport { Portal } from '../portal/portal'\nimport { ActionTypes, ActivationTrigger, ListboxStates, ValueMode } from './listbox-machine'\nimport { ListboxContext, useListboxMachine, useListboxMachineContext } from './listbox-machine-glue'\n\ntype ListboxOptionDataRef<T> = MutableRefObject<{\n  textValue?: string\n  disabled: boolean\n  value: T\n  domRef: MutableRefObject<HTMLElement | null>\n}>\n\nlet ListboxDataContext = createContext<{\n  value: unknown\n  disabled: boolean\n  invalid: boolean\n  mode: ValueMode\n  orientation: 'horizontal' | 'vertical'\n  onChange(value: unknown): void\n  compare(a: unknown, z: unknown): boolean\n  isSelected(value: unknown): boolean\n\n  optionsPropsRef: MutableRefObject<{\n    static: boolean\n    hold: boolean\n  }>\n\n  listRef: MutableRefObject<Map<string, HTMLElement | null>>\n} | null>(null)\nListboxDataContext.displayName = 'ListboxDataContext'\n\nfunction useData(component: string) {\n  let context = useContext(ListboxDataContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Listbox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useData)\n    throw err\n  }\n  return context\n}\ntype _Data = ReturnType<typeof useData>\n\n// ---\n\nlet DEFAULT_LISTBOX_TAG = Fragment\ntype ListboxRenderPropArg<T> = {\n  open: boolean\n  disabled: boolean\n  invalid: boolean\n  value: T\n}\n\nexport type ListboxProps<\n  TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG,\n  TType = string,\n  TActualType = TType,\n> = Props<\n  TTag,\n  ListboxRenderPropArg<TType>,\n  'value' | 'defaultValue' | 'onChange' | 'by' | 'disabled' | 'horizontal' | 'name' | 'multiple',\n  {\n    value?: TType\n    defaultValue?: TType\n    onChange?: (value: TType) => void\n    by?: ByComparator<TActualType>\n    disabled?: boolean\n    invalid?: boolean\n    horizontal?: boolean\n    form?: string\n    name?: string\n    multiple?: boolean\n\n    __demoMode?: boolean\n  }\n>\n\nfunction ListboxFn<\n  TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG,\n  TType = string,\n  TActualType = TType extends (infer U)[] ? U : TType,\n>(props: ListboxProps<TTag, TType, TActualType>, ref: Ref<HTMLElement>) {\n  let id = useId()\n\n  let providedDisabled = useDisabled()\n  let {\n    value: controlledValue,\n    defaultValue: _defaultValue,\n    form,\n    name,\n    onChange: controlledOnChange,\n    by,\n    invalid = false,\n    disabled = providedDisabled || false,\n    horizontal = false,\n    multiple = false,\n    __demoMode = false,\n    ...theirProps\n  } = props\n\n  const orientation = horizontal ? 'horizontal' : 'vertical'\n  let listboxRef = useSyncRefs(ref)\n\n  let defaultValue = useDefaultValue(_defaultValue)\n  let [value = multiple ? [] : undefined, theirOnChange] = useControllable<any>(\n    controlledValue,\n    controlledOnChange,\n    defaultValue\n  )\n\n  let machine = useListboxMachine({ id, __demoMode })\n  let optionsPropsRef = useRef<_Data['optionsPropsRef']['current']>({ static: false, hold: false })\n\n  let listRef = useRef<_Data['listRef']['current']>(new Map())\n\n  let compare = useByComparator(by)\n\n  let isSelected: (value: TActualType) => boolean = useCallback(\n    (compareValue) =>\n      match(data.mode, {\n        [ValueMode.Multi]: () => {\n          return (value as EnsureArray<TType>).some((option) => compare(option, compareValue))\n        },\n        [ValueMode.Single]: () => {\n          return compare(value as TActualType, compareValue)\n        },\n      }),\n    [value]\n  )\n\n  let data = useSlot<_Data>({\n    value,\n    disabled,\n    invalid,\n    mode: multiple ? ValueMode.Multi : ValueMode.Single,\n    orientation,\n    onChange: theirOnChange,\n    compare,\n    isSelected,\n    optionsPropsRef,\n    listRef,\n  })\n\n  useIsoMorphicEffect(() => {\n    machine.state.dataRef.current = data\n  }, [data])\n\n  let listboxState = useSlice(machine, (state) => state.listboxState)\n\n  let stackMachine = stackMachines.get(null)\n  let isTopLayer = useSlice(\n    stackMachine,\n    useCallback((state) => stackMachine.selectors.isTop(state, id), [stackMachine, id])\n  )\n\n  let [buttonElement, optionsElement] = useSlice(machine, (state) => [\n    state.buttonElement,\n    state.optionsElement,\n  ])\n\n  // Handle outside click\n  useOutsideClick(isTopLayer, [buttonElement, optionsElement], (event, target) => {\n    machine.send({ type: ActionTypes.CloseListbox })\n\n    if (!isFocusableElement(target, FocusableMode.Loose)) {\n      event.preventDefault()\n      buttonElement?.focus()\n    }\n  })\n\n  let slot = useSlot<ListboxRenderPropArg<TType>>({\n    open: listboxState === ListboxStates.Open,\n    disabled,\n    invalid,\n    value,\n  })\n\n  let [labelledby, LabelProvider] = useLabels({ inherit: true })\n\n  let ourProps = { ref: listboxRef }\n\n  let reset = useCallback(() => {\n    if (defaultValue === undefined) return\n    return theirOnChange?.(defaultValue)\n  }, [theirOnChange, defaultValue])\n\n  let render = useRender()\n\n  return (\n    <LabelProvider\n      value={labelledby}\n      props={{ htmlFor: buttonElement?.id }}\n      slot={{ open: listboxState === ListboxStates.Open, disabled }}\n    >\n      <FloatingProvider>\n        <ListboxContext.Provider value={machine}>\n          <ListboxDataContext.Provider value={data}>\n            <OpenClosedProvider\n              value={match(listboxState, {\n                [ListboxStates.Open]: State.Open,\n                [ListboxStates.Closed]: State.Closed,\n              })}\n            >\n              {name != null && value != null && (\n                <FormFields\n                  disabled={disabled}\n                  data={{ [name]: value }}\n                  form={form}\n                  onReset={reset}\n                />\n              )}\n              {render({\n                ourProps,\n                theirProps,\n                slot,\n                defaultTag: DEFAULT_LISTBOX_TAG,\n                name: 'Listbox',\n              })}\n            </OpenClosedProvider>\n          </ListboxDataContext.Provider>\n        </ListboxContext.Provider>\n      </FloatingProvider>\n    </LabelProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\ntype ButtonRenderPropArg = {\n  disabled: boolean\n  invalid: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n  open: boolean\n  active: boolean\n  value: any\n}\ntype ButtonPropsWeControl =\n  | 'aria-controls'\n  | 'aria-expanded'\n  | 'aria-haspopup'\n  | 'aria-labelledby'\n  | 'disabled'\n\nexport type ListboxButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    autoFocus?: boolean\n    disabled?: boolean\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: ListboxButtonProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let data = useData('Listbox.Button')\n  let machine = useListboxMachineContext('Listbox.Button')\n  let {\n    id = providedId || `headlessui-listbox-button-${internalId}`,\n    disabled = data.disabled || false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let buttonRef = useSyncRefs(ref, useFloatingReference(), machine.actions.setButtonElement)\n  let getFloatingReferenceProps = useFloatingReferenceProps()\n\n  let [listboxState, buttonElement, optionsElement] = useSlice(machine, (state) => [\n    state.listboxState,\n    state.buttonElement,\n    state.optionsElement,\n  ])\n\n  let enableQuickRelease = listboxState === ListboxStates.Open\n  useQuickRelease(enableQuickRelease, {\n    trigger: buttonElement,\n    action: useCallback(\n      (e) => {\n        if (buttonElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        let option = e.target.closest('[role=\"option\"]:not([data-disabled])')\n        if (DOM.isHTMLElement(option)) {\n          return QuickReleaseAction.Select(option)\n        }\n\n        if (optionsElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        return QuickReleaseAction.Close\n      },\n      [buttonElement, optionsElement]\n    ),\n    close: machine.actions.closeListbox,\n    select: machine.actions.selectActiveOption,\n  })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/#keyboard-interaction-13\n\n      case Keys.Enter:\n        attemptSubmit(event.currentTarget)\n        break\n\n      case Keys.Space:\n      case Keys.ArrowDown:\n        event.preventDefault()\n        machine.actions.openListbox({ focus: data.value ? Focus.Nothing : Focus.First })\n        break\n\n      case Keys.ArrowUp:\n        event.preventDefault()\n        machine.actions.openListbox({ focus: data.value ? Focus.Nothing : Focus.Last })\n        break\n    }\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Space:\n        // Required for firefox, event.preventDefault() in handleKeyDown for\n        // the Space key doesn't cancel the handleKeyUp, which in turn\n        // triggers a *click*.\n        event.preventDefault()\n        break\n    }\n  })\n\n  let toggleProps = useHandleToggle((event) => {\n    if (machine.state.listboxState === ListboxStates.Open) {\n      flushSync(() => machine.actions.closeListbox())\n      machine.state.buttonElement?.focus({ preventScroll: true })\n    } else {\n      event.preventDefault()\n      machine.actions.openListbox({ focus: Focus.Nothing })\n    }\n  })\n\n  // This is needed so that we can \"cancel\" the click event when we use the `Enter` key on a button.\n  let handleKeyPress = useEvent((event: ReactKeyboardEvent<HTMLElement>) => event.preventDefault())\n\n  let labelledBy = useLabelledBy([id])\n  let describedBy = useDescribedBy()\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<ButtonRenderPropArg>({\n    open: listboxState === ListboxStates.Open,\n    active: active || listboxState === ListboxStates.Open,\n    disabled,\n    invalid: data.invalid,\n    value: data.value,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let open = useSlice(machine, (state) => state.listboxState === ListboxStates.Open)\n  let ourProps = mergeProps(\n    getFloatingReferenceProps(),\n    {\n      ref: buttonRef,\n      id,\n      type: useResolveButtonType(props, buttonElement),\n      'aria-haspopup': 'listbox',\n      'aria-controls': optionsElement?.id,\n      'aria-expanded': open,\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      disabled: disabled || undefined,\n      autoFocus,\n      onKeyDown: handleKeyDown,\n      onKeyUp: handleKeyUp,\n      onKeyPress: handleKeyPress,\n    },\n    toggleProps,\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BUTTON_TAG,\n    name: 'Listbox.Button',\n  })\n}\n\n// ---\n\nlet SelectedOptionContext = createContext(false)\n\nlet DEFAULT_OPTIONS_TAG = 'div' as const\ntype OptionsRenderPropArg = {\n  open: boolean\n}\ntype OptionsPropsWeControl =\n  | 'aria-activedescendant'\n  | 'aria-labelledby'\n  | 'aria-multiselectable'\n  | 'aria-orientation'\n  | 'role'\n  | 'tabIndex'\n\nlet OptionsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type ListboxOptionsProps<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG> = Props<\n  TTag,\n  OptionsRenderPropArg,\n  OptionsPropsWeControl,\n  {\n    anchor?: AnchorPropsWithSelection\n    portal?: boolean\n    modal?: boolean\n    transition?: boolean\n  } & PropsForFeatures<typeof OptionsRenderFeatures>\n>\n\nfunction OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(\n  props: ListboxOptionsProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-listbox-options-${internalId}`,\n    anchor: rawAnchor,\n    portal = false,\n    modal = true,\n    transition = false,\n    ...theirProps\n  } = props\n  let anchor = useResolvedAnchor(rawAnchor)\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localOptionsElement, setLocalOptionsElement] = useState<HTMLElement | null>(null)\n\n  // Always enable `portal` functionality, when `anchor` is enabled\n  if (anchor) {\n    portal = true\n  }\n\n  let data = useData('Listbox.Options')\n  let machine = useListboxMachineContext('Listbox.Options')\n\n  let [listboxState, buttonElement, optionsElement, __demoMode] = useSlice(machine, (state) => [\n    state.listboxState,\n    state.buttonElement,\n    state.optionsElement,\n    state.__demoMode,\n  ])\n\n  let portalOwnerDocument = useOwnerDocument(buttonElement)\n  let ownerDocument = useOwnerDocument(optionsElement)\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localOptionsElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : listboxState === ListboxStates.Open\n  )\n\n  // Ensure we close the listbox as soon as the button becomes hidden\n  useOnDisappear(visible, buttonElement, machine.actions.closeListbox)\n\n  // Enable scroll locking when the listbox is visible, and `modal` is enabled\n  let scrollLockEnabled = __demoMode ? false : modal && listboxState === ListboxStates.Open\n  useScrollLock(scrollLockEnabled, ownerDocument)\n\n  // Mark other elements as inert when the listbox is visible, and `modal` is enabled\n  let inertOthersEnabled = __demoMode ? false : modal && listboxState === ListboxStates.Open\n  useInertOthers(inertOthersEnabled, {\n    allowed: useCallback(() => [buttonElement, optionsElement], [buttonElement, optionsElement]),\n  })\n\n  // We keep track whether the button moved or not, we only check this when the\n  // listbox state becomes closed. If the button moved, then we want to cancel\n  // pending transitions to prevent that the attached `ListboxOptions` is\n  // still transitioning while the button visually moved away.\n  //\n  // If we don't cancel these transitions then there will be a period where the\n  // `ListboxOptions` is visible and moving around because it is trying to\n  // re-position itself based on the new position.\n  let didButtonMove = useSlice(machine, machine.selectors.didButtonMove)\n\n  // Now that we know that the button did move or not, we can either disable the\n  // panel and all of its transitions, or rely on the `visible` state to hide\n  // the panel whenever necessary.\n  let panelEnabled = didButtonMove ? false : visible\n\n  // We should freeze when the listbox is visible but \"closed\". This means that\n  // a transition is currently happening and the component is still visible (for\n  // the transition) but closed from a functionality perspective.\n  //\n  // When the `static` prop is used, we should never freeze, because rendering\n  // is up to the user.\n  let shouldFreeze = useSlice(machine, machine.selectors.hasFrozenValue) && !props.static\n\n  // Frozen state, the selected value will only update visually when the user re-opens the <Listbox />\n  let frozenValue = useFrozenData(shouldFreeze, data.value)\n\n  let isSelected = useCallback(\n    (compareValue: unknown) => data.compare(frozenValue, compareValue),\n    [data.compare, frozenValue]\n  )\n\n  let selectedOptionIndex = useSlice(machine, (state) => {\n    if (anchor == null) return null\n    if (!anchor?.to?.includes('selection')) return null\n\n    // Only compute the selected option index when using `selection` in the\n    // `anchor` prop.\n    let idx = state.options.findIndex((option) => isSelected(option.dataRef.current.value))\n    // Ensure that if no data is selected, we default to the first item.\n    if (idx === -1) idx = 0\n\n    return idx\n  })\n\n  let anchorOptions = (() => {\n    if (anchor == null) return undefined\n    if (selectedOptionIndex === null) return { ...anchor, inner: undefined }\n\n    let elements = Array.from(data.listRef.current.values())\n\n    return {\n      ...anchor,\n      inner: {\n        listRef: { current: elements },\n        index: selectedOptionIndex,\n      },\n    }\n  })()\n\n  let [floatingRef, style] = useFloatingPanel(anchorOptions)\n  let getFloatingPanelProps = useFloatingPanelProps()\n  let optionsRef = useSyncRefs(\n    ref,\n    anchor ? floatingRef : null,\n    machine.actions.setOptionsElement,\n    setLocalOptionsElement\n  )\n\n  let searchDisposables = useDisposables()\n\n  useEffect(() => {\n    let container = optionsElement\n    if (!container) return\n    if (listboxState !== ListboxStates.Open) return\n    if (isActiveElement(container)) return\n\n    container?.focus({ preventScroll: true })\n  }, [listboxState, optionsElement])\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLElement>) => {\n    searchDisposables.dispose()\n\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n      // @ts-expect-error Fallthrough is expected here\n      case Keys.Space:\n        if (machine.state.searchQuery !== '') {\n          event.preventDefault()\n          event.stopPropagation()\n          return machine.actions.search(event.key)\n        }\n      // When in type ahead mode, fallthrough\n      case Keys.Enter:\n        event.preventDefault()\n        event.stopPropagation()\n\n        machine.actions.selectActiveOption()\n        break\n\n      case match(data.orientation, {\n        vertical: Keys.ArrowDown,\n        horizontal: Keys.ArrowRight,\n      }):\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.Next })\n\n      case match(data.orientation, {\n        vertical: Keys.ArrowUp,\n        horizontal: Keys.ArrowLeft,\n      }):\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.Previous })\n\n      case Keys.Home:\n      case Keys.PageUp:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.First })\n\n      case Keys.End:\n      case Keys.PageDown:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.actions.goToOption({ focus: Focus.Last })\n\n      case Keys.Escape:\n        event.preventDefault()\n        event.stopPropagation()\n        flushSync(() => machine.actions.closeListbox())\n        machine.state.buttonElement?.focus({ preventScroll: true })\n        return\n\n      case Keys.Tab:\n        event.preventDefault()\n        event.stopPropagation()\n        flushSync(() => machine.actions.closeListbox())\n        focusFrom(\n          machine.state.buttonElement!,\n          event.shiftKey ? FocusManagementFocus.Previous : FocusManagementFocus.Next\n        )\n        break\n\n      default:\n        if (event.key.length === 1) {\n          machine.actions.search(event.key)\n          searchDisposables.setTimeout(() => machine.actions.clearSearch(), 350)\n        }\n        break\n    }\n  })\n\n  let labelledby = useSlice(machine, (state) => state.buttonElement?.id)\n\n  let slot = useSlot<OptionsRenderPropArg>({\n    open: listboxState === ListboxStates.Open,\n  })\n\n  let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {\n    id,\n    ref: optionsRef,\n    'aria-activedescendant': useSlice(machine, machine.selectors.activeDescendantId),\n    'aria-multiselectable': data.mode === ValueMode.Multi ? true : undefined,\n    'aria-labelledby': labelledby,\n    'aria-orientation': data.orientation,\n    onKeyDown: handleKeyDown,\n    role: 'listbox',\n    // When the `Listbox` is closed, it should not be focusable. This allows us\n    // to skip focusing the `ListboxOptions` when pressing the tab key on an\n    // open `Listbox`, and go to the next focusable element.\n    tabIndex: listboxState === ListboxStates.Open ? 0 : undefined,\n    style: {\n      ...theirProps.style,\n      ...style,\n      '--button-width': useElementSize(visible, buttonElement, true).width,\n    } as CSSProperties,\n    ...transitionDataAttributes(transitionData),\n  })\n\n  let render = useRender()\n\n  // We want to use the local `isSelected` with frozen values when we are in\n  // single value mode.\n  let newData = useMemo(\n    () => (data.mode === ValueMode.Multi ? data : { ...data, isSelected }),\n    [data, isSelected]\n  )\n\n  return (\n    <Portal enabled={portal ? props.static || visible : false} ownerDocument={portalOwnerDocument}>\n      <ListboxDataContext.Provider value={newData}>\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_OPTIONS_TAG,\n          features: OptionsRenderFeatures,\n          visible: panelEnabled,\n          name: 'Listbox.Options',\n        })}\n      </ListboxDataContext.Provider>\n    </Portal>\n  )\n}\n\n// ---\n\nlet DEFAULT_OPTION_TAG = 'div' as const\ntype OptionRenderPropArg = {\n  /** @deprecated use `focus` instead */\n  active: boolean\n  focus: boolean\n  selected: boolean\n  disabled: boolean\n\n  selectedOption: boolean\n}\ntype OptionPropsWeControl = 'aria-disabled' | 'aria-selected' | 'role' | 'tabIndex'\n\nexport type ListboxOptionProps<\n  TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n  TType = string,\n> = Props<\n  TTag,\n  OptionRenderPropArg,\n  OptionPropsWeControl,\n  {\n    disabled?: boolean\n    value: TType\n  }\n>\n\nfunction OptionFn<\n  TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n  // TODO: One day we will be able to infer this type from the generic in Listbox itself.\n  // But today is not that day..\n  TType = Parameters<typeof ListboxRoot>[0]['value'],\n>(props: ListboxOptionProps<TTag, TType>, ref: Ref<HTMLElement>) {\n  let internalId = useId()\n  let {\n    id = `headlessui-listbox-option-${internalId}`,\n    disabled = false,\n    value,\n    ...theirProps\n  } = props\n  let usedInSelectedOption = useContext(SelectedOptionContext) === true\n  let data = useData('Listbox.Option')\n  let machine = useListboxMachineContext('Listbox.Option')\n\n  let active = useSlice(machine, (state) => machine.selectors.isActive(state, id))\n\n  let selected = data.isSelected(value)\n  let internalOptionRef = useRef<HTMLElement | null>(null)\n  let getTextValue = useTextValue(internalOptionRef)\n  let bag = useLatestValue<ListboxOptionDataRef<TType>['current']>({\n    disabled,\n    value,\n    domRef: internalOptionRef,\n    get textValue() {\n      return getTextValue()\n    },\n  })\n\n  let optionRef = useSyncRefs(ref, internalOptionRef, (el) => {\n    if (!el) {\n      data.listRef.current.delete(id)\n    } else {\n      data.listRef.current.set(id, el)\n    }\n  })\n\n  let shouldScrollIntoView = useSlice(machine, (state) =>\n    machine.selectors.shouldScrollIntoView(state, id)\n  )\n  useIsoMorphicEffect(() => {\n    if (!shouldScrollIntoView) return\n    return disposables().requestAnimationFrame(() => {\n      internalOptionRef.current?.scrollIntoView?.({ block: 'nearest' })\n    })\n  }, [shouldScrollIntoView, internalOptionRef])\n\n  useIsoMorphicEffect(() => {\n    if (usedInSelectedOption) return\n    machine.actions.registerOption(id, bag)\n    return () => machine.actions.unregisterOption(id)\n  }, [bag, id, usedInSelectedOption])\n\n  let handleClick = useEvent((event: { preventDefault: Function }) => {\n    if (disabled) return event.preventDefault()\n    machine.actions.selectOption(value)\n  })\n\n  let handleFocus = useEvent(() => {\n    if (disabled) return machine.actions.goToOption({ focus: Focus.Nothing })\n    machine.actions.goToOption({ focus: Focus.Specific, id })\n  })\n\n  let pointer = useTrackedPointer()\n\n  let handleEnter = useEvent((evt) => pointer.update(evt))\n\n  let handleMove = useEvent((evt) => {\n    if (!pointer.wasMoved(evt)) return\n    if (disabled) return\n\n    // Skip if the option is already active, however, if the activation trigger\n    // is not `Pointer` we have to convert it to a `Pointer` trigger\n    // activation instead.\n    if (active && machine.state.activationTrigger === ActivationTrigger.Pointer) return\n\n    // pointermove / mousemove will only be fired when the pointer is actually\n    // moving, therefore we can go to the option with the `Pointer` activation\n    // trigger.\n    machine.actions.goToOption({ focus: Focus.Specific, id }, ActivationTrigger.Pointer)\n  })\n\n  let handleLeave = useEvent((evt) => {\n    if (!pointer.wasMoved(evt)) return\n    if (disabled) return\n    if (!active) return\n\n    // pointerenter / mouseenter will be fired when the mouse is on top of an\n    // element that scrolls into view even when using the keyboard to\n    // navigate. Only handle the event when the pointer was actually moved.\n    if (machine.state.activationTrigger !== ActivationTrigger.Pointer) return\n\n    machine.actions.goToOption({ focus: Focus.Nothing })\n  })\n\n  let slot = useSlot<OptionRenderPropArg>({\n    active,\n    focus: active,\n    selected,\n    disabled,\n    selectedOption: selected && usedInSelectedOption,\n  })\n  let ourProps = !usedInSelectedOption\n    ? {\n        id,\n        ref: optionRef,\n        role: 'option',\n        tabIndex: disabled === true ? undefined : -1,\n        'aria-disabled': disabled === true ? true : undefined,\n        // According to the WAI-ARIA best practices, we should use aria-checked for\n        // multi-select,but Voice-Over disagrees. So we use aria-checked instead for\n        // both single and multi-select.\n        'aria-selected': selected,\n        disabled: undefined, // Never forward the `disabled` prop\n        onClick: handleClick,\n        onFocus: handleFocus,\n        onPointerEnter: handleEnter,\n        onMouseEnter: handleEnter,\n        onPointerMove: handleMove,\n        onMouseMove: handleMove,\n        onPointerLeave: handleLeave,\n        onMouseLeave: handleLeave,\n      }\n    : {}\n\n  let render = useRender()\n\n  if (!selected && usedInSelectedOption) {\n    return null\n  }\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_OPTION_TAG,\n    name: 'Listbox.Option',\n  })\n}\n\n// ---\n\nlet DEFAULT_SELECTED_OPTION_TAG = Fragment\ntype SelectedOptionRenderPropArg = {}\ntype SelectedOptionPropsWeControl = never\n\nexport type ListboxSelectedOptionProps<\n  TTag extends ElementType = typeof DEFAULT_SELECTED_OPTION_TAG,\n> = Props<\n  TTag,\n  SelectedOptionRenderPropArg,\n  SelectedOptionPropsWeControl,\n  {\n    options: React.ReactNode\n    placeholder?: React.ReactNode\n  }\n>\n\nfunction SelectedFn<TTag extends ElementType = typeof DEFAULT_SELECTED_OPTION_TAG>(\n  props: ListboxSelectedOptionProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { options: children, placeholder, ...theirProps } = props\n\n  let selectedRef = useSyncRefs(ref)\n  let ourProps = { ref: selectedRef }\n  let data = useData('ListboxSelectedOption')\n  let slot = useSlot<SelectedOptionRenderPropArg>({})\n\n  let shouldShowPlaceholder =\n    data.value === undefined ||\n    data.value === null ||\n    (data.mode === ValueMode.Multi && Array.isArray(data.value) && data.value.length === 0)\n\n  let render = useRender()\n\n  return (\n    <SelectedOptionContext.Provider value={true}>\n      {render({\n        ourProps,\n        theirProps: {\n          ...theirProps,\n          children: <>{placeholder && shouldShowPlaceholder ? placeholder : children}</>,\n        },\n        slot,\n        defaultTag: DEFAULT_SELECTED_OPTION_TAG,\n        name: 'ListboxSelectedOption',\n      })}\n    </SelectedOptionContext.Provider>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentListbox extends HasDisplayName {\n  <\n    TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG,\n    TType = string,\n    TActualType = TType extends (infer U)[] ? U : TType,\n  >(\n    props: ListboxProps<TTag, TType, TActualType> & RefProp<typeof ListboxFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentListboxButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: ListboxButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentListboxLabel extends _internal_ComponentLabel {}\n\nexport interface _internal_ComponentListboxOptions extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(\n    props: ListboxOptionsProps<TTag> & RefProp<typeof OptionsFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentListboxOption extends HasDisplayName {\n  <\n    TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n    TType = Parameters<typeof ListboxRoot>[0]['value'],\n  >(\n    props: ListboxOptionProps<TTag, TType> & RefProp<typeof OptionFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentListboxSelectedOption extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_SELECTED_OPTION_TAG>(\n    props: ListboxSelectedOptionProps<TTag> & RefProp<typeof SelectedFn>\n  ): React.JSX.Element\n}\n\nlet ListboxRoot = forwardRefWithAs(ListboxFn) as _internal_ComponentListbox\nexport let ListboxButton = forwardRefWithAs(ButtonFn) as _internal_ComponentListboxButton\n/** @deprecated use `<Label>` instead of `<ListboxLabel>` */\nexport let ListboxLabel = Label as _internal_ComponentListboxLabel\nexport let ListboxOptions = forwardRefWithAs(OptionsFn) as _internal_ComponentListboxOptions\nexport let ListboxOption = forwardRefWithAs(OptionFn) as _internal_ComponentListboxOption\nexport let ListboxSelectedOption = forwardRefWithAs(\n  SelectedFn\n) as _internal_ComponentListboxSelectedOption\n\nexport let Listbox = Object.assign(ListboxRoot, {\n  /** @deprecated use `<ListboxButton>` instead of `<Listbox.Button>` */\n  Button: ListboxButton,\n  /** @deprecated use `<Label>` instead of `<Listbox.Label>` */\n  Label: ListboxLabel,\n  /** @deprecated use `<ListboxOptions>` instead of `<Listbox.Options>` */\n  Options: ListboxOptions,\n  /** @deprecated use `<ListboxOption>` instead of `<Listbox.Option>` */\n  Option: ListboxOption,\n  /** @deprecated use `<ListboxSelectedOption>` instead of `<Listbox.SelectedOption>` */\n  SelectedOption: ListboxSelectedOption,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox-button/listbox-button.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../listbox/listbox'\nexport { ListboxButton } from '../listbox/listbox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox-label/listbox-label.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../listbox/listbox'\nexport { ListboxLabel } from '../listbox/listbox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox-option/listbox-option.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../listbox/listbox'\nexport { ListboxOption } from '../listbox/listbox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox-options/listbox-options.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../listbox/listbox'\nexport { ListboxOptions } from '../listbox/listbox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/listbox-selected-option/listbox-selected-option.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../listbox/listbox'\nexport { ListboxSelectedOption } from '../listbox/listbox'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu/menu-machine-glue.tsx",
    "content": "import { createContext, useContext, useMemo } from 'react'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { MenuMachine } from './menu-machine'\n\nexport const MenuContext = createContext<MenuMachine | null>(null)\nexport function useMenuMachineContext(component: string) {\n  let context = useContext(MenuContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Menu /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuMachine)\n    throw err\n  }\n  return context\n}\n\nexport function useMenuMachine({ id, __demoMode = false }: { id: string; __demoMode?: boolean }) {\n  let machine = useMemo(() => MenuMachine.new({ id, __demoMode }), [])\n  useOnUnmount(() => machine.dispose())\n  return machine\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu/menu-machine.ts",
    "content": "import { Machine, batch } from '../../machine'\nimport { ActionTypes as StackActionTypes, stackMachines } from '../../machines/stack-machine'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport {\n  ElementPositionState,\n  computeVisualPosition,\n  detectMovement,\n} from '../../utils/element-movement'\nimport { sortByDomNode } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\n\nexport enum MenuState {\n  Open,\n  Closed,\n}\n\nexport enum ActivationTrigger {\n  Pointer,\n  Other,\n}\n\nexport type MenuItemDataRef = {\n  current: {\n    textValue?: string\n    disabled: boolean\n    domRef: { current: HTMLElement | null }\n  }\n}\n\nexport interface State {\n  id: string\n\n  __demoMode: boolean\n  menuState: MenuState\n\n  buttonElement: HTMLButtonElement | null\n  itemsElement: HTMLElement | null\n\n  items: { id: string; dataRef: MenuItemDataRef }[]\n  searchQuery: string\n  activeItemIndex: number | null\n  activationTrigger: ActivationTrigger\n\n  pendingShouldSort: boolean\n  pendingFocus: { focus: Exclude<Focus, Focus.Specific> } | { focus: Focus.Specific; id: string }\n\n  // Track button to determine if it moved\n  buttonPositionState: ElementPositionState\n}\n\nexport enum ActionTypes {\n  OpenMenu,\n  CloseMenu,\n\n  GoToItem,\n  Search,\n  ClearSearch,\n  RegisterItems,\n  UnregisterItems,\n\n  SetButtonElement,\n  SetItemsElement,\n\n  SortItems,\n\n  MarkButtonAsMoved,\n}\n\nfunction adjustOrderedState(\n  state: State,\n  adjustment: (items: State['items']) => State['items'] = (i) => i\n) {\n  let currentActiveItem = state.activeItemIndex !== null ? state.items[state.activeItemIndex] : null\n\n  let sortedItems = sortByDomNode(\n    adjustment(state.items.slice()),\n    (item) => item.dataRef.current.domRef.current\n  )\n\n  // If we inserted an item before the current active item then the active item index\n  // would be wrong. To fix this, we will re-lookup the correct index.\n  let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null\n\n  // Reset to `null` in case the currentActiveItem was removed.\n  if (adjustedActiveItemIndex === -1) {\n    adjustedActiveItemIndex = null\n  }\n\n  return {\n    items: sortedItems,\n    activeItemIndex: adjustedActiveItemIndex,\n  }\n}\n\nexport type Actions =\n  | { type: ActionTypes.CloseMenu }\n  | {\n      type: ActionTypes.OpenMenu\n      focus: { focus: Exclude<Focus, Focus.Specific> } | { focus: Focus.Specific; id: string }\n      trigger?: ActivationTrigger\n    }\n  | { type: ActionTypes.GoToItem; focus: Focus.Specific; id: string; trigger?: ActivationTrigger }\n  | {\n      type: ActionTypes.GoToItem\n      focus: Exclude<Focus, Focus.Specific>\n      trigger?: ActivationTrigger\n    }\n  | { type: ActionTypes.Search; value: string }\n  | { type: ActionTypes.ClearSearch }\n  | { type: ActionTypes.RegisterItems; items: { id: string; dataRef: MenuItemDataRef }[] }\n  | { type: ActionTypes.UnregisterItems; items: string[] }\n  | { type: ActionTypes.SetButtonElement; element: HTMLButtonElement | null }\n  | { type: ActionTypes.SetItemsElement; element: HTMLElement | null }\n  | { type: ActionTypes.SortItems }\n  | { type: ActionTypes.MarkButtonAsMoved }\n\nlet reducers: {\n  [P in ActionTypes]: (state: State, action: Extract<Actions, { type: P }>) => State\n} = {\n  [ActionTypes.CloseMenu](state) {\n    if (state.menuState === MenuState.Closed) return state\n    let buttonPositionState = state.buttonElement\n      ? ElementPositionState.Tracked(computeVisualPosition(state.buttonElement))\n      : state.buttonPositionState\n\n    return {\n      ...state,\n      activeItemIndex: null,\n      pendingFocus: { focus: Focus.Nothing },\n      menuState: MenuState.Closed,\n      buttonPositionState,\n    }\n  },\n  [ActionTypes.OpenMenu](state, action) {\n    if (state.menuState === MenuState.Open) return state\n\n    return {\n      ...state,\n      /* We can turn off demo mode once we re-open the `Menu` */\n      __demoMode: false,\n      pendingFocus: action.focus,\n      menuState: MenuState.Open,\n      buttonPositionState: ElementPositionState.Idle,\n    }\n  },\n  [ActionTypes.GoToItem]: (state, action) => {\n    if (state.menuState === MenuState.Closed) return state\n\n    let base = {\n      ...state,\n      searchQuery: '',\n      activationTrigger: action.trigger ?? ActivationTrigger.Other,\n      __demoMode: false,\n    }\n\n    // Optimization:\n    //\n    // There is no need to sort the DOM nodes if we know that we don't want to focus anything\n    if (action.focus === Focus.Nothing) {\n      return {\n        ...base,\n        activeItemIndex: null,\n      }\n    }\n\n    // Optimization:\n    //\n    // There is no need to sort the DOM nodes if we know exactly where to go\n    if (action.focus === Focus.Specific) {\n      return {\n        ...base,\n        activeItemIndex: state.items.findIndex((o) => o.id === action.id),\n      }\n    }\n\n    // Optimization:\n    //\n    // If the current DOM node and the previous DOM node are next to each other,\n    // or if the previous DOM node is already the first DOM node, then we don't\n    // have to sort all the DOM nodes.\n    else if (action.focus === Focus.Previous) {\n      let activeItemIdx = state.activeItemIndex\n      if (activeItemIdx !== null) {\n        let currentDom = state.items[activeItemIdx].dataRef.current.domRef\n        let previousItemIndex = calculateActiveIndex(action, {\n          resolveItems: () => state.items,\n          resolveActiveIndex: () => state.activeItemIndex,\n          resolveId: (item) => item.id,\n          resolveDisabled: (item) => item.dataRef.current.disabled,\n        })\n        if (previousItemIndex !== null) {\n          let previousDom = state.items[previousItemIndex].dataRef.current.domRef\n          if (\n            // Next to each other\n            currentDom.current?.previousElementSibling === previousDom.current ||\n            // Or already the first element\n            previousDom.current?.previousElementSibling === null\n          ) {\n            return {\n              ...base,\n              activeItemIndex: previousItemIndex,\n            }\n          }\n        }\n      }\n    }\n\n    // Optimization:\n    //\n    // If the current DOM node and the next DOM node are next to each other, or\n    // if the next DOM node is already the last DOM node, then we don't have to\n    // sort all the DOM nodes.\n    else if (action.focus === Focus.Next) {\n      let activeItemIdx = state.activeItemIndex\n      if (activeItemIdx !== null) {\n        let currentDom = state.items[activeItemIdx].dataRef.current.domRef\n        let nextItemIndex = calculateActiveIndex(action, {\n          resolveItems: () => state.items,\n          resolveActiveIndex: () => state.activeItemIndex,\n          resolveId: (item) => item.id,\n          resolveDisabled: (item) => item.dataRef.current.disabled,\n        })\n        if (nextItemIndex !== null) {\n          let nextDom = state.items[nextItemIndex].dataRef.current.domRef\n          if (\n            // Next to each other\n            currentDom.current?.nextElementSibling === nextDom.current ||\n            // Or already the last element\n            nextDom.current?.nextElementSibling === null\n          ) {\n            return {\n              ...base,\n              activeItemIndex: nextItemIndex,\n            }\n          }\n        }\n      }\n    }\n\n    // Slow path:\n    //\n    // Ensure all the items are correctly sorted according to DOM position\n    let adjustedState = adjustOrderedState(state)\n    let activeItemIndex = calculateActiveIndex(action, {\n      resolveItems: () => adjustedState.items,\n      resolveActiveIndex: () => adjustedState.activeItemIndex,\n      resolveId: (item) => item.id,\n      resolveDisabled: (item) => item.dataRef.current.disabled,\n    })\n\n    return {\n      ...base,\n      ...adjustedState,\n      activeItemIndex,\n    }\n  },\n  [ActionTypes.Search]: (state, action) => {\n    let wasAlreadySearching = state.searchQuery !== ''\n    let offset = wasAlreadySearching ? 0 : 1\n    let searchQuery = state.searchQuery + action.value.toLowerCase()\n\n    let reOrderedItems =\n      state.activeItemIndex !== null\n        ? state.items\n            .slice(state.activeItemIndex + offset)\n            .concat(state.items.slice(0, state.activeItemIndex + offset))\n        : state.items\n\n    let matchingItem = reOrderedItems.find(\n      (item) =>\n        item.dataRef.current.textValue?.startsWith(searchQuery) && !item.dataRef.current.disabled\n    )\n\n    let matchIdx = matchingItem ? state.items.indexOf(matchingItem) : -1\n    if (matchIdx === -1 || matchIdx === state.activeItemIndex) return { ...state, searchQuery }\n    return {\n      ...state,\n      searchQuery,\n      activeItemIndex: matchIdx,\n      activationTrigger: ActivationTrigger.Other,\n    }\n  },\n  [ActionTypes.ClearSearch](state) {\n    if (state.searchQuery === '') return state\n    return { ...state, searchQuery: '', searchActiveItemIndex: null }\n  },\n  [ActionTypes.RegisterItems]: (state, action) => {\n    let items = state.items.concat(action.items.map((item) => item))\n\n    let activeItemIndex = state.activeItemIndex\n    if (state.pendingFocus.focus !== Focus.Nothing) {\n      activeItemIndex = calculateActiveIndex(state.pendingFocus, {\n        resolveItems: () => items,\n        resolveActiveIndex: () => state.activeItemIndex,\n        resolveId: (item) => item.id,\n        resolveDisabled: (item) => item.dataRef.current.disabled,\n      })\n    }\n    return {\n      ...state,\n      items,\n      activeItemIndex,\n      pendingFocus: { focus: Focus.Nothing },\n      pendingShouldSort: true,\n    }\n  },\n  [ActionTypes.UnregisterItems]: (state, action) => {\n    let items = state.items\n\n    let idxs = []\n    let ids = new Set(action.items)\n    for (let [idx, item] of items.entries()) {\n      if (ids.has(item.id)) {\n        idxs.push(idx)\n        ids.delete(item.id)\n        if (ids.size === 0) break\n      }\n    }\n\n    if (idxs.length > 0) {\n      items = items.slice()\n      for (let idx of idxs.reverse()) {\n        items.splice(idx, 1)\n      }\n    }\n\n    return {\n      ...state,\n      items,\n      activationTrigger: ActivationTrigger.Other,\n    }\n  },\n\n  [ActionTypes.SetButtonElement]: (state, action) => {\n    if (state.buttonElement === action.element) return state\n    return { ...state, buttonElement: action.element }\n  },\n  [ActionTypes.SetItemsElement]: (state, action) => {\n    if (state.itemsElement === action.element) return state\n    return { ...state, itemsElement: action.element }\n  },\n  [ActionTypes.SortItems]: (state) => {\n    if (!state.pendingShouldSort) return state\n\n    return {\n      ...state,\n      ...adjustOrderedState(state),\n      pendingShouldSort: false,\n    }\n  },\n  [ActionTypes.MarkButtonAsMoved](state) {\n    if (state.buttonPositionState.kind !== 'Tracked') return state\n\n    return {\n      ...state,\n      buttonPositionState: ElementPositionState.Moved,\n    }\n  },\n}\n\nexport class MenuMachine extends Machine<State, Actions> {\n  static new({ id, __demoMode = false }: { id: string; __demoMode?: boolean }) {\n    return new MenuMachine({\n      id,\n      __demoMode,\n      menuState: __demoMode ? MenuState.Open : MenuState.Closed,\n      buttonElement: null,\n      itemsElement: null,\n      items: [],\n      searchQuery: '',\n      activeItemIndex: null,\n      activationTrigger: ActivationTrigger.Other,\n      pendingShouldSort: false,\n      pendingFocus: { focus: Focus.Nothing },\n      buttonPositionState: ElementPositionState.Idle,\n    })\n  }\n\n  constructor(initialState: State) {\n    super(initialState)\n\n    this.on(ActionTypes.RegisterItems, () => {\n      // Schedule a sort of the items when the DOM is ready. This doesn't\n      // change anything rendering wise, but the sorted items are used when\n      // using arrow keys so we can jump to previous / next items.\n      this.disposables.requestAnimationFrame(() => {\n        this.send({ type: ActionTypes.SortItems })\n      })\n    })\n\n    // When the menu is open, and it's not on the top of the hierarchy, we\n    // should close it again.\n    {\n      let id = this.state.id\n      let stackMachine = stackMachines.get(null)\n\n      this.disposables.add(\n        stackMachine.on(StackActionTypes.Push, (state) => {\n          if (!stackMachine.selectors.isTop(state, id) && this.state.menuState === MenuState.Open) {\n            this.send({ type: ActionTypes.CloseMenu })\n          }\n        })\n      )\n\n      this.on(ActionTypes.OpenMenu, () => stackMachine.actions.push(id))\n      this.on(ActionTypes.CloseMenu, () => stackMachine.actions.pop(id))\n    }\n\n    // Track whether the button moved or not\n    this.disposables.group((d) => {\n      this.on(ActionTypes.CloseMenu, (state) => {\n        if (!state.buttonElement) return\n\n        d.dispose()\n        d.add(\n          detectMovement(state.buttonElement, state.buttonPositionState, () => {\n            this.send({ type: ActionTypes.MarkButtonAsMoved })\n          })\n        )\n      })\n    })\n  }\n\n  reduce(state: Readonly<State>, action: Actions): State {\n    return match(action.type, reducers, state, action)\n  }\n\n  actions = {\n    // Batched version to register multiple items at the same time\n    registerItem: batch(() => {\n      let items: { id: string; dataRef: MenuItemDataRef }[] = []\n      let seen = new Set<MenuItemDataRef>()\n\n      return [\n        (id: string, dataRef: MenuItemDataRef) => {\n          if (seen.has(dataRef)) return\n          seen.add(dataRef)\n          items.push({ id, dataRef })\n        },\n        () => {\n          seen.clear()\n          return this.send({ type: ActionTypes.RegisterItems, items: items.splice(0) })\n        },\n      ]\n    }),\n    unregisterItem: batch(() => {\n      let items: string[] = []\n\n      return [\n        (id: string) => items.push(id),\n        () => this.send({ type: ActionTypes.UnregisterItems, items: items.splice(0) }),\n      ]\n    }),\n  }\n\n  selectors = {\n    activeDescendantId(state: State) {\n      let activeItemIndex = state.activeItemIndex\n      let items = state.items\n      return activeItemIndex === null ? undefined : items[activeItemIndex]?.id\n    },\n\n    isActive(state: State, id: string) {\n      let activeItemIndex = state.activeItemIndex\n      let items = state.items\n\n      return activeItemIndex !== null ? items[activeItemIndex]?.id === id : false\n    },\n\n    shouldScrollIntoView(state: State, id: string) {\n      if (state.__demoMode) return false\n      if (state.menuState !== MenuState.Open) return false\n      if (state.activationTrigger === ActivationTrigger.Pointer) return false\n      return this.isActive(state, id)\n    },\n\n    didButtonMove(state: State) {\n      return state.buttonPositionState.kind === 'Moved'\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu/menu.test.tsx",
    "content": "import { act, render, waitFor } from '@testing-library/react'\nimport React, { Fragment, createElement, createRef, useEffect } from 'react'\nimport {\n  MenuState,\n  assertActiveElement,\n  assertMenu,\n  assertMenuButton,\n  assertMenuButtonLinkedWithMenu,\n  assertMenuItem,\n  assertMenuLinkedWithMenuItem,\n  assertNoActiveMenuItem,\n  getByText,\n  getMenu,\n  getMenuButton,\n  getMenuButtons,\n  getMenuItems,\n  getMenus,\n} from '../../test-utils/accessibility-assertions'\nimport {\n  Keys,\n  MouseButton,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  rawClick,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Transition } from '../transition/transition'\nimport { Menu, MenuButton, MenuItem, MenuItems } from './menu'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['Menu.Button', Menu.Button],\n    ['Menu.Items', Menu.Items],\n    ['Menu.Item', Menu.Item],\n  ])(\n    'should error when we are using a <%s /> without a parent <Menu />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component as any))).toThrow(\n        `<${name} /> is missing a parent <Menu /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Menu without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Menu', () => {\n    it(\n      'should be possible to render a Menu using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            {({ open }) => (\n              <>\n                <Menu.Button>Trigger</Menu.Button>\n                {open && (\n                  <Menu.Items>\n                    <Menu.Item as=\"a\">Item A</Menu.Item>\n                    <Menu.Item as=\"a\">Item B</Menu.Item>\n                    <Menu.Item as=\"a\">Item C</Menu.Item>\n                  </Menu.Items>\n                )}\n              </>\n            )}\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to manually close the Menu using the exposed close function',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            {({ close }) => (\n              <>\n                <Menu.Button>Trigger</Menu.Button>\n                <Menu.Items>\n                  <Menu.Item>\n                    <button\n                      onClick={(e) => {\n                        e.preventDefault()\n                        close()\n                      }}\n                    >\n                      Close\n                    </button>\n                  </Menu.Item>\n                </Menu.Items>\n              </>\n            )}\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenu({ state: MenuState.Visible })\n\n        await click(getByText('Close'))\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n  })\n\n  describe('Menu.Button', () => {\n    it(\n      'should be possible to render a Menu.Button using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>{(slot) => <>{JSON.stringify(slot)}</>}</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({\n          state: MenuState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertMenu({ state: MenuState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Menu.Button using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button as=\"div\" role=\"button\">\n              {(slot) => <>{JSON.stringify(slot)}</>}\n            </Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({\n          state: MenuState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertMenu({ state: MenuState.Visible })\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n          </Menu>\n        )\n\n        expect(getMenuButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        render(\n          <Menu>\n            <Menu.Button type=\"submit\">Trigger</Menu.Button>\n          </Menu>\n        )\n\n        expect(getMenuButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n        let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n          <button ref={ref} {...props} />\n        ))\n\n        render(\n          <Menu>\n            <Menu.Button as={CustomButton}>Trigger</Menu.Button>\n          </Menu>\n        )\n\n        expect(getMenuButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        render(\n          <Menu>\n            <Menu.Button as=\"div\">Trigger</Menu.Button>\n          </Menu>\n        )\n\n        expect(getMenuButton()).not.toHaveAttribute('type')\n      })\n\n      it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n        let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n          <div ref={ref} {...props} />\n        ))\n\n        render(\n          <Menu>\n            <Menu.Button as={CustomButton}>Trigger</Menu.Button>\n          </Menu>\n        )\n\n        expect(getMenuButton()).not.toHaveAttribute('type')\n      })\n    })\n\n    it(\n      'should be possible to render a MenuButton using as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <MenuButton as={Fragment}>\n              <button>Toggle</button>\n            </MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n      })\n    )\n  })\n\n  describe('Menu.Items', () => {\n    it(\n      'should be possible to render Menu.Items using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              {(data) => (\n                <>\n                  <Menu.Item as=\"a\">{JSON.stringify(data)}</Menu.Item>\n                </>\n              )}\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({\n          state: MenuState.Visible,\n        })\n        assertMenu({\n          state: MenuState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n\n    it('should be possible to always render the Menu.Items if we provide it a `static` prop', () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items static>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Let's verify that the Menu is already there\n      expect(getMenu()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the Menu.Items', async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items unmount={false}>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenu({ state: MenuState.InvisibleHidden })\n\n      // Let's open the Menu, to see if it is not hidden anymore\n      await click(getMenuButton())\n\n      assertMenu({ state: MenuState.Visible })\n    })\n  })\n\n  describe('Menu.Item', () => {\n    it(\n      'should be possible to render a Menu.Item using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">{(slot) => <>{JSON.stringify(slot)}</>}</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenuButton({\n          state: MenuState.Visible,\n        })\n        assertMenu({\n          state: MenuState.Visible,\n          textContent: JSON.stringify({ active: false, focus: false, disabled: false }),\n        })\n      })\n    )\n\n    it(\n      'should be possible to manually close the Menu using the exposed close function',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item>\n                {({ close }) => (\n                  <button\n                    onClick={(e) => {\n                      e.preventDefault()\n                      close()\n                    }}\n                  >\n                    Close\n                  </button>\n                )}\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        await click(getMenuButton())\n\n        assertMenu({ state: MenuState.Visible })\n\n        await click(getByText('Close'))\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it('should not override an explicit disabled prop on MenuItems child', async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item>{({ disabled }) => <button disabled={disabled}>Item A</button>}</Menu.Item>\n            <Menu.Item>{({ disabled }) => <button disabled={disabled}>Item B</button>}</Menu.Item>\n            <Menu.Item disabled>\n              {({ disabled }) => <button disabled={disabled}>Item C</button>}\n            </Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      getMenuButton()?.focus()\n\n      await press(Keys.Enter)\n\n      assertMenuButton({\n        state: MenuState.Visible,\n      })\n      assertMenu({ state: MenuState.Visible })\n      assertMenuItem(getMenuItems()[0], {\n        tag: 'button',\n        attributes: { 'data-focus': '' },\n      })\n      assertMenuItem(getMenuItems()[1], {\n        tag: 'button',\n        attributes: {},\n      })\n      assertMenuItem(getMenuItems()[2], {\n        tag: 'button',\n        attributes: { disabled: '' },\n      })\n    })\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    function Example({ hide = false }) {\n      return (\n        <>\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"button\">Item 1</Menu.Item>\n              {!hide && <Menu.Item as=\"button\">Item 2</Menu.Item>}\n              <Menu.Item as=\"button\">Item 3</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        </>\n      )\n    }\n\n    let { rerender } = render(<Example />)\n\n    // Open the Menu\n    await click(getByText('Trigger'))\n\n    rerender(<Example hide={true} />) // Remove Menu.Item 2\n    rerender(<Example hide={false} />) // Re-add Menu.Item 2\n\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // Focus the first item\n    await press(Keys.ArrowDown)\n\n    // Verify that the first menu item is active\n    assertMenuLinkedWithMenuItem(items[0])\n\n    await press(Keys.ArrowDown)\n    // Verify that the second menu item is active\n    assertMenuLinkedWithMenuItem(items[1])\n\n    await press(Keys.ArrowDown)\n    // Verify that the third menu item is active\n    assertMenuLinkedWithMenuItem(items[2])\n  })\n\n  it(\n    'should be possible to open a menu programmatically via .click()',\n    suppressConsoleLogs(async () => {\n      let btnRef = createRef<HTMLButtonElement>()\n\n      render(\n        <Menu>\n          <MenuButton ref={btnRef}>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      act(() => btnRef.current?.click())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({ state: MenuState.Visible })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n    })\n  )\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to conditionally render classNames (aka className can be a function?!)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\" className={(bag) => JSON.stringify(bag)}>\n              Item A\n            </Menu.Item>\n            <Menu.Item as=\"a\" disabled className={(bag) => JSON.stringify(bag)}>\n              Item B\n            </Menu.Item>\n            <Menu.Item as=\"a\" className=\"no-special-treatment\">\n              Item C\n            </Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // Verify correct classNames\n      expect('' + items[0].classList).toEqual(\n        JSON.stringify({ active: false, focus: false, disabled: false })\n      )\n      expect('' + items[1].classList).toEqual(\n        JSON.stringify({ active: false, focus: false, disabled: true })\n      )\n      expect('' + items[2].classList).toEqual('no-special-treatment')\n\n      // Double check that nothing is active\n      assertNoActiveMenuItem()\n\n      // Make the first item active\n      await press(Keys.ArrowDown)\n\n      // Verify the classNames\n      expect('' + items[0].classList).toEqual(\n        JSON.stringify({ active: true, focus: true, disabled: false })\n      )\n      expect('' + items[1].classList).toEqual(\n        JSON.stringify({ active: false, focus: false, disabled: true })\n      )\n      expect('' + items[2].classList).toEqual('no-special-treatment')\n\n      // Double check that the first item is the active one\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // Let's go down, this should go to the third item since the second item is disabled!\n      await press(Keys.ArrowDown)\n\n      // Verify the classNames\n      expect('' + items[0].classList).toEqual(\n        JSON.stringify({ active: false, focus: false, disabled: false })\n      )\n      expect('' + items[1].classList).toEqual(\n        JSON.stringify({ active: false, focus: false, disabled: true })\n      )\n      expect('' + items[2].classList).toEqual('no-special-treatment')\n\n      // Double check that the last item is the active one\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n  )\n\n  it(\n    'should be possible to swap the menu item with a button for example',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"button\">Item A</Menu.Item>\n            <Menu.Item as=\"button\">Item B</Menu.Item>\n            <Menu.Item as=\"button\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify items are buttons now\n      let items = getMenuItems()\n      items.forEach((item) => assertMenuItem(item, { tag: 'button' }))\n    })\n  )\n\n  it(\n    'should mark all the elements between Menu.Items and Menu.Item with role none',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <div className=\"outer\">\n            <Menu.Items>\n              <div className=\"inner py-1\">\n                <Menu.Item as=\"button\">Item A</Menu.Item>\n                <Menu.Item as=\"button\">Item B</Menu.Item>\n              </div>\n              <div className=\"inner py-1\">\n                <Menu.Item as=\"button\">Item C</Menu.Item>\n                <Menu.Item>\n                  <div>\n                    <div className=\"outer\">Item D</div>\n                  </div>\n                </Menu.Item>\n              </div>\n              <div className=\"inner py-1\">\n                <form className=\"inner\">\n                  <Menu.Item as=\"button\">Item E</Menu.Item>\n                </form>\n              </div>\n            </Menu.Items>\n          </div>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      expect.hasAssertions()\n\n      document.querySelectorAll('.outer').forEach((element) => {\n        expect(element).not.toHaveAttribute('role', 'none')\n      })\n\n      document.querySelectorAll('.inner').forEach((element) => {\n        expect(element).toHaveAttribute('role', 'none')\n      })\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {\n    useEffect(() => {\n      fn(`Mounting - ${name}`)\n      return () => {\n        fn(`Unmounting - ${name}`)\n      }\n    }, [fn, name])\n    return null\n  }\n\n  it(\n    'should be possible to wrap the Menu.Items with a Transition component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Debug name=\"Menu\" fn={orderFn} />\n          <Transition>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Menu.Items>\n              <Menu.Item as=\"a\">\n                {(data) => (\n                  <>\n                    {JSON.stringify(data)}\n                    <Debug name=\"Menu.Item\" fn={orderFn} />\n                  </>\n                )}\n              </Menu.Item>\n            </Menu.Items>\n          </Transition>\n        </Menu>\n      )\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await rawClick(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n      })\n      assertMenu({\n        state: MenuState.Visible,\n        textContent: JSON.stringify({ active: false, focus: false, disabled: false }),\n      })\n\n      await rawClick(getMenuButton())\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Menu'],\n        ['Mounting - Transition'],\n        ['Mounting - Menu.Item'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Menu.Item'],\n      ])\n    })\n  )\n\n  it(\n    'should be possible to wrap the Menu.Items with a Transition.Child component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Debug name=\"Menu\" fn={orderFn} />\n          <Transition.Child>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Menu.Items>\n              <Menu.Item as=\"a\">\n                {(data) => (\n                  <>\n                    {JSON.stringify(data)}\n                    <Debug name=\"Menu.Item\" fn={orderFn} />\n                  </>\n                )}\n              </Menu.Item>\n            </Menu.Items>\n          </Transition.Child>\n        </Menu>\n      )\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await rawClick(getMenuButton())\n\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        textContent: JSON.stringify({ active: false, focus: false, disabled: false }),\n      })\n\n      await rawClick(getMenuButton())\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Menu'],\n        ['Mounting - Transition'],\n        ['Mounting - Menu.Item'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Menu.Item'],\n      ])\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the menu with Enter',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n\n        // Verify that the first menu item is active\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the menu with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button disabled>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Try to open the menu\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should have no active menu item when there are no menu items at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items />\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n        assertMenu({ state: MenuState.Visible })\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should focus the first non disabled menu item when opening with Enter',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // Verify that the first non-disabled menu item is active\n        assertMenuLinkedWithMenuItem(items[1])\n      })\n    )\n\n    it(\n      'should focus the first non disabled menu item when opening with Enter (jump over multiple disabled ones)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // Verify that the first non-disabled menu item is active\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should have no active menu item upon Enter key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should be possible to close the menu with Enter when there is no active menuitem',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Close menu\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n\n    it(\n      'should be possible to close the menu with Enter and invoke the active menu item',\n      suppressConsoleLogs(async () => {\n        let clickHandler = jest.fn()\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" onClick={clickHandler}>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Activate the first menu item\n        let items = getMenuItems()\n        await mouseMove(items[0])\n\n        // Close menu, and invoke the item\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n\n        // Verify the \"click\" went through on the `a` tag\n        expect(clickHandler).toHaveBeenCalled()\n      })\n    )\n  })\n\n  it(\n    'should be possible to use a button as a menu item and invoke it upon Enter',\n    suppressConsoleLogs(async () => {\n      let clickHandler = jest.fn()\n\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"button\" onClick={clickHandler}>\n              Item B\n            </Menu.Item>\n            <Menu.Item>\n              <button onClick={clickHandler}>Item C</button>\n            </Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n\n      // Activate the second menu item\n      let items = getMenuItems()\n      await mouseMove(items[1])\n\n      // Close menu, and invoke the item\n      await press(Keys.Enter)\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button got \"clicked\"\n      expect(clickHandler).toHaveBeenCalledTimes(1)\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n\n      // Click the menu button again\n      await click(getMenuButton())\n\n      // Activate the last menu item\n      await mouseMove(getMenuItems()[2])\n\n      // Close menu, and invoke the item\n      await press(Keys.Enter)\n\n      // Verify the button got \"clicked\"\n      expect(clickHandler).toHaveBeenCalledTimes(2)\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  )\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the menu with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the menu with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button disabled>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Try to open the menu\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should have no active menu item when there are no menu items at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items />\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n        assertMenu({ state: MenuState.Visible })\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should focus the first non disabled menu item when opening with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n\n        let items = getMenuItems()\n\n        // Verify that the first non-disabled menu item is active\n        assertMenuLinkedWithMenuItem(items[1])\n      })\n    )\n\n    it(\n      'should focus the first non disabled menu item when opening with Space (jump over multiple disabled ones)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n\n        let items = getMenuItems()\n\n        // Verify that the first non-disabled menu item is active\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should have no active menu item upon Space key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should be possible to close the menu with Space when there is no active menuitem',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Close menu\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n\n    it(\n      'should be possible to close the menu with Space and invoke the active menu item',\n      suppressConsoleLogs(async () => {\n        let clickHandler = jest.fn()\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" onClick={clickHandler}>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Activate the first menu item\n        let items = getMenuItems()\n        await mouseMove(items[0])\n\n        // Close menu, and invoke the item\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the \"click\" went through on the `a` tag\n        expect(clickHandler).toHaveBeenCalled()\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it(\n      'should be possible to close an open menu with Escape',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Close menu\n        await press(Keys.Escape)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should close when we use Tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // Try to tab\n        await press(Keys.Tab)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus trap when we use Shift+Tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // Try to Shift+Tab\n        await press(shift(Keys.Tab))\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n  })\n\n  describe('`ArrowDown` key', () => {\n    it(\n      'should be possible to open the menu with ArrowDown',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowDown)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n\n        // Verify that the first menu item is active\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the menu with ArrowDown when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button disabled>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Try to open the menu\n        await press(Keys.ArrowDown)\n\n        // Verify it is still closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should have no active menu item when there are no menu items at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items />\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowDown)\n        assertMenu({ state: MenuState.Visible })\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowDown)\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should NOT be able to go down again (because last item). Current implementation won't go around.\n        await press(Keys.ArrowDown)\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the menu items and skip the first disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the menu items and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n  })\n\n  describe('`ArrowUp` key', () => {\n    it(\n      'should be possible to open the menu with ArrowUp and the last item should be active',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n\n        // ! ALERT: The LAST item should now be active\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should not be possible to open the menu with ArrowUp and the last item should be active when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button disabled>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Try to open the menu\n        await press(Keys.ArrowUp)\n\n        // Verify it is still closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should have no active menu item when there are no menu items at all',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items />\n          </Menu>\n        )\n\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n        assertMenu({ state: MenuState.Visible })\n\n        assertNoActiveMenuItem()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the menu items and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should not be possible to navigate up or down if there is only a single non-disabled item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should not be able to go up (because those are disabled)\n        await press(Keys.ArrowUp)\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should not be able to go down (because this is the last item)\n        await press(Keys.ArrowDown)\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n        assertMenu({ state: MenuState.Visible })\n        assertMenuButtonLinkedWithMenu()\n\n        // Verify we have menu items\n        let items = getMenuItems()\n        expect(items).toHaveLength(3)\n        items.forEach((item) => assertMenuItem(item))\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go down once\n        await press(Keys.ArrowUp)\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowUp)\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should NOT be able to go up again (because first item). Current implementation won't go around.\n        await press(Keys.ArrowUp)\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n  })\n\n  describe('`End` key', () => {\n    it(\n      'should be possible to use the End key to go to the last menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // We should be on the first item\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await press(Keys.End)\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the last non disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // We should be on the first item\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last non-disabled item\n        await press(Keys.End)\n        assertMenuLinkedWithMenuItem(items[1])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the first menu item if that is the only non-disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        let items = getMenuItems()\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should have no active menu item upon End key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        assertNoActiveMenuItem()\n      })\n    )\n  })\n\n  describe('`PageDown` key', () => {\n    it(\n      'should be possible to use the PageDown key to go to the last menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // We should be on the first item\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await press(Keys.PageDown)\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the last non disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.Enter)\n\n        let items = getMenuItems()\n\n        // We should be on the first item\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last non-disabled item\n        await press(Keys.PageDown)\n        assertMenuLinkedWithMenuItem(items[1])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the first menu item if that is the only non-disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        let items = getMenuItems()\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should have no active menu item upon PageDown key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        assertNoActiveMenuItem()\n      })\n    )\n  })\n\n  describe('`Home` key', () => {\n    it(\n      'should be possible to use the Home key to go to the first menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go to the first item\n        await press(Keys.Home)\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the first non disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n              <Menu.Item as=\"a\">Item D</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let items = getMenuItems()\n\n        // We should be on the first non-disabled item\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the last menu item if that is the only non-disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item D</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let items = getMenuItems()\n        assertMenuLinkedWithMenuItem(items[3])\n      })\n    )\n\n    it(\n      'should have no active menu item upon Home key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        assertNoActiveMenuItem()\n      })\n    )\n  })\n\n  describe('`PageUp` key', () => {\n    it(\n      'should be possible to use the PageUp key to go to the first menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">Item A</Menu.Item>\n              <Menu.Item as=\"a\">Item B</Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go to the first item\n        await press(Keys.PageUp)\n        assertMenuLinkedWithMenuItem(items[0])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the first non disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item C</Menu.Item>\n              <Menu.Item as=\"a\">Item D</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let items = getMenuItems()\n\n        // We should be on the first non-disabled item\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the last menu item if that is the only non-disabled menu item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\">Item D</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let items = getMenuItems()\n        assertMenuLinkedWithMenuItem(items[3])\n      })\n    )\n\n    it(\n      'should have no active menu item upon PageUp key press, when there are no non-disabled menu items',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\" disabled>\n                Item A\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item B\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item C\n              </Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                Item D\n              </Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        // We opened via click, we don't have an active item\n        assertNoActiveMenuItem()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        assertNoActiveMenuItem()\n      })\n    )\n  })\n\n  describe('`Any` key aka search', () => {\n    it(\n      'should be possible to type a full word that has a perfect match',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        let items = getMenuItems()\n\n        // We should be able to go to the second item\n        await type(word('bob'))\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go to the first item\n        await type(word('alice'))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await type(word('charlie'))\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to type a partial of a word',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go to the second item\n        await type(word('bo'))\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go to the first item\n        await type(word('ali'))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await type(word('char'))\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to type words with spaces',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">value a</Menu.Item>\n              <Menu.Item as=\"a\">value b</Menu.Item>\n              <Menu.Item as=\"a\">value c</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go to the second item\n        await type(word('value b'))\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go to the first item\n        await type(word('value a'))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await type(word('value c'))\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should not be possible to search for a disabled item',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\" disabled>\n                bob\n              </Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should not be able to go to the disabled item\n        await type(word('bo'))\n\n        // We should still be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it(\n      'should be possible to search for a word (case insensitive)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Focus the button\n        await focus(getMenuButton())\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // Search for bob in a different casing\n        await type(word('BO'))\n\n        // We should be on `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n      })\n    )\n\n    it(\n      'should be possible to search for the next occurrence',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        let items = getMenuItems()\n\n        // Search for bob\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for bob again\n        await type(word('b'))\n\n        // We should be on the second `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n      })\n    )\n\n    it(\n      'should stay on the same item while keystrokes still match',\n      suppressConsoleLogs(async () => {\n        render(\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        )\n\n        // Open menu\n        await click(getMenuButton())\n\n        let items = getMenuItems()\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"b\" in \"bob\"\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"b\" in \"bob\" again\n        await type(word('b'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"bo\" in \"bob\"\n        await type(word('bo'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"bo\" in \"bob\" again\n        await type(word('bo'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"bob\" in \"bob\"\n        await type(word('bob'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"bob\" in \"bob\" again\n        await type(word('bob'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to open a menu on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({ state: MenuState.Visible })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n    })\n  )\n\n  it(\n    'should not be possible to open a menu on right click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Try to open the menu\n      await click(getMenuButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open a menu on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button disabled>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Try to open the menu\n      await click(getMenuButton())\n\n      // Verify it is still closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a menu on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">Item A</Menu.Item>\n            <Menu.Item as=\"a\">Item B</Menu.Item>\n            <Menu.Item as=\"a\">Item C</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n\n      // Click to close\n      await click(getMenuButton())\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be a no-op when we click outside of a closed menu',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Verify that the window is closed\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Click something that is not related to the menu\n      await click(document.body)\n\n      // Should still be closed\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click outside of the menu which should close the menu',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      // Click something that is not related to the menu\n      await click(document.body)\n\n      // Should be closed now\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the menu which should close the menu (even if we press the menu button)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      // Click the menu button again\n      await click(getMenuButton())\n\n      // Should be closed now\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the menu on another menu button which should close the current menu and open the new menu',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n        </div>\n      )\n\n      let [button1, button2] = getMenuButtons()\n\n      // Click the first menu button\n      await click(button1)\n      expect(getMenus()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the first button\n      assertMenuButtonLinkedWithMenu(button1, getMenu())\n\n      // Click the second menu button\n      await click(button2)\n\n      expect(getMenus()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the second button\n      assertMenuButtonLinkedWithMenu(button2, getMenu())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the menu, on an element which is within a focusable element, which closes the menu',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n\n          <button id=\"btn\">\n            <span>Next</span>\n          </button>\n        </div>\n      )\n\n      // Click the menu button\n      await click(getMenuButton())\n\n      // Ensure the menu is open\n      assertMenu({ state: MenuState.Visible })\n\n      // Click the span inside the button\n      await click(getByText('Next'))\n\n      // Ensure the menu is closed\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Ensure the outside button is focused\n      assertActiveElement(document.getElementById('btn'))\n    })\n  )\n\n  // TODO: This test doesn't work — and it would be more suited for browser testing anyway\n  it.skip(\n    'should be possible to click outside of the menu into an iframe and which should close the menu',\n    suppressConsoleLogs(async () => {\n      render(\n        <div>\n          <Menu>\n            <Menu.Button>Trigger</Menu.Button>\n            <Menu.Items>\n              <Menu.Item as=\"a\">alice</Menu.Item>\n              <Menu.Item as=\"a\">bob</Menu.Item>\n              <Menu.Item as=\"a\">charlie</Menu.Item>\n            </Menu.Items>\n          </Menu>\n          <iframe srcDoc={'<button>Trigger</button>'} width=\"300\" height=\"300\"></iframe>\n        </div>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      // Click the input element in the iframe\n      await click(document.querySelector('iframe')?.contentDocument!.querySelector('button')!)\n\n      // Should be closed now\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  )\n\n  it(\n    'should be possible to hover an item and make it active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n      // We should be able to go to the second item\n      await mouseMove(items[1])\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go to the first item\n      await mouseMove(items[0])\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last item\n      await mouseMove(items[2])\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n  )\n\n  it(\n    'should make a menu item active when you move the mouse over it',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n      // We should be able to go to the second item\n      await mouseMove(items[1])\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the menu item is already active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // We should be able to go to the second item\n      await mouseMove(items[1])\n      assertMenuLinkedWithMenuItem(items[1])\n\n      await mouseMove(items[1])\n\n      // Nothing should be changed\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the menu item is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      await mouseMove(items[1])\n      assertNoActiveMenuItem()\n    })\n  )\n\n  it(\n    'should not be possible to hover an item that is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // Try to hover over item 1, which is disabled\n      await mouseMove(items[1])\n\n      // We should not have an active item now\n      assertNoActiveMenuItem()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave an item and make it inactive',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // We should be able to go to the second item\n      await mouseMove(items[1])\n      assertMenuLinkedWithMenuItem(items[1])\n\n      await mouseLeave(items[1])\n      assertNoActiveMenuItem()\n\n      // We should be able to go to the first item\n      await mouseMove(items[0])\n      assertMenuLinkedWithMenuItem(items[0])\n\n      await mouseLeave(items[0])\n      assertNoActiveMenuItem()\n\n      // We should be able to go to the last item\n      await mouseMove(items[2])\n      assertMenuLinkedWithMenuItem(items[2])\n\n      await mouseLeave(items[2])\n      assertNoActiveMenuItem()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave a disabled item and be a no-op',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // Try to hover over item 1, which is disabled\n      await mouseMove(items[1])\n      assertNoActiveMenuItem()\n\n      await mouseLeave(items[1])\n      assertNoActiveMenuItem()\n    })\n  )\n\n  it(\n    'should be possible to click a menu item, which closes the menu',\n    suppressConsoleLogs(async () => {\n      let clickHandler = jest.fn()\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" onClick={clickHandler}>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      let items = getMenuItems()\n\n      // We should be able to click the first item\n      await click(items[1])\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n      expect(clickHandler).toHaveBeenCalled()\n    })\n  )\n\n  it(\n    'should be possible to click a menu item, which closes the menu and invokes the @click handler',\n    suppressConsoleLogs(async () => {\n      let clickHandler = jest.fn()\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"button\" onClick={clickHandler}>\n              bob\n            </Menu.Item>\n            <Menu.Item>\n              <button onClick={clickHandler}>charlie</button>\n            </Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      // We should be able to click the first item\n      await click(getMenuItems()[1])\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the callback has been called\n      expect(clickHandler).toHaveBeenCalledTimes(1)\n\n      // Let's re-open the window for now\n      await click(getMenuButton())\n\n      // Click the last item, which should close and invoke the handler\n      await click(getMenuItems()[2])\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the callback has been called\n      expect(clickHandler).toHaveBeenCalledTimes(2)\n    })\n  )\n\n  it(\n    'should be possible to click a disabled menu item, which is a no-op',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      let items = getMenuItems()\n\n      // We should be able to click the first item\n      await click(items[1])\n      assertMenu({ state: MenuState.Visible })\n    })\n  )\n\n  it(\n    'should be possible focus a menu item, so that it becomes active',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\">bob</Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      let items = getMenuItems()\n\n      // Verify that nothing is active yet\n      assertNoActiveMenuItem()\n\n      // We should be able to focus the first item\n      await focus(items[1])\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n  )\n\n  it(\n    'should not be possible to focus a menu item which is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\">alice</Menu.Item>\n            <Menu.Item as=\"a\" disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item as=\"a\">charlie</Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      let items = getMenuItems()\n\n      // We should not be able to focus the first item\n      await focus(items[1])\n      assertNoActiveMenuItem()\n    })\n  )\n\n  it(\n    'should not be possible to activate a disabled item',\n    suppressConsoleLogs(async () => {\n      let clickHandler = jest.fn()\n\n      render(\n        <Menu>\n          <Menu.Button>Trigger</Menu.Button>\n          <Menu.Items>\n            <Menu.Item as=\"a\" onClick={clickHandler}>\n              alice\n            </Menu.Item>\n            <Menu.Item as=\"a\" onClick={clickHandler} disabled>\n              bob\n            </Menu.Item>\n            <Menu.Item disabled>\n              <button onClick={clickHandler}>charlie</button>\n            </Menu.Item>\n          </Menu.Items>\n        </Menu>\n      )\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      let items = getMenuItems()\n\n      await focus(items[0])\n      await click(items[1])\n      expect(clickHandler).not.toHaveBeenCalled()\n\n      // Activate the last item\n      await click(getMenuItems()[2])\n      expect(clickHandler).not.toHaveBeenCalled()\n    })\n  )\n})\n\ndescribe('transitions', () => {\n  it(\n    'should be possible to close the Menu when using the `transition` prop',\n    suppressConsoleLogs(async () => {\n      render(\n        <Menu>\n          <MenuButton>Toggle</MenuButton>\n          <MenuItems transition>\n            <MenuItem as=\"a\">Alice</MenuItem>\n            <MenuItem as=\"a\">Bob</MenuItem>\n            <MenuItem as=\"a\">Charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      )\n\n      // Focus the button\n      await focus(getMenuButton())\n\n      // Ensure the button is focused\n      assertActiveElement(getMenuButton())\n\n      // Open the menu\n      await click(getMenuButton())\n\n      // Ensure the menu is visible\n      assertMenu({ state: MenuState.Visible })\n\n      // Close the menu\n      await click(getMenuButton())\n\n      // Wait for the transition to finish, and the menu to close\n      await waitFor(() => {\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n      })\n\n      // Ensure the button got the restored focus\n      assertActiveElement(getMenuButton())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu/menu.tsx",
    "content": "'use client'\n\n// WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  Fragment,\n  useCallback,\n  useEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type ElementType,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type Ref,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useElementSize } from '../../hooks/use-element-size'\nimport { useEvent } from '../../hooks/use-event'\nimport { useHandleToggle } from '../../hooks/use-handle-toggle'\nimport { useId } from '../../hooks/use-id'\nimport { useInertOthers } from '../../hooks/use-inert-others'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useOnDisappear } from '../../hooks/use-on-disappear'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport { Action as QuickReleaseAction, useQuickRelease } from '../../hooks/use-quick-release'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useScrollLock } from '../../hooks/use-scroll-lock'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useTextValue } from '../../hooks/use-text-value'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { useTreeWalker } from '../../hooks/use-tree-walker'\nimport {\n  FloatingProvider,\n  useFloatingPanel,\n  useFloatingPanelProps,\n  useFloatingReference,\n  useFloatingReferenceProps,\n  useResolvedAnchor,\n  type AnchorProps,\n} from '../../internal/floating'\nimport { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'\nimport { stackMachines } from '../../machines/stack-machine'\nimport { useSlice } from '../../react-glue'\nimport type { Props } from '../../types'\nimport { Focus } from '../../utils/calculate-active-index'\nimport { disposables } from '../../utils/disposables'\nimport * as DOM from '../../utils/dom'\nimport {\n  Focus as FocusManagementFocus,\n  FocusableMode,\n  focusFrom,\n  isFocusableElement,\n  restoreFocusIfNecessary,\n} from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { isActiveElement } from '../../utils/owner'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescriptions } from '../description/description'\nimport { Keys } from '../keyboard'\nimport { useLabelContext, useLabels } from '../label/label'\nimport { Portal } from '../portal/portal'\nimport { ActionTypes, ActivationTrigger, MenuState, type MenuItemDataRef } from './menu-machine'\nimport { MenuContext, useMenuMachine, useMenuMachineContext } from './menu-machine-glue'\n\nlet DEFAULT_MENU_TAG = Fragment\ntype MenuRenderPropArg = {\n  open: boolean\n  close: () => void\n}\ntype MenuPropsWeControl = never\n\nexport type MenuProps<TTag extends ElementType = typeof DEFAULT_MENU_TAG> = Props<\n  TTag,\n  MenuRenderPropArg,\n  MenuPropsWeControl,\n  {\n    __demoMode?: boolean\n  }\n>\n\nfunction MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(\n  props: MenuProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let id = useId()\n\n  let { __demoMode = false, ...theirProps } = props\n  let machine = useMenuMachine({ id, __demoMode })\n\n  let [menuState, itemsElement, buttonElement] = useSlice(machine, (state) => [\n    state.menuState,\n    state.itemsElement,\n    state.buttonElement,\n  ])\n  let menuRef = useSyncRefs(ref)\n\n  let stackMachine = stackMachines.get(null)\n  let isTopLayer = useSlice(\n    stackMachine,\n    useCallback((state) => stackMachine.selectors.isTop(state, id), [stackMachine, id])\n  )\n\n  useOutsideClick(isTopLayer, [buttonElement, itemsElement], (event, target) => {\n    machine.send({ type: ActionTypes.CloseMenu })\n\n    if (!isFocusableElement(target, FocusableMode.Loose)) {\n      event.preventDefault()\n      machine.state.buttonElement?.focus()\n    }\n  })\n\n  let close = useEvent(() => {\n    machine.send({ type: ActionTypes.CloseMenu })\n  })\n\n  let slot = useSlot<MenuRenderPropArg>({ open: menuState === MenuState.Open, close })\n\n  let ourProps = { ref: menuRef }\n\n  let render = useRender()\n\n  return (\n    <FloatingProvider>\n      <MenuContext.Provider value={machine}>\n        <OpenClosedProvider\n          value={match(menuState, {\n            [MenuState.Open]: State.Open,\n            [MenuState.Closed]: State.Closed,\n          })}\n        >\n          {render({\n            ourProps,\n            theirProps,\n            slot,\n            defaultTag: DEFAULT_MENU_TAG,\n            name: 'Menu',\n          })}\n        </OpenClosedProvider>\n      </MenuContext.Provider>\n    </FloatingProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\ntype ButtonRenderPropArg = {\n  open: boolean\n  active: boolean\n  hover: boolean\n  focus: boolean\n  disabled: boolean\n  autofocus: boolean\n}\ntype ButtonPropsWeControl = 'aria-controls' | 'aria-expanded' | 'aria-haspopup'\n\nexport type MenuButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    disabled?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: MenuButtonProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let machine = useMenuMachineContext('Menu.Button')\n  let internalId = useId()\n  let {\n    id = `headlessui-menu-button-${internalId}`,\n    disabled = false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let internalButtonRef = useRef<HTMLButtonElement | null>(null)\n  let getFloatingReferenceProps = useFloatingReferenceProps()\n  let buttonRef = useSyncRefs(\n    ref,\n    internalButtonRef,\n    useFloatingReference(),\n    useEvent((element) => machine.send({ type: ActionTypes.SetButtonElement, element }))\n  )\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/#keyboard-interaction-13\n\n      case Keys.Space:\n      case Keys.Enter:\n      case Keys.ArrowDown:\n        event.preventDefault()\n        event.stopPropagation()\n        machine.send({ type: ActionTypes.OpenMenu, focus: { focus: Focus.First } })\n        break\n\n      case Keys.ArrowUp:\n        event.preventDefault()\n        event.stopPropagation()\n        machine.send({ type: ActionTypes.OpenMenu, focus: { focus: Focus.Last } })\n        break\n    }\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Space:\n        // Required for firefox, event.preventDefault() in handleKeyDown for\n        // the Space key doesn't cancel the handleKeyUp, which in turn\n        // triggers a *click*.\n        event.preventDefault()\n        break\n    }\n  })\n\n  let [menuState, buttonElement, itemsElement] = useSlice(machine, (state) => [\n    state.menuState,\n    state.buttonElement,\n    state.itemsElement,\n  ])\n\n  let enableQuickRelease = menuState === MenuState.Open\n  useQuickRelease(enableQuickRelease, {\n    trigger: buttonElement,\n    action: useCallback(\n      (e) => {\n        if (buttonElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        let item = e.target.closest('[role=\"menuitem\"]:not([data-disabled])')\n        if (DOM.isHTMLElement(item)) {\n          return QuickReleaseAction.Select(item)\n        }\n\n        if (itemsElement?.contains(e.target)) {\n          return QuickReleaseAction.Ignore\n        }\n\n        return QuickReleaseAction.Close\n      },\n      [buttonElement, itemsElement]\n    ),\n    close: useCallback(() => machine.send({ type: ActionTypes.CloseMenu }), []),\n    select: useCallback((target) => target.click(), []),\n  })\n\n  let toggleProps = useHandleToggle((event) => {\n    if (disabled) return\n    if (menuState === MenuState.Open) {\n      flushSync(() => machine.send({ type: ActionTypes.CloseMenu }))\n      internalButtonRef.current?.focus({ preventScroll: true })\n    } else {\n      event.preventDefault()\n      machine.send({\n        type: ActionTypes.OpenMenu,\n        focus: { focus: Focus.Nothing },\n        trigger: ActivationTrigger.Pointer,\n      })\n    }\n  })\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<ButtonRenderPropArg>({\n    open: menuState === MenuState.Open,\n    active: active || menuState === MenuState.Open,\n    disabled,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let ourProps = mergeProps(\n    getFloatingReferenceProps(),\n    {\n      ref: buttonRef,\n      id,\n      type: useResolveButtonType(props, internalButtonRef.current),\n      'aria-haspopup': 'menu',\n      'aria-controls': itemsElement?.id,\n      'aria-expanded': menuState === MenuState.Open,\n      disabled: disabled || undefined,\n      autoFocus,\n      onKeyDown: handleKeyDown,\n      onKeyUp: handleKeyUp,\n    },\n    toggleProps,\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BUTTON_TAG,\n    name: 'Menu.Button',\n  })\n}\n\n// ---\n\nlet DEFAULT_ITEMS_TAG = 'div' as const\ntype ItemsRenderPropArg = {\n  open: boolean\n}\ntype ItemsPropsWeControl = 'aria-activedescendant' | 'aria-labelledby' | 'role' | 'tabIndex'\n\nlet ItemsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type MenuItemsProps<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG> = Props<\n  TTag,\n  ItemsRenderPropArg,\n  ItemsPropsWeControl,\n  {\n    anchor?: AnchorProps\n    portal?: boolean\n    modal?: boolean\n    transition?: boolean\n\n    // ItemsRenderFeatures\n    static?: boolean\n    unmount?: boolean\n  }\n>\n\nfunction ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(\n  props: MenuItemsProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-menu-items-${internalId}`,\n    anchor: rawAnchor,\n    portal = false,\n    modal = true,\n    transition = false,\n    ...theirProps\n  } = props\n  let anchor = useResolvedAnchor(rawAnchor)\n  let machine = useMenuMachineContext('Menu.Items')\n  let [floatingRef, style] = useFloatingPanel(anchor)\n  let getFloatingPanelProps = useFloatingPanelProps()\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localItemsElement, setLocalItemsElement] = useState<HTMLElement | null>(null)\n\n  let itemsRef = useSyncRefs(\n    ref,\n    anchor ? floatingRef : null,\n    useEvent((element) => machine.send({ type: ActionTypes.SetItemsElement, element })),\n    setLocalItemsElement\n  )\n\n  let [menuState, buttonElement] = useSlice(machine, (state) => [\n    state.menuState,\n    state.buttonElement,\n  ])\n\n  let portalOwnerDocument = useOwnerDocument(buttonElement)\n  let ownerDocument = useOwnerDocument(localItemsElement)\n\n  // Always enable `portal` functionality, when `anchor` is enabled\n  if (anchor) {\n    portal = true\n  }\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localItemsElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : menuState === MenuState.Open\n  )\n\n  // Ensure we close the menu as soon as the button becomes hidden\n  useOnDisappear(visible, buttonElement, () => {\n    machine.send({ type: ActionTypes.CloseMenu })\n  })\n\n  // Enable scroll locking when the menu is visible, and `modal` is enabled\n  let __demoMode = useSlice(machine, (state) => state.__demoMode)\n  let scrollLockEnabled = __demoMode ? false : modal && menuState === MenuState.Open\n  useScrollLock(scrollLockEnabled, ownerDocument)\n\n  // Mark other elements as inert when the menu is visible, and `modal` is enabled\n  let inertOthersEnabled = __demoMode ? false : modal && menuState === MenuState.Open\n  useInertOthers(inertOthersEnabled, {\n    allowed: useCallback(\n      () => [buttonElement, localItemsElement],\n      [buttonElement, localItemsElement]\n    ),\n  })\n\n  // We keep track whether the button moved or not, we only check this when the\n  // menu state becomes closed. If the button moved, then we want to cancel\n  // pending transitions to prevent that the attached `MenuItems` is still\n  // transitioning while the button visually moved away.\n  //\n  // If we don't cancel these transitions then there will be a period where the\n  // `MenuItems` is visible and moving around because it is trying to\n  // re-position itself based on the new position.\n  let didButtonMove = useSlice(machine, machine.selectors.didButtonMove)\n\n  // Now that we know that the button did move or not, we can either disable the\n  // panel and all of its transitions, or rely on the `visible` state to hide\n  // the panel whenever necessary.\n  let panelEnabled = didButtonMove ? false : visible\n\n  useEffect(() => {\n    let container = localItemsElement\n    if (!container) return\n    if (menuState !== MenuState.Open) return\n    if (isActiveElement(container)) return\n\n    container.focus({ preventScroll: true })\n  }, [menuState, localItemsElement])\n\n  useTreeWalker(menuState === MenuState.Open, {\n    container: localItemsElement,\n    accept(node) {\n      if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT\n      if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP\n      return NodeFilter.FILTER_ACCEPT\n    },\n    walk(node) {\n      node.setAttribute('role', 'none')\n    },\n  })\n\n  let searchDisposables = useDisposables()\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLElement>) => {\n    searchDisposables.dispose()\n\n    switch (event.key) {\n      // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n      // @ts-expect-error Fallthrough is expected here\n      case Keys.Space:\n        if (machine.state.searchQuery !== '') {\n          event.preventDefault()\n          event.stopPropagation()\n          return machine.send({ type: ActionTypes.Search, value: event.key })\n        }\n      // When in type ahead mode, fallthrough\n      case Keys.Enter:\n        event.preventDefault()\n        event.stopPropagation()\n        if (machine.state.activeItemIndex !== null) {\n          let { dataRef } = machine.state.items[machine.state.activeItemIndex]\n          dataRef.current?.domRef.current?.click()\n        }\n        machine.send({ type: ActionTypes.CloseMenu })\n        restoreFocusIfNecessary(machine.state.buttonElement)\n        break\n\n      case Keys.ArrowDown:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.send({ type: ActionTypes.GoToItem, focus: Focus.Next })\n\n      case Keys.ArrowUp:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.send({ type: ActionTypes.GoToItem, focus: Focus.Previous })\n\n      case Keys.Home:\n      case Keys.PageUp:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.send({ type: ActionTypes.GoToItem, focus: Focus.First })\n\n      case Keys.End:\n      case Keys.PageDown:\n        event.preventDefault()\n        event.stopPropagation()\n        return machine.send({ type: ActionTypes.GoToItem, focus: Focus.Last })\n\n      case Keys.Escape:\n        event.preventDefault()\n        event.stopPropagation()\n        flushSync(() => machine.send({ type: ActionTypes.CloseMenu }))\n        machine.state.buttonElement?.focus({ preventScroll: true })\n        break\n\n      case Keys.Tab:\n        event.preventDefault()\n        event.stopPropagation()\n        flushSync(() => machine.send({ type: ActionTypes.CloseMenu }))\n        focusFrom(\n          machine.state.buttonElement!,\n          event.shiftKey ? FocusManagementFocus.Previous : FocusManagementFocus.Next\n        )\n        break\n\n      default:\n        if (event.key.length === 1) {\n          machine.send({ type: ActionTypes.Search, value: event.key })\n          searchDisposables.setTimeout(() => machine.send({ type: ActionTypes.ClearSearch }), 350)\n        }\n        break\n    }\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Space:\n        // Required for firefox, event.preventDefault() in handleKeyDown for\n        // the Space key doesn't cancel the handleKeyUp, which in turn\n        // triggers a *click*.\n        event.preventDefault()\n        break\n    }\n  })\n\n  let slot = useSlot<ItemsRenderPropArg>({\n    open: menuState === MenuState.Open,\n  })\n\n  let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {\n    'aria-activedescendant': useSlice(machine, machine.selectors.activeDescendantId),\n    'aria-labelledby': useSlice(machine, (state) => state.buttonElement?.id),\n    id,\n    onKeyDown: handleKeyDown,\n    onKeyUp: handleKeyUp,\n    role: 'menu',\n    // When the `Menu` is closed, it should not be focusable. This allows us\n    // to skip focusing the `MenuItems` when pressing the tab key on an\n    // open `Menu`, and go to the next focusable element.\n    tabIndex: menuState === MenuState.Open ? 0 : undefined,\n    ref: itemsRef,\n    style: {\n      ...theirProps.style,\n      ...style,\n      '--button-width': useElementSize(visible, buttonElement, true).width,\n    } as CSSProperties,\n    ...transitionDataAttributes(transitionData),\n  })\n\n  let render = useRender()\n\n  return (\n    <Portal enabled={portal ? props.static || visible : false} ownerDocument={portalOwnerDocument}>\n      {render({\n        ourProps,\n        theirProps,\n        slot,\n        defaultTag: DEFAULT_ITEMS_TAG,\n        features: ItemsRenderFeatures,\n        visible: panelEnabled,\n        name: 'Menu.Items',\n      })}\n    </Portal>\n  )\n}\n\n// ---\n\nlet DEFAULT_ITEM_TAG = Fragment\ntype ItemRenderPropArg = {\n  /** @deprecated use `focus` instead */\n  active: boolean\n  focus: boolean\n  disabled: boolean\n  close: () => void\n}\ntype ItemPropsWeControl =\n  | 'aria-describedby'\n  | 'aria-disabled'\n  | 'aria-labelledby'\n  | 'role'\n  | 'tabIndex'\n\nexport type MenuItemProps<TTag extends ElementType = typeof DEFAULT_ITEM_TAG> = Props<\n  TTag,\n  ItemRenderPropArg,\n  ItemPropsWeControl,\n  {\n    disabled?: boolean\n  }\n>\n\nfunction ItemFn<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(\n  props: MenuItemProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let { id = `headlessui-menu-item-${internalId}`, disabled = false, ...theirProps } = props\n  let machine = useMenuMachineContext('Menu.Item')\n\n  let active = useSlice(machine, (state) => machine.selectors.isActive(state, id))\n\n  let internalItemRef = useRef<HTMLElement | null>(null)\n  let itemRef = useSyncRefs(ref, internalItemRef)\n\n  let shouldScrollIntoView = useSlice(machine, (state) =>\n    machine.selectors.shouldScrollIntoView(state, id)\n  )\n  useIsoMorphicEffect(() => {\n    if (!shouldScrollIntoView) return\n    return disposables().requestAnimationFrame(() => {\n      internalItemRef.current?.scrollIntoView?.({ block: 'nearest' })\n    })\n  }, [shouldScrollIntoView, internalItemRef])\n\n  let getTextValue = useTextValue(internalItemRef)\n\n  let bag = useRef<MenuItemDataRef['current']>({\n    disabled,\n    domRef: internalItemRef,\n    get textValue() {\n      return getTextValue()\n    },\n  })\n\n  useIsoMorphicEffect(() => {\n    bag.current.disabled = disabled\n  }, [bag, disabled])\n\n  useIsoMorphicEffect(() => {\n    machine.actions.registerItem(id, bag)\n    return () => machine.actions.unregisterItem(id)\n  }, [bag, id])\n\n  let close = useEvent(() => {\n    machine.send({ type: ActionTypes.CloseMenu })\n  })\n\n  let handleClick = useEvent((event: MouseEvent) => {\n    if (disabled) return event.preventDefault()\n    machine.send({ type: ActionTypes.CloseMenu })\n    restoreFocusIfNecessary(machine.state.buttonElement)\n  })\n\n  let handleFocus = useEvent(() => {\n    if (disabled) return machine.send({ type: ActionTypes.GoToItem, focus: Focus.Nothing })\n    machine.send({ type: ActionTypes.GoToItem, focus: Focus.Specific, id })\n  })\n\n  let pointer = useTrackedPointer()\n\n  let handleEnter = useEvent((event) => pointer.update(event))\n\n  let handleMove = useEvent((event) => {\n    if (!pointer.wasMoved(event)) return\n    if (disabled) return\n    if (active) return\n\n    // pointermove / mousemove will only be fired when the pointer is actually\n    // moving, therefore we can go to the option with the `Pointer` activation\n    // trigger.\n    machine.send({\n      type: ActionTypes.GoToItem,\n      focus: Focus.Specific,\n      id,\n      trigger: ActivationTrigger.Pointer,\n    })\n  })\n\n  let handleLeave = useEvent((event) => {\n    if (!pointer.wasMoved(event)) return\n    if (disabled) return\n    if (!active) return\n\n    // pointerenter / mouseenter will be fired when the mouse is on top of an\n    // element that scrolls into view even when using the keyboard to\n    // navigate. Only handle the event when the pointer was actually moved.\n    if (machine.state.activationTrigger !== ActivationTrigger.Pointer) return\n\n    machine.send({ type: ActionTypes.GoToItem, focus: Focus.Nothing })\n  })\n\n  let [labelledby, LabelProvider] = useLabels()\n  let [describedby, DescriptionProvider] = useDescriptions()\n\n  let slot = useSlot<ItemRenderPropArg>({ active, focus: active, disabled, close })\n  let ourProps = {\n    id,\n    ref: itemRef,\n    role: 'menuitem',\n    tabIndex: disabled === true ? undefined : -1,\n    'aria-disabled': disabled === true ? true : undefined,\n    'aria-labelledby': labelledby,\n    'aria-describedby': describedby,\n    disabled: undefined, // Never forward the `disabled` prop\n    onClick: handleClick,\n    onFocus: handleFocus,\n    onPointerEnter: handleEnter,\n    onMouseEnter: handleEnter,\n    onPointerMove: handleMove,\n    onMouseMove: handleMove,\n    onPointerLeave: handleLeave,\n    onMouseLeave: handleLeave,\n  }\n\n  let render = useRender()\n\n  return (\n    <LabelProvider>\n      <DescriptionProvider>\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_ITEM_TAG,\n          name: 'Menu.Item',\n        })}\n      </DescriptionProvider>\n    </LabelProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_SECTION_TAG = 'div' as const\ntype SectionRenderPropArg = {}\ntype SectionPropsWeControl = 'role' | 'aria-labelledby'\n\nexport type MenuSectionProps<TTag extends ElementType = typeof DEFAULT_SECTION_TAG> = Props<\n  TTag,\n  SectionRenderPropArg,\n  SectionPropsWeControl\n>\n\nfunction SectionFn<TTag extends ElementType = typeof DEFAULT_SECTION_TAG>(\n  props: MenuSectionProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let [labelledby, LabelProvider] = useLabels()\n\n  let theirProps = props\n  let ourProps = { ref, 'aria-labelledby': labelledby, role: 'group' }\n\n  let render = useRender()\n\n  return (\n    <LabelProvider>\n      {render({\n        ourProps,\n        theirProps,\n        slot: {},\n        defaultTag: DEFAULT_SECTION_TAG,\n        name: 'Menu.Section',\n      })}\n    </LabelProvider>\n  )\n}\n\n// --\n\nlet DEFAULT_HEADING_TAG = 'header' as const\ntype HeadingRenderPropArg = {}\ntype HeadingPropsWeControl = 'role'\n\nexport type MenuHeadingProps<TTag extends ElementType = typeof DEFAULT_HEADING_TAG> = Props<\n  TTag,\n  HeadingRenderPropArg,\n  HeadingPropsWeControl\n>\n\nfunction HeadingFn<TTag extends ElementType = typeof DEFAULT_HEADING_TAG>(\n  props: MenuHeadingProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let { id = `headlessui-menu-heading-${internalId}`, ...theirProps } = props\n\n  let context = useLabelContext()\n  useIsoMorphicEffect(() => context.register(id), [id, context.register])\n\n  let ourProps = { id, ref, role: 'presentation', ...context.props }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot: {},\n    defaultTag: DEFAULT_HEADING_TAG,\n    name: 'Menu.Heading',\n  })\n}\n\n// ---\n\nlet DEFAULT_SEPARATOR_TAG = 'div' as const\ntype SeparatorRenderPropArg = {}\ntype SeparatorPropsWeControl = 'role'\n\nexport type MenuSeparatorProps<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG> = Props<\n  TTag,\n  SeparatorRenderPropArg,\n  SeparatorPropsWeControl\n>\n\nfunction SeparatorFn<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG>(\n  props: MenuSeparatorProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let theirProps = props\n  let ourProps = { ref, role: 'separator' }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot: {},\n    defaultTag: DEFAULT_SEPARATOR_TAG,\n    name: 'Menu.Separator',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentMenu extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_MENU_TAG>(\n    props: MenuProps<TTag> & RefProp<typeof MenuFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: MenuButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuItems extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(\n    props: MenuItemsProps<TTag> & RefProp<typeof ItemsFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuItem extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(\n    props: MenuItemProps<TTag> & RefProp<typeof ItemFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuSection extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_SECTION_TAG>(\n    props: MenuSectionProps<TTag> & RefProp<typeof SectionFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuHeading extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_HEADING_TAG>(\n    props: MenuHeadingProps<TTag> & RefProp<typeof HeadingFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentMenuSeparator extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG>(\n    props: MenuSeparatorProps<TTag> & RefProp<typeof SeparatorFn>\n  ): React.JSX.Element\n}\n\nlet MenuRoot = forwardRefWithAs(MenuFn) as _internal_ComponentMenu\nexport let MenuButton = forwardRefWithAs(ButtonFn) as _internal_ComponentMenuButton\nexport let MenuItems = forwardRefWithAs(ItemsFn) as _internal_ComponentMenuItems\nexport let MenuItem = forwardRefWithAs(ItemFn) as _internal_ComponentMenuItem\nexport let MenuSection = forwardRefWithAs(SectionFn) as _internal_ComponentMenuSection\nexport let MenuHeading = forwardRefWithAs(HeadingFn) as _internal_ComponentMenuHeading\nexport let MenuSeparator = forwardRefWithAs(SeparatorFn) as _internal_ComponentMenuSeparator\n\nexport let Menu = Object.assign(MenuRoot, {\n  /** @deprecated use `<MenuButton>` instead of `<Menu.Button>` */\n  Button: MenuButton,\n  /** @deprecated use `<MenuItems>` instead of `<Menu.Items>` */\n  Items: MenuItems,\n  /** @deprecated use `<MenuItem>` instead of `<Menu.Item>` */\n  Item: MenuItem,\n  /** @deprecated use `<MenuSection>` instead of `<Menu.Section>` */\n  Section: MenuSection,\n  /** @deprecated use `<MenuHeading>` instead of `<Menu.Heading>` */\n  Heading: MenuHeading,\n  /** @deprecated use `<MenuSeparator>` instead of `<Menu.Separator>` */\n  Separator: MenuSeparator,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-button/menu-button.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuButton } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-heading/menu-heading.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuHeading } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-item/menu-item.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuItem } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-items/menu-items.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuItems } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-section/menu-section.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuSection } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/menu-separator/menu-separator.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../menu/menu'\nexport { MenuSeparator } from '../menu/menu'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/mouse.ts",
    "content": "export enum MouseButton {\n  Left = 0,\n  Right = 2,\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover/popover-machine-glue.tsx",
    "content": "import { createContext, useContext, useMemo } from 'react'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { PopoverMachine } from './popover-machine'\n\nexport const PopoverContext = createContext<PopoverMachine | null>(null)\nexport function usePopoverMachineContext(component: string) {\n  let context = useContext(PopoverContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Popover /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, usePopoverMachineContext)\n    throw err\n  }\n  return context\n}\n\nexport function usePopoverMachine({\n  id,\n  __demoMode = false,\n}: {\n  id: string\n  __demoMode?: boolean\n}) {\n  let machine = useMemo(() => PopoverMachine.new({ id, __demoMode }), [])\n  useOnUnmount(() => machine.dispose())\n  return machine\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover/popover-machine.ts",
    "content": "import { type MouseEventHandler } from 'react'\nimport { Machine } from '../../machine'\nimport { stackMachines } from '../../machines/stack-machine'\nimport * as DOM from '../../utils/dom'\nimport { getFocusableElements } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { getOwnerDocument } from '../../utils/owner'\n\ntype MouseEvent<T> = Parameters<MouseEventHandler<T>>[0]\n\nexport enum PopoverStates {\n  Open,\n  Closed,\n}\n\ninterface State {\n  id: string\n\n  popoverState: PopoverStates\n\n  buttons: { current: Symbol[] }\n\n  button: HTMLElement | null\n  buttonId: string | null\n  panel: HTMLElement | null\n  panelId: string | null\n\n  beforePanelSentinel: { current: HTMLButtonElement | null }\n  afterPanelSentinel: { current: HTMLButtonElement | null }\n  afterButtonSentinel: { current: HTMLButtonElement | null }\n\n  __demoMode: boolean\n}\n\nexport enum ActionTypes {\n  OpenPopover,\n  ClosePopover,\n\n  SetButton,\n  SetButtonId,\n  SetPanel,\n  SetPanelId,\n}\n\nexport type Actions =\n  | { type: ActionTypes.OpenPopover }\n  | { type: ActionTypes.ClosePopover }\n  | { type: ActionTypes.SetButton; button: HTMLElement | null }\n  | { type: ActionTypes.SetButtonId; buttonId: string | null }\n  | { type: ActionTypes.SetPanel; panel: HTMLElement | null }\n  | { type: ActionTypes.SetPanelId; panelId: string | null }\n\nlet reducers: {\n  [P in ActionTypes]: (state: State, action: Extract<Actions, { type: P }>) => State\n} = {\n  [ActionTypes.OpenPopover]: (state) => {\n    if (state.popoverState === PopoverStates.Open) return state\n    return { ...state, popoverState: PopoverStates.Open, __demoMode: false }\n  },\n  [ActionTypes.ClosePopover](state) {\n    if (state.popoverState === PopoverStates.Closed) return state\n    return { ...state, popoverState: PopoverStates.Closed, __demoMode: false }\n  },\n  [ActionTypes.SetButton](state, action) {\n    if (state.button === action.button) return state\n    return { ...state, button: action.button }\n  },\n  [ActionTypes.SetButtonId](state, action) {\n    if (state.buttonId === action.buttonId) return state\n    return { ...state, buttonId: action.buttonId }\n  },\n  [ActionTypes.SetPanel](state, action) {\n    if (state.panel === action.panel) return state\n    return { ...state, panel: action.panel }\n  },\n  [ActionTypes.SetPanelId](state, action) {\n    if (state.panelId === action.panelId) return state\n    return { ...state, panelId: action.panelId }\n  },\n}\n\nexport class PopoverMachine extends Machine<State, Actions> {\n  static new({ id, __demoMode = false }: { id: string; __demoMode?: boolean }) {\n    return new PopoverMachine({\n      id,\n      __demoMode,\n      popoverState: __demoMode ? PopoverStates.Open : PopoverStates.Closed,\n      buttons: { current: [] },\n      button: null,\n      buttonId: null,\n      panel: null,\n      panelId: null,\n      beforePanelSentinel: { current: null },\n      afterPanelSentinel: { current: null },\n      afterButtonSentinel: { current: null },\n    })\n  }\n\n  constructor(initialState: State) {\n    super(initialState)\n\n    {\n      let id = this.state.id\n      let stackMachine = stackMachines.get(null)\n\n      this.on(ActionTypes.OpenPopover, () => stackMachine.actions.push(id))\n      this.on(ActionTypes.ClosePopover, () => stackMachine.actions.pop(id))\n    }\n  }\n\n  reduce(state: Readonly<State>, action: Actions): State {\n    return match(action.type, reducers, state, action)\n  }\n\n  actions = {\n    close: () => this.send({ type: ActionTypes.ClosePopover }),\n    refocusableClose: (\n      focusableElement?: HTMLElement | { current: HTMLElement | null } | MouseEvent<HTMLElement>\n    ) => {\n      this.actions.close()\n\n      let restoreElement = (() => {\n        if (!focusableElement) return this.state.button\n        if (DOM.isHTMLElement(focusableElement)) return focusableElement\n        if ('current' in focusableElement && DOM.isHTMLElement(focusableElement.current)) {\n          return focusableElement.current\n        }\n\n        return this.state.button\n      })()\n\n      restoreElement?.focus()\n    },\n    open: () => this.send({ type: ActionTypes.OpenPopover }),\n    setButtonId: (id: string | null) => this.send({ type: ActionTypes.SetButtonId, buttonId: id }),\n    setButton: (button: HTMLElement | null) => this.send({ type: ActionTypes.SetButton, button }),\n    setPanelId: (id: string | null) => this.send({ type: ActionTypes.SetPanelId, panelId: id }),\n    setPanel: (panel: HTMLElement | null) => this.send({ type: ActionTypes.SetPanel, panel }),\n  }\n\n  selectors = {\n    isPortalled: (state: State) => {\n      if (!state.button) return false\n      if (!state.panel) return false\n\n      let ownerDocument = getOwnerDocument(state.button) ?? document\n\n      // We are part of a different \"root\" tree, so therefore we can consider it portalled. This is a\n      // heuristic because 3rd party tools could use some form of portal, typically rendered at the\n      // end of the body but we don't have an actual reference to that.\n      for (let root of ownerDocument.querySelectorAll('body > *')) {\n        if (Number(root?.contains(state.button)) ^ Number(root?.contains(state.panel))) {\n          return true\n        }\n      }\n\n      // Use another heuristic to try and calculate whether or not the focusable\n      // elements are near each other (aka, following the default focus/tab order\n      // from the browser). If they are then it doesn't really matter if they are\n      // portalled or not because we can follow the default tab order. But if they\n      // are not, then we can consider it being portalled so that we can ensure\n      // that tab and shift+tab (hopefully) go to the correct spot.\n      let elements = getFocusableElements(ownerDocument)\n      let buttonIdx = elements.indexOf(state.button)\n\n      let beforeIdx = (buttonIdx + elements.length - 1) % elements.length\n      let afterIdx = (buttonIdx + 1) % elements.length\n\n      let beforeElement = elements[beforeIdx]\n      let afterElement = elements[afterIdx]\n\n      if (!state.panel.contains(beforeElement) && !state.panel.contains(afterElement)) {\n        return true\n      }\n\n      // It may or may not be portalled, but we don't really know.\n      return false\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover/popover.test.tsx",
    "content": "import { render, waitFor } from '@testing-library/react'\nimport React, { Fragment, act, createElement, useEffect, useRef, useState } from 'react'\nimport ReactDOM from 'react-dom'\nimport {\n  PopoverState,\n  assertActiveElement,\n  assertContainsActiveElement,\n  assertPopoverButton,\n  assertPopoverPanel,\n  getByText,\n  getPopoverButton,\n  getPopoverOverlay,\n  getPopoverPanel,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, MouseButton, click, focus, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Portal } from '../portal/portal'\nimport { Transition } from '../transition/transition'\nimport { Popover, PopoverButton, PopoverPanel } from './popover'\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['Popover.Button', Popover.Button],\n    ['Popover.Panel', Popover.Panel],\n    ['Popover.Backdrop', Popover.Backdrop],\n    ['Popover.Backdrop', Popover.Overlay],\n  ])(\n    'should error when we are using a <%s /> without a parent <Popover />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component as any))).toThrow(\n        `<${name} /> is missing a parent <Popover /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Popover without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Popover.Group', () => {\n    it(\n      'should be possible to render a Popover.Group with multiple Popover components',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>Panel 1</Popover.Panel>\n            </Popover>\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getByText('Panel 1'))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getByText('Panel 2'))\n\n        await click(getByText('Trigger 1'))\n\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        assertPopoverPanel({ state: PopoverState.Visible }, getByText('Panel 1'))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getByText('Panel 2'))\n\n        await click(getByText('Trigger 2'))\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getByText('Panel 1'))\n        assertPopoverPanel({ state: PopoverState.Visible }, getByText('Panel 2'))\n      })\n    )\n  })\n\n  describe('Popover', () => {\n    it(\n      'should be possible to render a Popover using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            {({ open }) => (\n              <>\n                <Popover.Button>Trigger</Popover.Button>\n                <Popover.Panel>Panel is: {open ? 'open' : 'closed'}</Popover.Panel>\n              </>\n            )}\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible, textContent: 'Panel is: open' })\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            {({ close }) => (\n              <>\n                <Popover.Button>Trigger</Popover.Button>\n                <Popover.Panel>\n                  <button onClick={() => close()}>Close me</button>\n                </Popover.Panel>\n              </>\n            )}\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the Popover.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button id=\"test\">restorable</button>\n            <Popover>\n              {({ close }) => (\n                <>\n                  <Popover.Button>Trigger</Popover.Button>\n                  <Popover.Panel>\n                    <button onClick={() => close(document.getElementById('test')!)}>\n                      Close me\n                    </button>\n                  </Popover.Panel>\n                </>\n              )}\n            </Popover>\n          </>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a ref',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let elementRef = useRef(null)\n          return (\n            <>\n              <button ref={elementRef}>restorable</button>\n              <Popover>\n                {({ close }) => (\n                  <>\n                    <Popover.Button>Trigger</Popover.Button>\n                    <Popover.Panel>\n                      <button onClick={() => close(elementRef)}>Close me</button>\n                    </Popover.Panel>\n                  </>\n                )}\n              </Popover>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and takes an event',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          return (\n            <>\n              <Popover>\n                {({ close }) => (\n                  <>\n                    <Popover.Button>Trigger</Popover.Button>\n                    <Popover.Panel>\n                      <button onClick={close}>Close me</button>\n                    </Popover.Panel>\n                  </>\n                )}\n              </Popover>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the Popover.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    describe('refs', () => {\n      it(\n        'should be possible to get a ref to the Popover',\n        suppressConsoleLogs(async () => {\n          let popoverRef = { current: null }\n\n          render(\n            <Popover as=\"div\" ref={popoverRef}>\n              <Popover.Button>Trigger</Popover.Button>\n              <Popover.Panel>Popover</Popover.Panel>\n            </Popover>\n          )\n\n          expect(popoverRef.current).not.toBeNull()\n        })\n      )\n\n      it(\n        'should be possible to use a Fragment with an optional ref',\n        suppressConsoleLogs(async () => {\n          render(\n            <Popover as={Fragment}>\n              <Popover.Button>Trigger</Popover.Button>\n              <Popover.Panel>Popover</Popover.Panel>\n            </Popover>\n          )\n\n          // It should not throw\n        })\n      )\n    })\n  })\n\n  describe('Popover.Button', () => {\n    it(\n      'should be possible to render a Popover.Button using a fragment',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button as={Fragment}>\n              <button>Trigger</button>\n            </Popover.Button>\n            <Popover.Panel></Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n    it(\n      'should be possible to render a Popover.Button using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>{(slot) => <>{JSON.stringify(slot)}</>}</Popover.Button>\n            <Popover.Panel></Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a Popover.Button using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button as=\"div\" role=\"button\">\n              {(slot) => <>{JSON.stringify(slot)}</>}\n            </Popover.Button>\n            <Popover.Panel />\n          </Popover>\n        )\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          textContent: JSON.stringify({\n            open: false,\n            active: false,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          textContent: JSON.stringify({\n            open: true,\n            active: true,\n            disabled: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          }),\n        })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n          </Popover>\n        )\n\n        expect(getPopoverButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        render(\n          <Popover>\n            <Popover.Button type=\"submit\">Trigger</Popover.Button>\n          </Popover>\n        )\n\n        expect(getPopoverButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n        let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n          <button ref={ref} {...props} />\n        ))\n\n        render(\n          <Popover>\n            <Popover.Button as={CustomButton}>Trigger</Popover.Button>\n          </Popover>\n        )\n\n        expect(getPopoverButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        render(\n          <Popover>\n            <Popover.Button as=\"div\">Trigger</Popover.Button>\n          </Popover>\n        )\n\n        expect(getPopoverButton()).not.toHaveAttribute('type')\n      })\n\n      it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n        let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n          <div ref={ref} {...props} />\n        ))\n\n        render(\n          <Popover>\n            <Popover.Button as={CustomButton}>Trigger</Popover.Button>\n          </Popover>\n        )\n\n        expect(getPopoverButton()).not.toHaveAttribute('type')\n      })\n    })\n  })\n\n  describe('Popover.Panel', () => {\n    it(\n      'should be possible to render Popover.Panel using a render prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>{(slot) => <>{JSON.stringify(slot)}</>}</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n\n    it('should be possible to always render the Popover.Panel if we provide it a `static` prop', () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel static>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      // Let's verify that the Popover is already there\n      expect(getPopoverPanel()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the Popover.Panel', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel unmount={false}>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      await focus(getPopoverButton())\n\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n      assertPopoverPanel({ state: PopoverState.InvisibleHidden })\n\n      // Let's open the Popover, to see if it is not hidden anymore\n      await click(getPopoverButton())\n\n      assertPopoverButton({ state: PopoverState.Visible })\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Let's re-click the Popover, to see if it is hidden again\n      await click(getPopoverButton())\n\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n      assertPopoverPanel({ state: PopoverState.InvisibleHidden })\n    })\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (very first link)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel focus>\n              <a href=\"/\">Link 1</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n      })\n    )\n\n    it(\n      'should close the Popover, when Popover.Panel has the focus prop and you focus the open button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel focus>\n              <a href=\"/\">Link 1</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n\n        // Focus the button again\n        await focus(getPopoverButton())\n\n        // Ensure the Popover is closed again\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (skip hidden link)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel focus>\n              <a href=\"/\" style={{ display: 'none' }}>\n                Link 1\n              </a>\n              <a href=\"/\">Link 2</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (very first link) when the hidden render strategy is used',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel focus unmount={false}>\n              <a href=\"/\">Link 1</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>\n              {({ close }) => <button onClick={() => close()}>Close me</button>}\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the Popover.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button id=\"test\">restorable</button>\n            <Popover>\n              <Popover.Button>Trigger</Popover.Button>\n              <Popover.Panel>\n                {({ close }) => (\n                  <button onClick={() => close(document.getElementById('test')!)}>Close me</button>\n                )}\n              </Popover.Panel>\n            </Popover>\n          </>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a ref',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let elementRef = useRef(null)\n          return (\n            <>\n              <button ref={elementRef}>restorable</button>\n              <Popover>\n                <Popover.Button>Trigger</Popover.Button>\n                <Popover.Panel>\n                  {({ close }) => <button onClick={() => close(elementRef)}>Close me</button>}\n                </Popover.Panel>\n              </Popover>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should be possible to use the `anchor` prop on the `PopoverPanel`',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel anchor=\"bottom\">Panel open</PopoverPanel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n  })\n\n  describe('Multiple `Popover.Button` warnings', () => {\n    let spy = jest.spyOn(console, 'warn').mockImplementation(() => {})\n    beforeEach(() => {\n      spy.mockRestore()\n      spy = jest.spyOn(console, 'warn').mockImplementation(() => {})\n    })\n    afterEach(() => {\n      spy.mockRestore()\n    })\n\n    it('should warn when you are using multiple `Popover.Button` components', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Popover.Button>Button #2</Popover.Button>\n          <Popover.Panel>Popover panel</Popover.Panel>\n        </Popover>\n      )\n\n      // Open Popover\n      await click(getPopoverButton())\n\n      expect(spy).toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n\n    it('should warn when you are using multiple `Popover.Button` components (wrapped in a Transition)', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Popover.Button>Button #2</Popover.Button>\n          <Transition>\n            <Popover.Panel>Popover panel</Popover.Panel>\n          </Transition>\n        </Popover>\n      )\n\n      // Open Popover\n      await act(() => click(getPopoverButton()))\n\n      expect(spy).toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n\n    it('should not warn when you are using multiple `Popover.Button` components inside the `Popover.Panel`', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Popover.Panel>\n            <Popover.Button>Close #1</Popover.Button>\n            <Popover.Button>Close #2</Popover.Button>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open Popover\n      await click(getPopoverButton())\n\n      expect(spy).not.toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n\n    it('should not warn when you are using multiple `Popover.Button` components inside the `Popover.Panel` (wrapped in a Transition)', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Transition>\n            <Popover.Panel>\n              <Popover.Button>Close #1</Popover.Button>\n              <Popover.Button>Close #2</Popover.Button>\n            </Popover.Panel>\n          </Transition>\n        </Popover>\n      )\n\n      // Open Popover\n      await act(() => click(getPopoverButton()))\n\n      expect(spy).not.toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n\n    it('should warn when you are using multiple `Popover.Button` components in a nested `Popover`', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Popover.Panel>\n            Popover panel #1\n            <Popover>\n              <Popover.Button>Button #2</Popover.Button>\n              <Popover.Button>Button #3</Popover.Button>\n              <Popover.Panel>Popover panel #2</Popover.Panel>\n            </Popover>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the first Popover\n      await click(getByText('Button #1'))\n\n      // Open the second Popover\n      await click(getByText('Button #2'))\n\n      expect(spy).toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n\n    it('should not warn when you are using multiple `Popover.Button` components in a nested `Popover.Panel`', async () => {\n      render(\n        <Popover>\n          <Popover.Button>Button #1</Popover.Button>\n          <Popover.Panel>\n            Popover panel #1\n            <Popover>\n              <Popover.Button>Button #2</Popover.Button>\n              <Popover.Panel>\n                <Popover.Button>Button #3</Popover.Button>\n                <Popover.Button>Button #4</Popover.Button>\n              </Popover.Panel>\n            </Popover>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the first Popover\n      await click(getByText('Button #1'))\n\n      // Open the second Popover\n      await click(getByText('Button #2'))\n\n      expect(spy).not.toHaveBeenCalledWith(\n        'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n      )\n    })\n  })\n})\n\ndescribe('Composition', () => {\n  function Debug({ fn, name }: { fn: (text: string) => void; name: string }) {\n    useEffect(() => {\n      fn(`Mounting - ${name}`)\n      return () => {\n        fn(`Unmounting - ${name}`)\n      }\n    }, [fn, name])\n    return null\n  }\n\n  it(\n    'should be possible to wrap the Popover.Panel with a Transition component',\n    suppressConsoleLogs(async () => {\n      let orderFn = jest.fn()\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Debug name=\"Popover\" fn={orderFn} />\n          <Transition>\n            <Debug name=\"Transition\" fn={orderFn} />\n            <Popover.Panel>\n              <Transition.Child as=\"div\">\n                <Debug name=\"Transition.Child\" fn={orderFn} />\n              </Transition.Child>\n            </Popover.Panel>\n          </Transition>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Wait for all transitions to finish\n      await nextFrame()\n      await nextFrame()\n\n      // Verify that we tracked the `mounts` and `unmounts` in the correct order\n      expect(orderFn.mock.calls).toEqual([\n        ['Mounting - Popover'],\n        ['Mounting - Transition'],\n        ['Mounting - Transition.Child'],\n        ['Unmounting - Transition'],\n        ['Unmounting - Transition.Child'],\n      ])\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the Popover with Enter',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Open popover\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Close popover\n        await press(Keys.Enter)\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to open the popover with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button disabled>Trigger</Popover.Button>\n            <Popover.Panel>Content</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Try to open the popover\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the popover with Enter when the popover is open',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Open popover\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Close popover\n        await press(Keys.Enter)\n\n        // Verify it is closed again\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close other popover menus when we open a new one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>Panel 1</Popover.Panel>\n            </Popover>\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Open the first Popover\n        await click(getByText('Trigger 1'))\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Focus trigger 2\n        getByText('Trigger 2')?.focus()\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Open the second popover\n        await press(Keys.Enter)\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Enter` on a Popover.Button inside a Popover.Panel',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Open</Popover.Button>\n            <Popover.Panel>\n              <Popover.Button>Close</Popover.Button>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeBtn = getByText('Close')\n\n        expect(closeBtn).not.toHaveAttribute('id')\n        expect(closeBtn).not.toHaveAttribute('aria-controls')\n        expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Enter, closeBtn)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it(\n      'should close the Popover menu, when pressing escape on the Popover.Button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify button is (still) focused\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should close the Popover menu, when pressing escape on the Popover.Panel',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>\n              <a href=\"/\">Link</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Tab to next focusable item\n        await press(Keys.Tab)\n\n        // Verify the active element is inside the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify button is focused again\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should be possible to close a sibling Popover when pressing escape on a sibling Popover.Button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>Panel 1</Popover.Panel>\n            </Popover>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        assertPopoverPanel({ state: PopoverState.Visible }, getByText('Panel 1'))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getByText('Panel 2'))\n\n        // Focus the button of the second popover menu\n        getByText('Trigger 2')?.focus()\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify both popovers are closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Verify the button of the second popover is still focused\n        assertActiveElement(getByText('Trigger 2'))\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to Tab through the panel contents and end up in the Button again (without PopoverGroup)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel portal>\n              <a href=\"/\">Link 1</a>\n              <a href=\"/\">Link 2</a>\n            </PopoverPanel>\n          </Popover>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab again\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the button is focused again\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should be possible to Tab through the panel contents onto the next Popover.Button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab again\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should be possible to place a focusable item in the Popover.Group, and keep the Popover open when we focus the focusable element',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n\n            <a href=\"/\">Link in between</a>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab to the in between link\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the in between link is focused\n        assertActiveElement(getByText('Link in between'))\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the Popover.Group',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Popover.Group>\n              <Popover>\n                <Popover.Button>Trigger 1</Popover.Button>\n                <Popover.Panel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </Popover.Panel>\n              </Popover>\n\n              <Popover>\n                <Popover.Button>Trigger 2</Popover.Button>\n                <Popover.Panel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </Popover.Panel>\n              </Popover>\n            </Popover.Group>\n\n            <a href=\"/\">Next</a>\n          </>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab again\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Let's Tab out of the Popover.Group\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the Popover',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n\n            <a href=\"/\">Next</a>\n          </>\n        )\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab out of the Popover\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of a Popover without focusable elements',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>No focusable elements here</Popover.Panel>\n            </Popover>\n\n            <a href=\"/\">Next</a>\n          </>\n        )\n\n        // Focus the button of the Popover\n        await focus(getPopoverButton())\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Let's Tab out of the Popover\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover when the Popover.Panel has a focus prop',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <Popover.Button>Trigger</Popover.Button>\n              <Popover.Panel focus>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n            <a href=\"/\">Next</a>\n          </>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // Tab out of the component\n        await press(Keys.Tab) // Tab to link 1\n        await press(Keys.Tab) // Tab out\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Next link outside of the popover\n        assertActiveElement(getByText('Next'))\n      })\n    )\n\n    it(\n      'should close the Popover when the Popover.Panel has a focus prop (Popover.Panel uses a Portal)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <Popover.Button>Trigger</Popover.Button>\n              <Portal>\n                <Popover.Panel focus>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </Popover.Panel>\n              </Portal>\n            </Popover>\n            <a href=\"/\">Next</a>\n          </>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // The focus should be on the first link\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab to the next link\n        await press(Keys.Tab)\n\n        // The focus should be on the second link\n        assertActiveElement(getByText('Link 2'))\n\n        // Tab out of the component\n        await press(Keys.Tab)\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Next link outside of the popover\n        assertActiveElement(getByText('Next'))\n      })\n    )\n\n    it(\n      'should close the Popover when the Popover.Panel has a focus prop (Popover.Panel uses a Portal), and focus the next focusable item in line',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <Popover.Button>Trigger</Popover.Button>\n              <Portal>\n                <Popover.Panel focus>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </Popover.Panel>\n              </Portal>\n            </Popover>\n          </>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // The focus should be on the first link\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab to the next link\n        await press(Keys.Tab)\n\n        // The focus should be on the second link\n        assertActiveElement(getByText('Link 2'))\n\n        // Tab out of the component\n        await press(Keys.Tab)\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Previous link outside of the popover, this is the next one in line\n        assertActiveElement(getByText('Previous'))\n      })\n    )\n  })\n\n  describe('`Shift+Tab` key', () => {\n    it(\n      'should close the Popover menu once we Tab out of the Popover.Group',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n\n            <Popover.Group>\n              <Popover>\n                <Popover.Button>Trigger 1</Popover.Button>\n                <Popover.Panel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </Popover.Panel>\n              </Popover>\n\n              <Popover>\n                <Popover.Button>Trigger 2</Popover.Button>\n                <Popover.Panel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </Popover.Panel>\n              </Popover>\n            </Popover.Group>\n          </>\n        )\n\n        // Focus the button of the second Popover\n        getByText('Trigger 2')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 2'))\n\n        // Verify we can tab to Trigger 1\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Trigger 1'))\n\n        // Let's Tab out of the Popover.Group\n        await press(shift(Keys.Tab))\n\n        // Verify the previous link is now focused\n        assertActiveElement(getByText('Previous'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the Popover',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <a href=\"/\">Previous</a>\n\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n          </>\n        )\n\n        // Focus the button of the Popover\n        await focus(getPopoverButton())\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Let's Tab out of the Popover\n        await press(shift(Keys.Tab))\n\n        // Verify the previous link is now focused\n        assertActiveElement(getByText('Previous'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the previous Popover.Button when Shift+Tab on the second Popover.Button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 3</a>\n                <a href=\"/\">Link 4</a>\n              </Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Open the second popover\n        await click(getByText('Trigger 2'))\n        getByText('Trigger 2')?.focus()\n\n        // Ensure the second popover is open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n\n        // Close the popover\n        await press(Keys.Escape)\n\n        // Ensure the popover is now closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Ensure the second Popover.Button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Tab backwards\n        await press(shift(Keys.Tab))\n\n        // Ensure the first Popover.Button is open\n        assertActiveElement(getByText('Trigger 1'))\n      })\n    )\n\n    it(\n      'should focus the Popover.Button when pressing Shift+Tab when we focus inside the Popover.Panel',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger 1</Popover.Button>\n            <Popover.Panel focus>\n              <a href=\"/\">Link 1</a>\n              <a href=\"/\">Link 2</a>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the Popover.Button is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the Popover.Button when pressing Shift+Tab when we focus inside the Popover.Panel (inside a Portal)',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger 1</Popover.Button>\n            <Portal>\n              <Popover.Panel focus>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Portal>\n          </Popover>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the Popover.Button is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the Popover.Button when pressing Shift+Tab when we focus inside the Popover.Panel (heuristic based portal)',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [portal, setPortal] = useState<HTMLElement | null>(null)\n\n          return (\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              {portal &&\n                ReactDOM.createPortal(\n                  <Popover.Panel focus>\n                    <a href=\"/\">Link 1</a>\n                    <a href=\"/\">Link 2</a>\n                  </Popover.Panel>,\n                  portal\n                )}\n              <button>Before</button>\n              <div ref={setPortal} />\n              <button>After</button>\n            </Popover>\n          )\n        }\n\n        render(<Example />)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the Popover.Button is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to focus the last item in the Popover.Panel when pressing Shift+Tab on the next Popover.Button',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </Popover.Panel>\n            </Popover>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>\n                <a href=\"/\">Link 3</a>\n                <a href=\"/\">Link 4</a>\n              </Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Open the popover\n        await click(getByText('Trigger 1'))\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Focus the second button\n        getByText('Trigger 2')?.focus()\n\n        // Verify the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Ensure the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Press shift+tab, to move focus to the last item in the Popover.Panel\n        await press(shift(Keys.Tab), getByText('Trigger 2'))\n\n        // Verify we are focusing the last link of the first Popover\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n\n    it(\n      \"should be possible to focus the last item in the Popover.Panel when pressing Shift+Tab on the next Popover.Button (using Portal's)\",\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Portal>\n                <Popover.Panel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </Popover.Panel>\n              </Portal>\n            </Popover>\n\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Portal>\n                <Popover.Panel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </Popover.Panel>\n              </Portal>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Open the popover\n        await click(getByText('Trigger 1'))\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Focus the second button\n        getByText('Trigger 2')?.focus()\n\n        // Verify the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Ensure the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Press shift+tab, to move focus to the last item in the Popover.Panel\n        await press(shift(Keys.Tab), getByText('Trigger 2'))\n\n        // Verify we are focusing the last link of the first Popover\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the popover with Space',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Open popover\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n\n    it(\n      'should not be possible to open the popover with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button disabled>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Try to open the popover\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the popover with Space when the popover is open',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n        )\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        await focus(getPopoverButton())\n\n        // Open popover\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Close popover\n        await press(Keys.Space)\n\n        // Verify it is closed again\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close other popover menus when we open a new one',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover.Group>\n            <Popover>\n              <Popover.Button>Trigger 1</Popover.Button>\n              <Popover.Panel>Panel 1</Popover.Panel>\n            </Popover>\n            <Popover>\n              <Popover.Button>Trigger 2</Popover.Button>\n              <Popover.Panel>Panel 2</Popover.Panel>\n            </Popover>\n          </Popover.Group>\n        )\n\n        // Open the first Popover\n        await click(getByText('Trigger 1'))\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Focus trigger 2\n        getByText('Trigger 2')?.focus()\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Open the second popover\n        await press(Keys.Space)\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Space` on a Popover.Button inside a Popover.Panel',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Open</Popover.Button>\n            <Popover.Panel>\n              <Popover.Button>Close</Popover.Button>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeBtn = getByText('Close')\n\n        expect(closeBtn).not.toHaveAttribute('id')\n        expect(closeBtn).not.toHaveAttribute('aria-controls')\n        expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Space, closeBtn)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Enter` on a Popover.Button and go to the href of the `a` inside a Popover.Panel',\n      suppressConsoleLogs(async () => {\n        render(\n          <Popover>\n            <Popover.Button>Open</Popover.Button>\n            <Popover.Panel>\n              <Popover.Button as={React.Fragment}>\n                <a href=\"#closed\">Close</a>\n              </Popover.Button>\n            </Popover.Panel>\n          </Popover>\n        )\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeLink = getByText('Close')\n\n        expect(closeLink).not.toHaveAttribute('id')\n        expect(closeLink).not.toHaveAttribute('aria-controls')\n        expect(closeLink).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Enter, closeLink)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n\n        // Verify that we got redirected to the href\n        expect(document.location.hash).toEqual('#closed')\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to open a popover on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n      assertPopoverPanel({ state: PopoverState.Visible })\n    })\n  )\n\n  it(\n    'should not be possible to open a popover on right click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Open popover\n      await click(getPopoverButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open a popover on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button disabled>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Try to open the popover\n      await click(getPopoverButton())\n\n      // Verify it is still closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a popover on click',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      await focus(getPopoverButton())\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click to close\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a Popover using a click on the Popover.Overlay',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n          <Popover.Overlay />\n        </Popover>\n      )\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the overlay to close\n      await click(getPopoverOverlay())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close the popover, and re-focus the button when we click outside on the body element',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Trigger</Popover.Button>\n          <Popover.Panel>Contents</Popover.Panel>\n        </Popover>\n      )\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the body to close\n      await click(document.body)\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the popover, and re-focus the button when we click outside on a non-focusable element',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n\n          <span>I am just text</span>\n        </>\n      )\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the span to close\n      await click(getByText('I am just text'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the popover, by clicking outside the popover on another focusable element',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <Popover>\n            <Popover.Button>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n\n          <button>Different button</button>\n        </>\n      )\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the extra button to close\n      await click(getByText('Different button'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the other button is focused\n      assertActiveElement(getByText('Different button'))\n    })\n  )\n\n  it(\n    'should be possible to close the popover, by clicking outside the popover on another element inside a focusable element',\n    suppressConsoleLogs(async () => {\n      let focusFn = jest.fn()\n      render(\n        <>\n          <Popover>\n            <Popover.Button onFocus={focusFn}>Trigger</Popover.Button>\n            <Popover.Panel>Contents</Popover.Panel>\n          </Popover>\n\n          <button id=\"btn\">\n            <span>Different button</span>\n          </button>\n        </>\n      )\n\n      // Open popover\n      await click(getPopoverButton())\n      getPopoverButton()?.focus()\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the span inside the extra button to close\n      await click(getByText('Different button'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the other button is focused\n      assertActiveElement(document.getElementById('btn'))\n\n      // Ensure that the focus button only got focus once (first click)\n      expect(focusFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on a Popover.Button inside a Popover.Panel',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Open</Popover.Button>\n          <Popover.Panel>\n            <Popover.Button>Close</Popover.Button>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      let closeBtn = getByText('Close')\n\n      expect(closeBtn).not.toHaveAttribute('id')\n      expect(closeBtn).not.toHaveAttribute('aria-controls')\n      expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n      // The close button should close the popover\n      await click(closeBtn)\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify we restored the Open button\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should not close the Popover when clicking on a focusable element inside a static Popover.Panel',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n\n      render(\n        <Popover>\n          <Popover.Button>Open</Popover.Button>\n          <Popover.Panel static>\n            <button onClick={clickFn}>btn</button>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The button should not close the popover\n      await click(getByText('btn'))\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Verify we actually clicked the button\n      expect(clickFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should not close the Popover when clicking on a non-focusable element inside a static Popover.Panel',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Open</Popover.Button>\n          <Popover.Panel static>\n            <span>element</span>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The element should not close the popover\n      await click(getByText('element'))\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.Visible })\n    })\n  )\n\n  it(\n    'should close the Popover when clicking outside of a static Popover.Panel',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Open</Popover.Button>\n          <Popover.Panel static>\n            <span>element</span>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The element should close the popover\n      await click(document.body)\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on the Popover.Button outside the Popover.Panel',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Toggle</Popover.Button>\n          <Popover.Panel>\n            <button>Contents</button>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on the Popover.Button outside the Popover.Panel (when using the `focus` prop)',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <Popover.Button>Toggle</Popover.Button>\n          <Popover.Panel focus>\n            <button>Contents</button>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using Portals',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n      render(\n        <Popover>\n          <Popover.Button>Toggle</Popover.Button>\n          <Popover.Panel>\n            <Portal>\n              <button onClick={clickFn}>foo</button>\n            </Portal>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Click the button outside the Popover (DOM) but inside (Portal / React tree)\n      await click(getByText('foo'))\n\n      // Verify it is still open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Verify the button was clicked\n      expect(clickFn).toHaveBeenCalled()\n    })\n  )\n\n  it(\n    'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using nested Portals',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n      render(\n        <Popover>\n          <Popover.Button>Toggle</Popover.Button>\n          <Popover.Panel>\n            Level 0\n            <Portal>\n              Level 1\n              <Portal>\n                Level 2\n                <Portal>\n                  Level 3\n                  <Portal>\n                    Level 4<button onClick={clickFn}>foo</button>\n                  </Portal>\n                </Portal>\n              </Portal>\n            </Portal>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Click the button outside the Popover (DOM) but inside (Portal / React tree)\n      await click(getByText('foo'))\n\n      // Verify it is still open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Verify the button was clicked\n      expect(clickFn).toHaveBeenCalled()\n    })\n  )\n})\n\ndescribe('Nested popovers', () => {\n  it(\n    'should be possible to nest Popover components and control them individually',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover data-testid=\"popover-a\">\n          <Popover.Button>Toggle A</Popover.Button>\n          <Popover.Panel>\n            <span>Contents A</span>\n            <Popover data-testid=\"popover-b\">\n              <Popover.Button>Toggle B</Popover.Button>\n              <Popover.Panel>\n                <span>Contents B</span>\n              </Popover.Panel>\n            </Popover>\n          </Popover.Panel>\n        </Popover>\n      )\n\n      // Verify that Popover B is not there yet\n      expect(document.querySelector('[data-testid=\"popover-b\"]')).toBeNull()\n\n      // Open Popover A\n      await click(getByText('Toggle A'))\n\n      // Ensure Popover A is visible\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-a\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n\n      // Ensure Popover B is visible\n      assertPopoverPanel(\n        { state: PopoverState.InvisibleUnmounted },\n        document.querySelector(\n          '[data-testid=\"popover-b\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n\n      // Open Popover B\n      await click(getByText('Toggle B'))\n\n      // Ensure both popovers are open\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-a\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-b\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n    })\n  )\n})\n\ndescribe('transitions', () => {\n  it(\n    'should be possible to close the Popover when using the `transition` prop',\n    suppressConsoleLogs(async () => {\n      render(\n        <Popover>\n          <PopoverButton>Toggle</PopoverButton>\n          <PopoverPanel transition>Contents</PopoverPanel>\n        </Popover>\n      )\n\n      // Focus the button\n      await focus(getPopoverButton())\n\n      // Ensure the button is focused\n      assertActiveElement(getPopoverButton())\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Ensure the popover is visible\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Wait for the transition to finish, and the popover to close\n      await waitFor(() => {\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n\n      // Ensure the button got the restored focus\n      assertActiveElement(getPopoverButton())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover/popover.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ContextType,\n  type ElementType,\n  type MouseEventHandler,\n  type MutableRefObject,\n  type FocusEvent as ReactFocusEvent,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useElementSize } from '../../hooks/use-element-size'\nimport { useEvent } from '../../hooks/use-event'\nimport { useEventListener } from '../../hooks/use-event-listener'\nimport { useId } from '../../hooks/use-id'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useOnDisappear } from '../../hooks/use-on-disappear'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useOwnerDocument, useRootDocument } from '../../hooks/use-owner'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport {\n  MainTreeProvider,\n  useMainTreeNode,\n  useRootContainers,\n} from '../../hooks/use-root-containers'\nimport { useScrollLock } from '../../hooks/use-scroll-lock'\nimport { useSlot } from '../../hooks/use-slot'\nimport { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'\nimport { Direction as TabDirection, useTabDirection } from '../../hooks/use-tab-direction'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { CloseProvider } from '../../internal/close-provider'\nimport {\n  FloatingProvider,\n  useFloatingPanel,\n  useFloatingPanelProps,\n  useFloatingReference,\n  useResolvedAnchor,\n  type AnchorProps,\n} from '../../internal/floating'\nimport { Hidden, HiddenFeatures } from '../../internal/hidden'\nimport {\n  OpenClosedProvider,\n  ResetOpenClosedProvider,\n  State,\n  useOpenClosed,\n} from '../../internal/open-closed'\nimport { useSlice } from '../../react-glue'\nimport type { Props } from '../../types'\nimport { isDisabledReactIssue7711 } from '../../utils/bugs'\nimport * as DOM from '../../utils/dom'\nimport {\n  Focus,\n  FocusResult,\n  FocusableMode,\n  focusIn,\n  getFocusableElements,\n  isFocusableElement,\n} from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { getActiveElement, getRootNode } from '../../utils/owner'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { Keys } from '../keyboard'\nimport { Portal, useNestedPortals } from '../portal/portal'\nimport { PopoverStates } from './popover-machine'\nimport { PopoverContext, usePopoverMachine, usePopoverMachineContext } from './popover-machine-glue'\n\ntype MouseEvent<T> = Parameters<MouseEventHandler<T>>[0]\n\nlet PopoverGroupContext = createContext<{\n  registerPopover: (registerBag: PopoverRegisterBag) => void\n  unregisterPopover: (registerBag: PopoverRegisterBag) => void\n  isFocusWithinPopoverGroup: () => boolean\n  closeOthers: (buttonId: string) => void\n} | null>(null)\nPopoverGroupContext.displayName = 'PopoverGroupContext'\n\nfunction usePopoverGroupContext() {\n  return useContext(PopoverGroupContext)\n}\n\nlet PopoverPanelContext = createContext<string | null>(null)\nPopoverPanelContext.displayName = 'PopoverPanelContext'\n\nfunction usePopoverPanelContext() {\n  return useContext(PopoverPanelContext)\n}\n\ninterface PopoverRegisterBag {\n  buttonId: MutableRefObject<string | null>\n  panelId: MutableRefObject<string | null>\n  close: () => void\n}\n\n// ---\n\nlet DEFAULT_POPOVER_TAG = 'div' as const\ntype PopoverRenderPropArg = {\n  open: boolean\n  close: (\n    focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null> | MouseEvent<HTMLElement>\n  ) => void\n}\ntype PopoverPropsWeControl = never\n\nexport type PopoverProps<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG> = Props<\n  TTag,\n  PopoverRenderPropArg,\n  PopoverPropsWeControl,\n  {\n    __demoMode?: boolean\n  }\n>\n\nfunction PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(\n  props: PopoverProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let id = useId()\n\n  let { __demoMode = false, ...theirProps } = props\n  let machine = usePopoverMachine({ id, __demoMode })\n\n  let internalPopoverRef = useRef<HTMLElement | null>(null)\n  let popoverRef = useSyncRefs(\n    ref,\n    optionalRef((ref) => {\n      internalPopoverRef.current = ref\n    })\n  )\n\n  let [popoverState, button, panel, buttonId, panelId] = useSlice(\n    machine,\n    useCallback((state) => {\n      return [state.popoverState, state.button, state.panel, state.buttonId, state.panelId] as const\n    }, [])\n  )\n\n  let rootDocument = useRootDocument(internalPopoverRef.current ?? button)\n\n  let buttonIdRef = useLatestValue(buttonId)\n  let panelIdRef = useLatestValue(panelId)\n\n  let registerBag = useMemo(\n    () => ({\n      buttonId: buttonIdRef,\n      panelId: panelIdRef,\n      close: machine.actions.close,\n    }),\n    [buttonIdRef, panelIdRef, machine]\n  )\n\n  let groupContext = usePopoverGroupContext()\n  let registerPopover = groupContext?.registerPopover\n  let isFocusWithinPopoverGroup = useEvent(() => {\n    let activeElement = getActiveElement(internalPopoverRef.current ?? button)\n\n    return (\n      groupContext?.isFocusWithinPopoverGroup() ??\n      (activeElement && (button?.contains(activeElement) || panel?.contains(activeElement)))\n    )\n  })\n\n  useEffect(() => registerPopover?.(registerBag), [registerPopover, registerBag])\n\n  let [portals, PortalWrapper] = useNestedPortals()\n  let mainTreeNode = useMainTreeNode(button)\n  let root = useRootContainers({\n    mainTreeNode,\n    portals,\n    defaultContainers: [\n      {\n        get current() {\n          return machine.state.button\n        },\n      },\n      {\n        get current() {\n          return machine.state.panel\n        },\n      },\n    ],\n  })\n\n  // Handle focus out\n  useEventListener(\n    rootDocument,\n    'focus',\n    (event) => {\n      if (event.target === window) return\n      if (!DOM.isHTMLorSVGElement(event.target)) return\n      if (machine.state.popoverState !== PopoverStates.Open) return\n      if (isFocusWithinPopoverGroup()) return\n      if (!machine.state.button) return\n      if (!machine.state.panel) return\n      if (root.contains(event.target)) return\n      if (machine.state.beforePanelSentinel.current?.contains?.(event.target)) return\n      if (machine.state.afterPanelSentinel.current?.contains?.(event.target)) return\n      if (machine.state.afterButtonSentinel.current?.contains?.(event.target)) return\n\n      machine.actions.close()\n    },\n    true\n  )\n\n  // Handle outside click\n  let outsideClickEnabled = popoverState === PopoverStates.Open\n  useOutsideClick(outsideClickEnabled, root.resolveContainers, (event, target) => {\n    machine.actions.close()\n\n    if (!isFocusableElement(target, FocusableMode.Loose)) {\n      event.preventDefault()\n      button?.focus()\n    }\n  })\n\n  let slot = useSlot<PopoverRenderPropArg>({\n    open: popoverState === PopoverStates.Open,\n    close: machine.actions.refocusableClose,\n  })\n\n  let openClosedState = useSlice(\n    machine,\n    useCallback((state) => {\n      return match(state.popoverState, {\n        [PopoverStates.Open]: State.Open,\n        [PopoverStates.Closed]: State.Closed,\n      })\n    }, [])\n  )\n\n  let ourProps = { ref: popoverRef }\n\n  let render = useRender()\n\n  return (\n    <MainTreeProvider node={mainTreeNode}>\n      <FloatingProvider>\n        <PopoverPanelContext.Provider value={null}>\n          <PopoverContext.Provider value={machine}>\n            <CloseProvider value={machine.actions.refocusableClose}>\n              <OpenClosedProvider value={openClosedState}>\n                <PortalWrapper>\n                  {render({\n                    ourProps,\n                    theirProps,\n                    slot,\n                    defaultTag: DEFAULT_POPOVER_TAG,\n                    name: 'Popover',\n                  })}\n                </PortalWrapper>\n              </OpenClosedProvider>\n            </CloseProvider>\n          </PopoverContext.Provider>\n        </PopoverPanelContext.Provider>\n      </FloatingProvider>\n    </MainTreeProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_BUTTON_TAG = 'button' as const\ntype ButtonRenderPropArg = {\n  open: boolean\n  active: boolean\n  hover: boolean\n  focus: boolean\n  disabled: boolean\n  autofocus: boolean\n}\ntype ButtonPropsWeControl = 'aria-controls' | 'aria-expanded'\n\nexport type PopoverButtonProps<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG> = Props<\n  TTag,\n  ButtonRenderPropArg,\n  ButtonPropsWeControl,\n  {\n    disabled?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n  props: PopoverButtonProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-popover-button-${internalId}`,\n    disabled = false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let machine = usePopoverMachineContext('Popover.Button')\n  let [popoverState, isPortalled, button, buttonId, panel, panelId, afterButtonSentinel] = useSlice(\n    machine,\n    useCallback((state) => {\n      return [\n        state.popoverState,\n        machine.selectors.isPortalled(state),\n        state.button,\n        state.buttonId,\n        state.panel,\n        state.panelId,\n        state.afterButtonSentinel,\n      ] as const\n    }, [])\n  )\n  let internalButtonRef = useRef<HTMLButtonElement | null>(null)\n\n  let sentinelId = `headlessui-focus-sentinel-${useId()}`\n\n  let groupContext = usePopoverGroupContext()\n  let closeOthers = groupContext?.closeOthers\n\n  let panelContext = usePopoverPanelContext()\n\n  // A button inside a panel will just have \"close\" functionality, no \"open\" functionality. However,\n  // if a `Popover.Button` is rendered inside a `Popover` which in turn is rendered inside a\n  // `Popover.Panel` (aka nested popovers), then we need to make sure that the button is able to\n  // open the nested popover.\n  //\n  // The `Popover` itself will also render a `PopoverPanelContext` but with a value of `null`. That\n  // way we don't need to keep track of _which_ `Popover.Panel` (if at all) we are in, we can just\n  // check if we are in a `Popover.Panel` or not since this will always point to the nearest one and\n  // won't pierce through `Popover` components themselves.\n  let isWithinPanel = panelContext !== null\n\n  useEffect(() => {\n    if (isWithinPanel) return\n    machine.actions.setButtonId(id)\n    return () => machine.actions.setButtonId(null)\n  }, [isWithinPanel, id, machine])\n\n  // This is a little bit different compared to the `id` we already have. The goal is to have a very\n  // unique identifier for this specific component. This can be achieved with the `id` from above.\n  //\n  // However, the difference is for React 17 and lower where the `useId` hook doesn't exist yet.\n  // There we will generate a unique ID based on a simple counter, but for SSR this will result in\n  // `undefined` first, later it is patched to be a unique ID. The problem is that this patching\n  // happens after the component is rendered and therefore there is a moment in time where multiple\n  // buttons have the exact same ID and the `state.buttons` would result in something like:\n  //\n  // ```js\n  // ['headlessui-popover-button-undefined', 'headlessui-popover-button-1']\n  // ```\n  //\n  // With this approach we guarantee that there is a unique value for each button.\n  let [uniqueIdentifier] = useState(() => Symbol())\n\n  let buttonRef = useSyncRefs(\n    internalButtonRef,\n    ref,\n    useFloatingReference(),\n    useEvent((button) => {\n      if (isWithinPanel) return\n      if (button) {\n        machine.state.buttons.current.push(uniqueIdentifier)\n      } else {\n        let idx = machine.state.buttons.current.indexOf(uniqueIdentifier)\n        if (idx !== -1) machine.state.buttons.current.splice(idx, 1)\n      }\n\n      if (machine.state.buttons.current.length > 1) {\n        console.warn(\n          'You are already using a <Popover.Button /> but only 1 <Popover.Button /> is supported.'\n        )\n      }\n\n      button && machine.actions.setButton(button)\n    })\n  )\n  let withinPanelButtonRef = useSyncRefs(internalButtonRef, ref)\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    if (isWithinPanel) {\n      if (machine.state.popoverState === PopoverStates.Closed) return\n      switch (event.key) {\n        case Keys.Space:\n        case Keys.Enter:\n          event.preventDefault() // Prevent triggering a *click* event\n          // @ts-expect-error\n          event.target.click?.()\n          machine.actions.close()\n          machine.state.button?.focus() // Re-focus the original opening Button\n          break\n      }\n    } else {\n      switch (event.key) {\n        case Keys.Space:\n        case Keys.Enter:\n          event.preventDefault() // Prevent triggering a *click* event\n          event.stopPropagation()\n          if (machine.state.popoverState === PopoverStates.Closed) {\n            closeOthers?.(machine.state.buttonId!)\n            machine.actions.open()\n          } else {\n            machine.actions.close()\n          }\n          break\n\n        case Keys.Escape:\n          if (machine.state.popoverState !== PopoverStates.Open) {\n            return closeOthers?.(machine.state.buttonId!)\n          }\n          if (!internalButtonRef.current) return\n          let activeElement = getActiveElement(internalButtonRef.current)\n          if (activeElement && !internalButtonRef.current.contains(activeElement)) {\n            return\n          }\n          event.preventDefault()\n          event.stopPropagation()\n          machine.actions.close()\n          break\n      }\n    }\n  })\n\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    if (isWithinPanel) return\n    if (event.key === Keys.Space) {\n      // Required for firefox, event.preventDefault() in handleKeyDown for\n      // the Space key doesn't cancel the handleKeyUp, which in turn\n      // triggers a *click*.\n      event.preventDefault()\n    }\n  })\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return\n    if (disabled) return\n    if (isWithinPanel) {\n      machine.actions.close()\n      machine.state.button?.focus() // Re-focus the original opening Button\n    } else {\n      event.preventDefault()\n      event.stopPropagation()\n      if (machine.state.popoverState === PopoverStates.Closed) {\n        closeOthers?.(machine.state.buttonId!)\n        machine.actions.open()\n      } else {\n        machine.actions.close()\n      }\n      machine.state.button?.focus()\n    }\n  })\n\n  let handleMouseDown = useEvent((event: ReactMouseEvent) => {\n    event.preventDefault()\n    event.stopPropagation()\n  })\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let visible = popoverState === PopoverStates.Open\n  let slot = useSlot<ButtonRenderPropArg>({\n    open: visible,\n    active: active || visible,\n    disabled,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let type = useResolveButtonType(props, button)\n  let ourProps = isWithinPanel\n    ? mergeProps(\n        {\n          ref: withinPanelButtonRef,\n          type,\n          onKeyDown: handleKeyDown,\n          onClick: handleClick,\n          disabled: disabled || undefined,\n          autoFocus,\n        },\n        focusProps,\n        hoverProps,\n        pressProps\n      )\n    : mergeProps(\n        {\n          ref: buttonRef,\n          id: buttonId,\n          type,\n          'aria-expanded': popoverState === PopoverStates.Open,\n          'aria-controls': panel ? panelId : undefined,\n          disabled: disabled || undefined,\n          autoFocus,\n          onKeyDown: handleKeyDown,\n          onKeyUp: handleKeyUp,\n          onClick: handleClick,\n          onMouseDown: handleMouseDown,\n        },\n        focusProps,\n        hoverProps,\n        pressProps\n      )\n\n  let direction = useTabDirection()\n  let handleFocus = useEvent(() => {\n    if (!DOM.isHTMLElement(machine.state.panel)) return\n    let el = machine.state.panel\n\n    function run() {\n      let result = match(direction.current, {\n        [TabDirection.Forwards]: () => focusIn(el, Focus.First),\n        [TabDirection.Backwards]: () => focusIn(el, Focus.Last),\n      })\n\n      if (result === FocusResult.Error) {\n        focusIn(\n          getFocusableElements(getRootNode(machine.state.button)).filter(\n            (el) => el.dataset.headlessuiFocusGuard !== 'true'\n          ),\n          match(direction.current, {\n            [TabDirection.Forwards]: Focus.Next,\n            [TabDirection.Backwards]: Focus.Previous,\n          }),\n          { relativeTo: machine.state.button }\n        )\n      }\n    }\n\n    // TODO: Cleanup once we are using real browser tests\n    if (process.env.NODE_ENV === 'test') {\n      microTask(run)\n    } else {\n      run()\n    }\n  })\n\n  let render = useRender()\n\n  return (\n    <>\n      {render({\n        ourProps,\n        theirProps,\n        slot,\n        defaultTag: DEFAULT_BUTTON_TAG,\n        name: 'Popover.Button',\n      })}\n      {visible && !isWithinPanel && isPortalled && (\n        <Hidden\n          id={sentinelId}\n          ref={afterButtonSentinel}\n          features={HiddenFeatures.Focusable}\n          data-headlessui-focus-guard\n          as=\"button\"\n          type=\"button\"\n          onFocus={handleFocus}\n        />\n      )}\n    </>\n  )\n}\n\n// ---\n\nlet DEFAULT_BACKDROP_TAG = 'div' as const\ntype BackdropRenderPropArg = {\n  open: boolean\n}\ntype BackdropPropsWeControl = 'aria-hidden'\n\nlet BackdropRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type PopoverBackdropProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> = Props<\n  TTag,\n  BackdropRenderPropArg,\n  BackdropPropsWeControl,\n  { transition?: boolean } & PropsForFeatures<typeof BackdropRenderFeatures>\n>\n\nexport type PopoverOverlayProps<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG> =\n  PopoverBackdropProps<TTag>\n\nfunction BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(\n  props: PopoverBackdropProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-popover-backdrop-${internalId}`,\n    transition = false,\n    ...theirProps\n  } = props\n  let machine = usePopoverMachineContext('Popover.Backdrop')\n  let popoverState = useSlice(\n    machine,\n    useCallback((state) => state.popoverState, [])\n  )\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localBackdropElement, setLocalBackdropElement] = useState<HTMLElement | null>(null)\n\n  let backdropRef = useSyncRefs(ref, setLocalBackdropElement)\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localBackdropElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : popoverState === PopoverStates.Open\n  )\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()\n    machine.actions.close()\n  })\n\n  let slot = useSlot<BackdropRenderPropArg>({\n    open: popoverState === PopoverStates.Open,\n  })\n\n  let ourProps = {\n    ref: backdropRef,\n    id,\n    'aria-hidden': true,\n    onClick: handleClick,\n    ...transitionDataAttributes(transitionData),\n  }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_BACKDROP_TAG,\n    features: BackdropRenderFeatures,\n    visible,\n    name: 'Popover.Backdrop',\n  })\n}\n\n// ---\n\nlet DEFAULT_PANEL_TAG = 'div' as const\ntype PanelRenderPropArg = {\n  open: boolean\n  close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void\n}\n\nlet PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\ntype PanelPropsWeControl = 'tabIndex'\n\nexport type PopoverPanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = Props<\n  TTag,\n  PanelRenderPropArg,\n  PanelPropsWeControl,\n  {\n    focus?: boolean\n    anchor?: AnchorProps\n    portal?: boolean\n    modal?: boolean\n    transition?: boolean\n\n    // ItemsRenderFeatures\n    static?: boolean\n    unmount?: boolean\n  }\n>\n\nfunction PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n  props: PopoverPanelProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-popover-panel-${internalId}`,\n    focus = false,\n    anchor: rawAnchor,\n    portal = false,\n    modal = false,\n    transition = false,\n    ...theirProps\n  } = props\n\n  let machine = usePopoverMachineContext('Popover.Panel')\n  let isPortalled = useSlice(machine, machine.selectors.isPortalled)\n\n  let [popoverState, button, __demoMode, beforePanelSentinel, afterPanelSentinel] = useSlice(\n    machine,\n    useCallback((state) => {\n      return [\n        state.popoverState,\n        state.button,\n        state.__demoMode,\n        state.beforePanelSentinel,\n        state.afterPanelSentinel,\n      ] as const\n    }, [])\n  )\n\n  let beforePanelSentinelId = `headlessui-focus-sentinel-before-${internalId}`\n  let afterPanelSentinelId = `headlessui-focus-sentinel-after-${internalId}`\n\n  let internalPanelRef = useRef<HTMLElement | null>(null)\n  let anchor = useResolvedAnchor(rawAnchor)\n  let [floatingRef, style] = useFloatingPanel(anchor)\n  let getFloatingPanelProps = useFloatingPanelProps()\n\n  // Always enable `portal` functionality, when `anchor` is enabled\n  if (anchor) {\n    portal = true\n  }\n\n  // To improve the correctness of transitions (timing related race conditions),\n  // we track the element locally to this component, instead of relying on the\n  // context value. This way, the component can re-render independently of the\n  // parent component when the `useTransition(…)` hook performs a state change.\n  let [localPanelElement, setLocalPanelElement] = useState<HTMLElement | null>(null)\n\n  let panelRef = useSyncRefs(\n    internalPanelRef,\n    ref,\n    anchor ? floatingRef : null,\n    machine.actions.setPanel,\n    setLocalPanelElement\n  )\n  let portalOwnerDocument = useOwnerDocument(button)\n  let ownerDocument = useOwnerDocument(internalPanelRef.current)\n\n  useIsoMorphicEffect(() => {\n    machine.actions.setPanelId(id)\n    return () => machine.actions.setPanelId(null)\n  }, [id, machine])\n\n  let usesOpenClosedState = useOpenClosed()\n  let [visible, transitionData] = useTransition(\n    transition,\n    localPanelElement,\n    usesOpenClosedState !== null\n      ? (usesOpenClosedState & State.Open) === State.Open\n      : popoverState === PopoverStates.Open\n  )\n\n  // Ensure we close the popover as soon as the button becomes hidden\n  useOnDisappear(visible, button, machine.actions.close)\n\n  // Enable scroll locking when the popover is visible, and `modal` is enabled\n  let scrollLockEnabled = __demoMode ? false : modal && visible\n  useScrollLock(scrollLockEnabled, ownerDocument)\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Escape:\n        if (machine.state.popoverState !== PopoverStates.Open) return\n        if (!internalPanelRef.current) return\n        let activeElement = getActiveElement(internalPanelRef.current)\n        if (activeElement && !internalPanelRef.current.contains(activeElement)) {\n          return\n        }\n        event.preventDefault()\n        event.stopPropagation()\n        machine.actions.close()\n        machine.state.button?.focus()\n        break\n    }\n  })\n\n  // Unlink on \"unmount\" children\n  useEffect(() => {\n    if (props.static) return\n\n    if (popoverState === PopoverStates.Closed && (props.unmount ?? true)) {\n      machine.actions.setPanel(null)\n    }\n  }, [popoverState, props.unmount, props.static, machine])\n\n  // Move focus within panel\n  useEffect(() => {\n    if (__demoMode) return\n    if (!focus) return\n    if (popoverState !== PopoverStates.Open) return\n    if (!internalPanelRef.current) return\n\n    let activeElement = getActiveElement(internalPanelRef.current)\n    if (internalPanelRef.current.contains(activeElement)) return // Already focused within Dialog\n\n    focusIn(internalPanelRef.current, Focus.First)\n  }, [__demoMode, focus, internalPanelRef.current, popoverState])\n\n  let slot = useSlot<PanelRenderPropArg>({\n    open: popoverState === PopoverStates.Open,\n    close: machine.actions.refocusableClose,\n  })\n\n  let ourProps: Record<string, any> = mergeProps(anchor ? getFloatingPanelProps() : {}, {\n    ref: panelRef,\n    id,\n    onKeyDown: handleKeyDown,\n    onBlur:\n      focus && popoverState === PopoverStates.Open\n        ? (event: ReactFocusEvent) => {\n            let el = event.relatedTarget as HTMLElement\n            if (!el) return\n            if (!internalPanelRef.current) return\n            if (internalPanelRef.current?.contains(el)) return\n\n            machine.actions.close()\n\n            if (\n              beforePanelSentinel.current?.contains?.(el) ||\n              afterPanelSentinel.current?.contains?.(el)\n            ) {\n              el.focus({ preventScroll: true })\n            }\n          }\n        : undefined,\n    tabIndex: -1,\n    style: {\n      ...theirProps.style,\n      ...style,\n      '--button-width': useElementSize(visible, button, true).width,\n    } as React.CSSProperties,\n    ...transitionDataAttributes(transitionData),\n  })\n\n  let direction = useTabDirection()\n  let handleBeforeFocus = useEvent(() => {\n    let el = internalPanelRef.current as HTMLElement\n    if (!el) return\n\n    function run() {\n      match(direction.current, {\n        [TabDirection.Forwards]: () => {\n          // Try to focus the first thing in the panel. But if that fails (e.g.: there are no\n          // focusable elements, then we can move outside of the panel)\n          let result = focusIn(el, Focus.First)\n          if (result === FocusResult.Error) {\n            machine.state.afterPanelSentinel.current?.focus()\n          }\n        },\n        [TabDirection.Backwards]: () => {\n          // Coming from the Popover.Panel (which is portalled to somewhere else). Let's redirect\n          // the focus to the Popover.Button again.\n          machine.state.button?.focus({ preventScroll: true })\n        },\n      })\n    }\n\n    // TODO: Cleanup once we are using real browser tests\n    if (process.env.NODE_ENV === 'test') {\n      microTask(run)\n    } else {\n      run()\n    }\n  })\n\n  let handleAfterFocus = useEvent(() => {\n    let el = internalPanelRef.current as HTMLElement\n    if (!el) return\n\n    function run() {\n      match(direction.current, {\n        [TabDirection.Forwards]: () => {\n          if (!machine.state.button) return\n\n          let root = getRootNode(machine.state.button) ?? document.body\n          let elements = getFocusableElements(root)\n\n          let idx = elements.indexOf(machine.state.button)\n          let before = elements.slice(0, idx + 1)\n          let after = elements.slice(idx + 1)\n\n          let combined = [...after, ...before]\n\n          // Ignore sentinel buttons and items inside the panel\n          for (let element of combined.slice()) {\n            if (\n              element.dataset.headlessuiFocusGuard === 'true' ||\n              localPanelElement?.contains(element)\n            ) {\n              let idx = combined.indexOf(element)\n              if (idx !== -1) combined.splice(idx, 1)\n            }\n          }\n\n          focusIn(combined, Focus.First, { sorted: false })\n        },\n        [TabDirection.Backwards]: () => {\n          // Try to focus the first thing in the panel. But if that fails (e.g.: there are no\n          // focusable elements, then we can move outside of the panel)\n          let result = focusIn(el, Focus.Previous)\n          if (result === FocusResult.Error) {\n            machine.state.button?.focus()\n          }\n        },\n      })\n    }\n\n    // TODO: Cleanup once we are using real browser tests\n    if (process.env.NODE_ENV === 'test') {\n      microTask(run)\n    } else {\n      run()\n    }\n  })\n\n  let render = useRender()\n\n  return (\n    <ResetOpenClosedProvider>\n      <PopoverPanelContext.Provider value={id}>\n        <CloseProvider value={machine.actions.refocusableClose}>\n          <Portal\n            enabled={portal ? props.static || visible : false}\n            ownerDocument={portalOwnerDocument}\n          >\n            {visible && isPortalled && (\n              <Hidden\n                id={beforePanelSentinelId}\n                ref={beforePanelSentinel}\n                features={HiddenFeatures.Focusable}\n                data-headlessui-focus-guard\n                as=\"button\"\n                type=\"button\"\n                onFocus={handleBeforeFocus}\n              />\n            )}\n            {render({\n              ourProps,\n              theirProps,\n              slot,\n              defaultTag: DEFAULT_PANEL_TAG,\n              features: PanelRenderFeatures,\n              visible,\n              name: 'Popover.Panel',\n            })}\n            {visible && isPortalled && (\n              <Hidden\n                id={afterPanelSentinelId}\n                ref={afterPanelSentinel}\n                features={HiddenFeatures.Focusable}\n                data-headlessui-focus-guard\n                as=\"button\"\n                type=\"button\"\n                onFocus={handleAfterFocus}\n              />\n            )}\n          </Portal>\n        </CloseProvider>\n      </PopoverPanelContext.Provider>\n    </ResetOpenClosedProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_GROUP_TAG = 'div' as const\ntype GroupRenderPropArg = {}\ntype GroupPropsWeControl = never\n\nexport type PopoverGroupProps<TTag extends ElementType = typeof DEFAULT_GROUP_TAG> = Props<\n  TTag,\n  GroupRenderPropArg,\n  GroupPropsWeControl\n>\n\nfunction GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n  props: PopoverGroupProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalGroupRef = useRef<HTMLElement | null>(null)\n  let groupRef = useSyncRefs(internalGroupRef, ref)\n  let [popovers, setPopovers] = useState<PopoverRegisterBag[]>([])\n\n  let unregisterPopover = useEvent((registerBag: PopoverRegisterBag) => {\n    setPopovers((existing) => {\n      let idx = existing.indexOf(registerBag)\n      if (idx !== -1) {\n        let clone = existing.slice()\n        clone.splice(idx, 1)\n        return clone\n      }\n      return existing\n    })\n  })\n\n  let registerPopover = useEvent((registerBag: PopoverRegisterBag) => {\n    setPopovers((existing) => [...existing, registerBag])\n    return () => unregisterPopover(registerBag)\n  })\n\n  let isFocusWithinPopoverGroup = useEvent(() => {\n    let root = getRootNode(internalGroupRef.current)\n    if (!root) return false\n    let activeElement = getActiveElement(internalGroupRef.current)\n\n    if (internalGroupRef.current?.contains(activeElement)) return true\n\n    // Check if the focus is in one of the button or panel elements. This is important in case you are rendering inside a Portal.\n    return popovers.some((bag) => {\n      return (\n        root!.getElementById(bag.buttonId.current!)?.contains(activeElement) ||\n        root!.getElementById(bag.panelId.current!)?.contains(activeElement)\n      )\n    })\n  })\n\n  let closeOthers = useEvent((buttonId: string) => {\n    for (let popover of popovers) {\n      if (popover.buttonId.current !== buttonId) popover.close()\n    }\n  })\n\n  let contextBag = useMemo<ContextType<typeof PopoverGroupContext>>(\n    () => ({\n      registerPopover: registerPopover,\n      unregisterPopover: unregisterPopover,\n      isFocusWithinPopoverGroup,\n      closeOthers,\n    }),\n    [registerPopover, unregisterPopover, isFocusWithinPopoverGroup, closeOthers]\n  )\n\n  let slot = useSlot<GroupRenderPropArg>({})\n\n  let theirProps = props\n  let ourProps = { ref: groupRef }\n\n  let render = useRender()\n\n  return (\n    <MainTreeProvider>\n      <PopoverGroupContext.Provider value={contextBag}>\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_GROUP_TAG,\n          name: 'Popover.Group',\n        })}\n      </PopoverGroupContext.Provider>\n    </MainTreeProvider>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentPopover extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(\n    props: PopoverProps<TTag> & RefProp<typeof PopoverFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPopoverButton extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(\n    props: PopoverButtonProps<TTag> & RefProp<typeof ButtonFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPopoverBackdrop extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(\n    props: PopoverBackdropProps<TTag> & RefProp<typeof BackdropFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPopoverPanel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n    props: PopoverPanelProps<TTag> & RefProp<typeof PanelFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPopoverGroup extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n    props: PopoverGroupProps<TTag> & RefProp<typeof GroupFn>\n  ): React.JSX.Element\n}\n\nlet PopoverRoot = forwardRefWithAs(PopoverFn) as _internal_ComponentPopover\nexport let PopoverButton = forwardRefWithAs(ButtonFn) as _internal_ComponentPopoverButton\n/** @deprecated use `<PopoverBackdrop>` instead of `<PopoverOverlay>` */\nexport let PopoverOverlay = forwardRefWithAs(BackdropFn) as _internal_ComponentPopoverBackdrop\nexport let PopoverBackdrop = forwardRefWithAs(BackdropFn) as _internal_ComponentPopoverBackdrop\nexport let PopoverPanel = forwardRefWithAs(PanelFn) as _internal_ComponentPopoverPanel\nexport let PopoverGroup = forwardRefWithAs(GroupFn) as _internal_ComponentPopoverGroup\n\nexport let Popover = Object.assign(PopoverRoot, {\n  /** @deprecated use `<PopoverButton>` instead of `<Popover.Button>` */\n  Button: PopoverButton,\n  /** @deprecated use `<PopoverBackdrop>` instead of `<Popover.Backdrop>` */\n  Backdrop: PopoverBackdrop,\n  /** @deprecated use `<PopoverOverlay>` instead of `<Popover.Overlay>` */\n  Overlay: PopoverOverlay,\n  /** @deprecated use `<PopoverPanel>` instead of `<Popover.Panel>` */\n  Panel: PopoverPanel,\n  /** @deprecated use `<PopoverGroup>` instead of `<Popover.Group>` */\n  Group: PopoverGroup,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover-backdrop/popover-backdrop.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../popover/popover'\nexport { PopoverBackdrop } from '../popover/popover'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover-button/popover-button.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../popover/popover'\nexport { PopoverButton } from '../popover/popover'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover-group/popover-group.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../popover/popover'\nexport { PopoverGroup } from '../popover/popover'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover-overlay/popover-overlay.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../popover/popover'\nexport { PopoverOverlay } from '../popover/popover'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/popover-panel/popover-panel.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../popover/popover'\nexport { PopoverPanel } from '../popover/popover'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/portal/__snapshots__/portal.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to force the Portal into a specific element using Portal.Group 1`] = `\"<div><main><aside id=\\\\\"group-1\\\\\">A<div data-headlessui-portal=\\\\\"\\\\\">Next to A</div></aside><section id=\\\\\"group-2\\\\\"><span>B</span></section></main></div><div id=\\\\\"headlessui-portal-root\\\\\"><div data-headlessui-portal=\\\\\"\\\\\">I am in the portal root</div></div>\"`;\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/portal/portal.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { useRef, useState } from 'react'\nimport { click } from '../../test-utils/interactions'\nimport { Portal } from './portal'\n\nfunction getPortalRoot() {\n  return document.getElementById('headlessui-portal-root')!\n}\n\nbeforeEach(() => {\n  document.body.innerHTML = ''\n})\n\nit('should be possible to use a Portal', () => {\n  expect(getPortalRoot()).toBe(null)\n\n  render(\n    <main id=\"parent\">\n      <Portal>\n        <p id=\"content\">Contents...</p>\n      </Portal>\n    </main>\n  )\n\n  let parent = document.getElementById('parent')\n  let content = document.getElementById('content')\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Ensure the content is not part of the parent\n  expect(parent).not.toContainElement(content)\n\n  // Ensure the content does exist\n  expect(content).not.toBe(null)\n  expect(content).toHaveTextContent('Contents...')\n})\n\nit('should be possible to use multiple Portal elements', () => {\n  expect(getPortalRoot()).toBe(null)\n\n  render(\n    <main id=\"parent\">\n      <Portal>\n        <p id=\"content1\">Contents 1 ...</p>\n      </Portal>\n      <hr />\n      <Portal>\n        <p id=\"content2\">Contents 2 ...</p>\n      </Portal>\n    </main>\n  )\n\n  let parent = document.getElementById('parent')\n  let content1 = document.getElementById('content1')\n  let content2 = document.getElementById('content2')\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Ensure the content1 is not part of the parent\n  expect(parent).not.toContainElement(content1)\n\n  // Ensure the content2 is not part of the parent\n  expect(parent).not.toContainElement(content2)\n\n  // Ensure the content does exist\n  expect(content1).not.toBe(null)\n  expect(content1).toHaveTextContent('Contents 1 ...')\n\n  // Ensure the content does exist\n  expect(content2).not.toBe(null)\n  expect(content2).toHaveTextContent('Contents 2 ...')\n})\n\nit('should cleanup the Portal root when the last Portal is unmounted', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  function Example() {\n    let [renderA, setRenderA] = useState(false)\n    let [renderB, setRenderB] = useState(false)\n\n    return (\n      <main id=\"parent\">\n        <button id=\"a\" onClick={() => setRenderA((v) => !v)}>\n          Toggle A\n        </button>\n        <button id=\"b\" onClick={() => setRenderB((v) => !v)}>\n          Toggle B\n        </button>\n\n        {renderA && (\n          <Portal>\n            <p id=\"content1\">Contents 1 ...</p>\n          </Portal>\n        )}\n\n        {renderB && (\n          <Portal>\n            <p id=\"content2\">Contents 2 ...</p>\n          </Portal>\n        )}\n      </main>\n    )\n  }\n\n  render(<Example />)\n\n  let a = document.getElementById('a')\n  let b = document.getElementById('b')\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Let's render the first Portal\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(1)\n\n  // Let's render the second Portal\n  await click(b)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(2)\n\n  // Let's remove the first portal\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(1)\n\n  // Let's remove the second Portal\n  await click(b)\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Let's render the first Portal again\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(1)\n})\n\nit('should be possible to render multiple portals at the same time', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  function Example() {\n    let [renderA, setRenderA] = useState(true)\n    let [renderB, setRenderB] = useState(true)\n    let [renderC, setRenderC] = useState(true)\n\n    return (\n      <main id=\"parent\">\n        <button id=\"a\" onClick={() => setRenderA((v) => !v)}>\n          Toggle A\n        </button>\n        <button id=\"b\" onClick={() => setRenderB((v) => !v)}>\n          Toggle B\n        </button>\n        <button id=\"c\" onClick={() => setRenderC((v) => !v)}>\n          Toggle C\n        </button>\n\n        <button\n          id=\"double\"\n          onClick={() => {\n            setRenderA((v) => !v)\n            setRenderB((v) => !v)\n          }}\n        >\n          Toggle A & B{' '}\n        </button>\n\n        {renderA && (\n          <Portal>\n            <p id=\"content1\">Contents 1 ...</p>\n          </Portal>\n        )}\n\n        {renderB && (\n          <Portal>\n            <p id=\"content2\">Contents 2 ...</p>\n          </Portal>\n        )}\n\n        {renderC && (\n          <Portal>\n            <p id=\"content3\">Contents 3 ...</p>\n          </Portal>\n        )}\n      </main>\n    )\n  }\n\n  render(<Example />)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(3)\n\n  // Remove Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot().childNodes).toHaveLength(2)\n\n  // Remove Portal 2\n  await click(document.getElementById('b'))\n  expect(getPortalRoot().childNodes).toHaveLength(1)\n\n  // Re-add Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot().childNodes).toHaveLength(2)\n\n  // Remove Portal 3\n  await click(document.getElementById('c'))\n  expect(getPortalRoot().childNodes).toHaveLength(1)\n\n  // Remove Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot()).toBe(null)\n\n  // Render A and B at the same time!\n  await click(document.getElementById('double'))\n  expect(getPortalRoot().childNodes).toHaveLength(2)\n})\n\nit('should be possible to tamper with the modal root and restore correctly', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  function Example() {\n    let [renderA, setRenderA] = useState(true)\n    let [renderB, setRenderB] = useState(true)\n\n    return (\n      <main id=\"parent\">\n        <button id=\"a\" onClick={() => setRenderA((v) => !v)}>\n          Toggle A\n        </button>\n        <button id=\"b\" onClick={() => setRenderB((v) => !v)}>\n          Toggle B\n        </button>\n\n        {renderA && (\n          <Portal>\n            <p id=\"content1\">Contents 1 ...</p>\n          </Portal>\n        )}\n\n        {renderB && (\n          <Portal>\n            <p id=\"content2\">Contents 2 ...</p>\n          </Portal>\n        )}\n      </main>\n    )\n  }\n\n  render(<Example />)\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Tamper tamper\n  document.body.removeChild(document.getElementById('headlessui-portal-root')!)\n\n  // Hide Portal 1 and 2\n  await click(document.getElementById('a'))\n  await click(document.getElementById('b'))\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Re-show Portal 1 and 2\n  await click(document.getElementById('a'))\n  await click(document.getElementById('b'))\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().childNodes).toHaveLength(2)\n})\n\nit('should be possible to force the Portal into a specific element using Portal.Group', async () => {\n  function Example() {\n    let container = useRef(null)\n\n    return (\n      <main>\n        <aside ref={container} id=\"group-1\">\n          A\n        </aside>\n\n        <Portal.Group target={container}>\n          <section id=\"group-2\">\n            <span>B</span>\n          </section>\n          <Portal>Next to A</Portal>\n        </Portal.Group>\n\n        <Portal>I am in the portal root</Portal>\n      </main>\n    )\n  }\n\n  render(<Example />)\n\n  expect(document.body.innerHTML).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/portal/portal.tsx",
    "content": "'use client'\n\nimport React, {\n  Fragment,\n  createContext,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ContextType,\n  type ElementType,\n  type MutableRefObject,\n  type Ref,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useOnUnmount } from '../../hooks/use-on-unmount'\nimport { useOwnerDocument } from '../../hooks/use-owner'\nimport { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'\nimport { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'\nimport { usePortalRoot } from '../../internal/portal-force-root'\nimport type { Props } from '../../types'\nimport { env } from '../../utils/env'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'\n\nfunction usePortalTarget(ownerDocument: Document | null): HTMLElement | null {\n  let forceInRoot = usePortalRoot()\n  let groupTarget = useContext(PortalGroupContext)\n\n  let [target, setTarget] = useState(() => {\n    // Group context is used, but still null\n    if (!forceInRoot && groupTarget !== null) return groupTarget.current ?? null\n\n    // No group context is used, let's create a default portal root\n    if (env.isServer) return null\n    let existingRoot = ownerDocument?.getElementById('headlessui-portal-root')\n    if (existingRoot) return existingRoot\n\n    if (ownerDocument === null) return null\n\n    let root = ownerDocument.createElement('div')\n    root.setAttribute('id', 'headlessui-portal-root')\n    return ownerDocument.body.appendChild(root)\n  })\n\n  // Ensure the portal root is always in the DOM\n  useEffect(() => {\n    if (target === null) return\n\n    if (!ownerDocument?.body.contains(target)) {\n      ownerDocument?.body.appendChild(target)\n    }\n  }, [target, ownerDocument])\n\n  useEffect(() => {\n    if (forceInRoot) return\n    if (groupTarget === null) return\n    setTarget(groupTarget.current)\n  }, [groupTarget, setTarget, forceInRoot])\n\n  return target\n}\n\n// ---\n\nlet DEFAULT_PORTAL_TAG = Fragment\ntype PortalRenderPropArg = {}\ntype PortalPropsWeControl = never\n\nexport type PortalProps<TTag extends ElementType = typeof DEFAULT_PORTAL_TAG> = Props<\n  TTag,\n  PortalRenderPropArg,\n  PortalPropsWeControl,\n  {\n    enabled?: boolean\n    ownerDocument?: Document | null\n  }\n>\n\nlet InternalPortalFn = forwardRefWithAs(function InternalPortalFn<\n  TTag extends ElementType = typeof DEFAULT_PORTAL_TAG,\n>(props: PortalProps<TTag>, ref: Ref<HTMLElement>) {\n  let { ownerDocument: incomingOwnerDocument = null, ...theirProps } = props\n  let internalPortalRootRef = useRef<HTMLElement | null>(null)\n  let portalRef = useSyncRefs(\n    optionalRef<(typeof internalPortalRootRef)['current']>((ref) => {\n      internalPortalRootRef.current = ref\n    }),\n    ref\n  )\n  let defaultOwnerDocument = useOwnerDocument(internalPortalRootRef.current)\n  let ownerDocument = incomingOwnerDocument ?? defaultOwnerDocument\n  let target = usePortalTarget(ownerDocument)\n  let parent = useContext(PortalParentContext)\n  let d = useDisposables()\n  let ready = useServerHandoffComplete()\n  let render = useRender()\n\n  useOnUnmount(() => {\n    if (!target) return\n\n    // Cleanup the portal root when all portals are unmounted\n    if (target.childNodes.length <= 0) {\n      target.parentElement?.removeChild(target)\n    }\n  })\n\n  let ourProps = { ref: portalRef }\n\n  return !target || !ready\n    ? null\n    : createPortal(\n        <div\n          data-headlessui-portal=\"\"\n          ref={(el) => {\n            d.dispose()\n\n            if (parent && el) {\n              d.add(parent.register(el))\n            }\n          }}\n        >\n          {render({\n            ourProps,\n            theirProps,\n            slot: {},\n            defaultTag: DEFAULT_PORTAL_TAG,\n            name: 'Portal',\n          })}\n        </div>,\n        target\n      )\n})\n\nfunction PortalFn<TTag extends ElementType = typeof DEFAULT_PORTAL_TAG>(\n  props: PortalProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let portalRef = useSyncRefs(ref)\n\n  let { enabled = true, ownerDocument, ...theirProps } = props\n\n  let render = useRender()\n\n  return enabled ? (\n    <InternalPortalFn {...theirProps} ownerDocument={ownerDocument} ref={portalRef} />\n  ) : (\n    render({\n      ourProps: { ref: portalRef },\n      theirProps,\n      slot: {},\n      defaultTag: DEFAULT_PORTAL_TAG,\n      name: 'Portal',\n    })\n  )\n}\n\n// ---\n\nlet DEFAULT_GROUP_TAG = Fragment\ntype GroupRenderPropArg = {}\ntype GroupPropsWeControl = never\n\nlet PortalGroupContext = createContext<MutableRefObject<HTMLElement | null> | null>(null)\n\nexport type PortalGroupProps<TTag extends ElementType = typeof DEFAULT_GROUP_TAG> = Props<\n  TTag,\n  GroupRenderPropArg,\n  GroupPropsWeControl,\n  {\n    target: MutableRefObject<HTMLElement | null>\n  }\n>\n\nfunction GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n  props: PortalGroupProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { target, ...theirProps } = props\n  let groupRef = useSyncRefs(ref)\n\n  let ourProps = { ref: groupRef }\n\n  let render = useRender()\n\n  return (\n    <PortalGroupContext.Provider value={target}>\n      {render({\n        ourProps,\n        theirProps,\n        defaultTag: DEFAULT_GROUP_TAG,\n        name: 'Popover.Group',\n      })}\n    </PortalGroupContext.Provider>\n  )\n}\n\n// ---\n\nlet PortalParentContext = createContext<{\n  register: (portal: HTMLElement) => () => void\n  unregister: (portal: HTMLElement) => void\n  portals: MutableRefObject<HTMLElement[]>\n} | null>(null)\n\nexport function useNestedPortals() {\n  let parent = useContext(PortalParentContext)\n  let portals = useRef<HTMLElement[]>([])\n\n  let register = useEvent((portal: HTMLElement) => {\n    portals.current.push(portal)\n    if (parent) parent.register(portal)\n    return () => unregister(portal)\n  })\n\n  let unregister = useEvent((portal: HTMLElement) => {\n    let idx = portals.current.indexOf(portal)\n    if (idx !== -1) portals.current.splice(idx, 1)\n    if (parent) parent.unregister(portal)\n  })\n\n  let api = useMemo<ContextType<typeof PortalParentContext>>(\n    () => ({ register, unregister, portals }),\n    [register, unregister, portals]\n  )\n\n  return [\n    portals,\n    useMemo(() => {\n      return function PortalWrapper({ children }: { children: React.ReactNode }) {\n        return <PortalParentContext.Provider value={api}>{children}</PortalParentContext.Provider>\n      }\n    }, [api]),\n  ] as const\n}\n\n// ---\n\nexport interface _internal_ComponentPortal extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PORTAL_TAG>(\n    props: PortalProps<TTag> & RefProp<typeof PortalFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPortalGroup extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n    props: PortalGroupProps<TTag> & RefProp<typeof GroupFn>\n  ): React.JSX.Element\n}\n\nlet PortalRoot = forwardRefWithAs(PortalFn) as unknown as _internal_ComponentPortal\nexport let PortalGroup = forwardRefWithAs(GroupFn) as _internal_ComponentPortalGroup\n\nexport let Portal = Object.assign(PortalRoot, {\n  /** @deprecated use `<PortalGroup>` instead of `<Portal.Group>` */\n  Group: PortalGroup,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio/radio.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../radio-group/radio-group'\nexport { Radio } from '../radio-group/radio-group'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { createElement, useState } from 'react'\nimport {\n  assertActiveElement,\n  assertFocusable,\n  assertNotFocusable,\n  assertRadioGroupLabel,\n  getByText,\n  getRadioGroupOptions,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, click, focus, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { RadioGroup } from './radio-group'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\ndescribe('Safe guards', () => {\n  it.each([['RadioGroup.Option', RadioGroup.Option]])(\n    'should error when we are using a <%s /> without a parent <RadioGroup />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component))).toThrow(\n        `<${name} /> is missing a parent <RadioGroup /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a RadioGroup without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <RadioGroup value={undefined} onChange={console.log}>\n          <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n          <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n          <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n          <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n        </RadioGroup>\n      )\n\n      assertRadioGroupLabel({ textContent: 'Pizza Delivery' })\n    })\n  )\n\n  it('should be possible to render a RadioGroup without options and without crashing', () => {\n    render(<RadioGroup value={undefined} onChange={console.log} />)\n  })\n})\n\ndescribe('Rendering', () => {\n  it(\n    'should be possible to render a RadioGroup, where the first element is tabbable (value is undefined)',\n    suppressConsoleLogs(async () => {\n      render(\n        <RadioGroup value={undefined} onChange={console.log}>\n          <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n          <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n          <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n          <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n        </RadioGroup>\n      )\n\n      expect(getRadioGroupOptions()).toHaveLength(3)\n\n      assertFocusable(getByText('Pickup'))\n      assertNotFocusable(getByText('Home delivery'))\n      assertNotFocusable(getByText('Dine in'))\n    })\n  )\n\n  it(\n    'should be possible to render a RadioGroup, where the first element is tabbable (value is null)',\n    suppressConsoleLogs(async () => {\n      render(\n        <RadioGroup value={null} onChange={console.log}>\n          <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n          <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n          <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n          <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n        </RadioGroup>\n      )\n\n      expect(getRadioGroupOptions()).toHaveLength(3)\n\n      assertFocusable(getByText('Pickup'))\n      assertNotFocusable(getByText('Home delivery'))\n      assertNotFocusable(getByText('Dine in'))\n    })\n  )\n\n  it(\n    'should be possible to render a RadioGroup with an active value',\n    suppressConsoleLogs(async () => {\n      render(\n        <RadioGroup value=\"home-delivery\" onChange={console.log}>\n          <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n          <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n          <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n          <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n        </RadioGroup>\n      )\n\n      expect(getRadioGroupOptions()).toHaveLength(3)\n\n      assertNotFocusable(getByText('Pickup'))\n      assertFocusable(getByText('Home delivery'))\n      assertNotFocusable(getByText('Dine in'))\n    })\n  )\n\n  it(\n    'should guarantee the radio option order after a few unmounts',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [showFirst, setShowFirst] = useState(false)\n        let [active, setActive] = useState()\n\n        return (\n          <>\n            <button onClick={() => setShowFirst((v) => !v)}>Toggle</button>\n            <RadioGroup value={active} onChange={setActive}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              {showFirst && <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>}\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await click(getByText('Toggle')) // Render the pickup again\n\n      await press(Keys.Tab) // Focus first element\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.ArrowUp) // Loop around\n      assertActiveElement(getByText('Dine in'))\n\n      await press(Keys.ArrowUp) // Up again\n      assertActiveElement(getByText('Home delivery'))\n    })\n  )\n\n  it(\n    'should be possible to disable a RadioGroup',\n    suppressConsoleLogs(async () => {\n      let changeFn = jest.fn()\n\n      function Example() {\n        let [disabled, setDisabled] = useState(true)\n        return (\n          <>\n            <button onClick={() => setDisabled((v) => !v)}>Toggle</button>\n            <RadioGroup value={undefined} onChange={changeFn} disabled={disabled}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n              <RadioGroup.Option value=\"render-prop\" data-value=\"render-prop\">\n                {(slot) => <>{JSON.stringify(slot)}</>}\n              </RadioGroup.Option>\n            </RadioGroup>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Try to click one a few options\n      await click(getByText('Pickup'))\n      await click(getByText('Dine in'))\n\n      // Verify that the RadioGroup.Option gets the disabled state\n      expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n        JSON.stringify({\n          checked: false,\n          disabled: true,\n          active: false,\n          hover: false,\n          focus: false,\n          autofocus: false,\n        })\n      )\n\n      // Make sure that the onChange handler never got called\n      expect(changeFn).toHaveBeenCalledTimes(0)\n\n      // Make sure that all the options get an `aria-disabled`\n      let options = getRadioGroupOptions()\n      expect(options).toHaveLength(4)\n      for (let option of options) expect(option).toHaveAttribute('aria-disabled', 'true')\n\n      // Toggle the disabled state\n      await click(getByText('Toggle'))\n\n      // Verify that the RadioGroup.Option gets the disabled state\n      expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n        JSON.stringify({\n          checked: false,\n          disabled: false,\n          active: false,\n          hover: false,\n          focus: false,\n          autofocus: false,\n        })\n      )\n\n      // Try to click one a few options\n      await click(getByText('Pickup'))\n\n      // Make sure that the onChange handler got called\n      expect(changeFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should be possible to disable a RadioGroup.Option',\n    suppressConsoleLogs(async () => {\n      let changeFn = jest.fn()\n\n      function Example() {\n        let [disabled, setDisabled] = useState(true)\n        return (\n          <>\n            <button onClick={() => setDisabled((v) => !v)}>Toggle</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n              <RadioGroup.Option value=\"render-prop\" disabled={disabled} data-value=\"render-prop\">\n                {(slot) => <>{JSON.stringify(slot)}</>}\n              </RadioGroup.Option>\n            </RadioGroup>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      // Try to click the disabled option\n      await click(document.querySelector('[data-value=\"render-prop\"]'))\n\n      // Verify that the RadioGroup.Option gets the disabled state\n      expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n        JSON.stringify({\n          checked: false,\n          disabled: true,\n          active: false,\n          hover: false,\n          focus: false,\n          autofocus: false,\n        })\n      )\n\n      // Make sure that the onChange handler never got called\n      expect(changeFn).toHaveBeenCalledTimes(0)\n\n      // Make sure that the option with value \"render-prop\" gets an `aria-disabled`\n      let options = getRadioGroupOptions()\n      expect(options).toHaveLength(4)\n      for (let option of options) {\n        if (option.dataset.value) {\n          expect(option).toHaveAttribute('aria-disabled', 'true')\n        } else {\n          expect(option).not.toHaveAttribute('aria-disabled')\n        }\n      }\n\n      // Toggle the disabled state\n      await click(getByText('Toggle'))\n\n      // Verify that the RadioGroup.Option gets the disabled state\n      expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n        JSON.stringify({\n          checked: false,\n          disabled: false,\n          active: false,\n          hover: false,\n          focus: false,\n          autofocus: false,\n        })\n      )\n\n      // Try to click one a few options\n      await click(document.querySelector('[data-value=\"render-prop\"]'))\n\n      // Make sure that the onChange handler got called\n      expect(changeFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when performing actions',\n    suppressConsoleLogs(async () => {\n      function Example({ hide = false }) {\n        return (\n          <RadioGroup value={undefined} onChange={() => {}}>\n            <RadioGroup.Option value=\"a\">Option 1</RadioGroup.Option>\n            {!hide && <RadioGroup.Option value=\"b\">Option 2</RadioGroup.Option>}\n            <RadioGroup.Option value=\"c\">Option 3</RadioGroup.Option>\n          </RadioGroup>\n        )\n      }\n\n      let { rerender } = render(<Example />)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      rerender(<Example hide={true} />) // Remove RadioGroup.Option 2\n      rerender(<Example hide={false} />) // Re-add RadioGroup.Option 2\n\n      // Verify that the first radio group option is active\n      assertActiveElement(getByText('Option 1'))\n\n      await press(Keys.ArrowDown)\n      // Verify that the second radio group option is active\n      assertActiveElement(getByText('Option 2'))\n\n      await press(Keys.ArrowDown)\n      // Verify that the third radio group option is active\n      assertActiveElement(getByText('Option 3'))\n    })\n  )\n\n  it(\n    'should expose internal data as a render prop',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [value, setValue] = useState(null)\n\n        return (\n          <RadioGroup value={value} onChange={setValue}>\n            <RadioGroup.Option value=\"a\">Option 1</RadioGroup.Option>\n            <RadioGroup.Option value=\"b\">Option 2</RadioGroup.Option>\n            <RadioGroup.Option value=\"c\">Option 3</RadioGroup.Option>\n          </RadioGroup>\n        )\n      }\n\n      render(<Example />)\n\n      let options = getRadioGroupOptions()\n\n      // Nothing is active yet\n      expect(options[0]).toHaveAttribute('data-headlessui-state', '')\n      expect(options[1]).toHaveAttribute('data-headlessui-state', '')\n      expect(options[2]).toHaveAttribute('data-headlessui-state', '')\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      // The first one should be active, but not checked yet\n      expect(options[0]).toHaveAttribute('data-headlessui-state', 'active focus')\n      expect(options[1]).toHaveAttribute('data-headlessui-state', '')\n      expect(options[2]).toHaveAttribute('data-headlessui-state', '')\n\n      // Select the first one\n      await press(Keys.Space)\n\n      // The first one should be active and checked\n      expect(options[0]).toHaveAttribute('data-headlessui-state', 'checked active focus')\n      expect(options[1]).toHaveAttribute('data-headlessui-state', '')\n      expect(options[2]).toHaveAttribute('data-headlessui-state', '')\n\n      // Go to the next option\n      await press(Keys.ArrowDown)\n\n      // The second one should be active and checked\n      expect(options[0]).toHaveAttribute('data-headlessui-state', '')\n      expect(options[1]).toHaveAttribute('data-headlessui-state', 'checked active focus')\n      expect(options[2]).toHaveAttribute('data-headlessui-state', '')\n    })\n  )\n\n  describe('Equality', () => {\n    let options = [\n      { id: 1, name: 'Alice' },\n      { id: 2, name: 'Bob' },\n      { id: 3, name: 'Charlie' },\n    ]\n\n    it(\n      'should use object equality by default',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup value={options[1]} onChange={console.log}>\n            {options.map((option) => (\n              <RadioGroup.Option\n                key={option.id}\n                value={option}\n                className={(info) => JSON.stringify(info)}\n              >\n                {option.name}\n              </RadioGroup.Option>\n            ))}\n          </RadioGroup>\n        )\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: true,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare null values by a field',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup value={null} onChange={console.log} by=\"id\">\n            {options.map((option) => (\n              <RadioGroup.Option\n                key={option.id}\n                value={option}\n                className={(info) => JSON.stringify(info)}\n              >\n                {option.name}\n              </RadioGroup.Option>\n            ))}\n          </RadioGroup>\n        )\n\n        let [alice, bob, charlie] = getRadioGroupOptions()\n        expect(alice).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: false,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n        expect(bob).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: false,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n        expect(charlie).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: false,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare objects by a field',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup value={{ id: 2, name: 'Bob' }} onChange={console.log} by=\"id\">\n            {options.map((option) => (\n              <RadioGroup.Option\n                key={option.id}\n                value={option}\n                className={(info) => JSON.stringify(info)}\n              >\n                {option.name}\n              </RadioGroup.Option>\n            ))}\n          </RadioGroup>\n        )\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: true,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare objects by a comparator function',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup\n            value={{ id: 2, name: 'Bob' }}\n            onChange={console.log}\n            by={(a, z) => a.id === z.id}\n          >\n            {options.map((option) => (\n              <RadioGroup.Option\n                key={option.id}\n                value={option}\n                className={(info) => JSON.stringify(info)}\n              >\n                {option.name}\n              </RadioGroup.Option>\n            ))}\n          </RadioGroup>\n        )\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveAttribute(\n          'class',\n          JSON.stringify({\n            checked: true,\n            disabled: false,\n            active: false,\n            hover: false,\n            focus: false,\n            autofocus: false,\n          })\n        )\n      })\n    )\n  })\n\n  describe('Uncontrolled', () => {\n    it(\n      'should be possible to use in an uncontrolled way',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        render(\n          <form\n            onSubmit={(e) => {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            }}\n          >\n            <RadioGroup name=\"assignee\">\n              <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n              <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n              <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n            </RadioGroup>\n            <button id=\"submit\">submit</button>\n          </form>\n        )\n\n        await click(document.getElementById('submit'))\n\n        // No values\n        expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Alice should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n        // Choose charlie\n        await click(getRadioGroupOptions()[2])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Charlie should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n      })\n    )\n\n    it(\n      'should be possible to provide a default value',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        render(\n          <form\n            onSubmit={(e) => {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            }}\n          >\n            <RadioGroup name=\"assignee\" defaultValue=\"bob\">\n              <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n              <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n              <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n            </RadioGroup>\n            <button id=\"submit\">submit</button>\n          </form>\n        )\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Alice should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        render(\n          <form\n            onSubmit={(e) => {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            }}\n          >\n            <RadioGroup name=\"assignee\" defaultValue=\"bob\">\n              <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n              <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n              <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n            </RadioGroup>\n            <button id=\"submit\">submit</button>\n            <button type=\"reset\" id=\"reset\">\n              reset\n            </button>\n          </form>\n        )\n\n        // Bob is the defaultValue\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Alice is now chosen\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // Bob should be submitted again\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset (using objects)',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        render(\n          <form\n            onSubmit={(e) => {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            }}\n          >\n            <RadioGroup name=\"assignee\" defaultValue={{ id: 2, name: 'bob', label: 'Bob' }} by=\"id\">\n              {data.map((person) => (\n                <RadioGroup.Option key={person.id} value={person}>\n                  {person.label}\n                </RadioGroup.Option>\n              ))}\n            </RadioGroup>\n            <button id=\"submit\">submit</button>\n            <button type=\"reset\" id=\"reset\">\n              reset\n            </button>\n          </form>\n        )\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Alice is now chosen\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '1',\n          'assignee[name]': 'alice',\n          'assignee[label]': 'Alice',\n        })\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // Bob should be submitted again\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n      })\n    )\n\n    it(\n      'should still call the onChange listeners when choosing new values',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        render(\n          <RadioGroup name=\"assignee\" onChange={handleChange}>\n            <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n            <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n            <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n          </RadioGroup>\n        )\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Choose bob\n        await click(getRadioGroupOptions()[1])\n\n        // Change handler should have been called twice\n        expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n        expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n      })\n    )\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to tab to the first item',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup value={undefined} onChange={console.log}>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n        )\n\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n      })\n    )\n\n    it(\n      'should not change the selected element on focus',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <RadioGroup value={undefined} onChange={changeFn}>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n        )\n\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(0)\n      })\n    )\n\n    it(\n      'should be possible to tab to the active item',\n      suppressConsoleLogs(async () => {\n        render(\n          <RadioGroup value=\"home-delivery\" onChange={console.log}>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n        )\n\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Home delivery'))\n      })\n    )\n\n    it(\n      'should not change the selected element on focus (when selecting the active item)',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <RadioGroup value=\"home-delivery\" onChange={changeFn}>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n        )\n\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Home delivery'))\n\n        expect(changeFn).toHaveBeenCalledTimes(0)\n      })\n    )\n\n    it(\n      'should be possible to tab out of the radio group (no selected value)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Before'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('After'))\n      })\n    )\n\n    it(\n      'should be possible to tab out of the radio group (selected value)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value=\"home-delivery\" onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Before'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Home delivery'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('After'))\n      })\n    )\n  })\n\n  describe('`Shift+Tab` key', () => {\n    it(\n      'should be possible to tab to the first item',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <RadioGroup value={undefined} onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n\n        assertActiveElement(getByText('Pickup'))\n      })\n    )\n\n    it(\n      'should not change the selected element on focus',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(0)\n      })\n    )\n\n    it(\n      'should be possible to tab to the active item',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <RadioGroup value=\"home-delivery\" onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n\n        assertActiveElement(getByText('Home delivery'))\n      })\n    )\n\n    it(\n      'should not change the selected element on focus (when selecting the active item)',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <RadioGroup value=\"home-delivery\" onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n\n        assertActiveElement(getByText('Home delivery'))\n\n        expect(changeFn).toHaveBeenCalledTimes(0)\n      })\n    )\n\n    it(\n      'should be possible to tab out of the radio group (no selected value)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Pickup'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Before'))\n      })\n    )\n\n    it(\n      'should be possible to tab out of the radio group (selected value)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value=\"home-delivery\" onChange={console.log}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        await focus(getByText('After'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Home delivery'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Before'))\n      })\n    )\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it(\n      'should go to the previous item when pressing the ArrowLeft key',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.ArrowLeft) // Loop around\n        assertActiveElement(getByText('Dine in'))\n\n        await press(Keys.ArrowLeft)\n        assertActiveElement(getByText('Home delivery'))\n\n        expect(changeFn).toHaveBeenCalledTimes(2)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'dine-in')\n        expect(changeFn).toHaveBeenNthCalledWith(2, 'home-delivery')\n      })\n    )\n  })\n\n  describe('`ArrowUp` key', () => {\n    it(\n      'should go to the previous item when pressing the ArrowUp key',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.ArrowUp) // Loop around\n        assertActiveElement(getByText('Dine in'))\n\n        await press(Keys.ArrowUp)\n        assertActiveElement(getByText('Home delivery'))\n\n        expect(changeFn).toHaveBeenCalledTimes(2)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'dine-in')\n        expect(changeFn).toHaveBeenNthCalledWith(2, 'home-delivery')\n      })\n    )\n  })\n\n  describe('`ArrowRight` key', () => {\n    it(\n      'should go to the next item when pressing the ArrowRight key',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.ArrowRight)\n        assertActiveElement(getByText('Home delivery'))\n\n        await press(Keys.ArrowRight)\n        assertActiveElement(getByText('Dine in'))\n\n        await press(Keys.ArrowRight) // Loop around\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(3)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n        expect(changeFn).toHaveBeenNthCalledWith(2, 'dine-in')\n        expect(changeFn).toHaveBeenNthCalledWith(3, 'pickup')\n      })\n    )\n  })\n\n  describe('`ArrowDown` key', () => {\n    it(\n      'should go to the next item when pressing the ArrowDown key',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.ArrowDown)\n        assertActiveElement(getByText('Home delivery'))\n\n        await press(Keys.ArrowDown)\n        assertActiveElement(getByText('Dine in'))\n\n        await press(Keys.ArrowDown) // Loop around\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(3)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n        expect(changeFn).toHaveBeenNthCalledWith(2, 'dine-in')\n        expect(changeFn).toHaveBeenNthCalledWith(3, 'pickup')\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should select the current option when pressing space',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        render(\n          <>\n            <button>Before</button>\n            <RadioGroup value={undefined} onChange={changeFn}>\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.Space)\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(1)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'pickup')\n      })\n    )\n\n    it(\n      'should select the current option only once when pressing space',\n      suppressConsoleLogs(async () => {\n        let changeFn = jest.fn()\n        function Example() {\n          let [value, setValue] = useState(undefined)\n\n          return (\n            <>\n              <button>Before</button>\n              <RadioGroup\n                value={value}\n                onChange={(v) => {\n                  setValue(v)\n                  changeFn(v)\n                }}\n              >\n                <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n                <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n                <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n                <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n              </RadioGroup>\n              <button>After</button>\n            </>\n          )\n        }\n        render(<Example />)\n\n        // Focus the \"Before\" button\n        await press(Keys.Tab)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        assertActiveElement(getByText('Pickup'))\n\n        await press(Keys.Space)\n        await press(Keys.Space)\n        await press(Keys.Space)\n        await press(Keys.Space)\n        await press(Keys.Space)\n        assertActiveElement(getByText('Pickup'))\n\n        expect(changeFn).toHaveBeenCalledTimes(1)\n        expect(changeFn).toHaveBeenNthCalledWith(1, 'pickup')\n      })\n    )\n  })\n\n  describe('`Enter`', () => {\n    it(\n      'should submit the form on `Enter`',\n      suppressConsoleLogs(async () => {\n        let submits = jest.fn()\n\n        function Example() {\n          let [value, setValue] = useState('bob')\n\n          return (\n            <form\n              onSubmit={(event) => {\n                event.preventDefault()\n                submits([...new FormData(event.currentTarget).entries()])\n              }}\n            >\n              <RadioGroup value={value} onChange={setValue} name=\"option\">\n                <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n                <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n                <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n              </RadioGroup>\n              <button>Submit</button>\n            </form>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        // Press enter (which should submit the form)\n        await press(Keys.Enter)\n\n        // Verify the form was submitted\n        expect(submits).toHaveBeenCalledTimes(1)\n        expect(submits).toHaveBeenCalledWith([['option', 'bob']])\n      })\n    )\n\n    it(\n      'should submit the form on `Enter` (when no submit button was found)',\n      suppressConsoleLogs(async () => {\n        let submits = jest.fn()\n\n        function Example() {\n          let [value, setValue] = useState('bob')\n\n          return (\n            <form\n              onSubmit={(event) => {\n                event.preventDefault()\n                submits([...new FormData(event.currentTarget).entries()])\n              }}\n            >\n              <RadioGroup value={value} onChange={setValue} name=\"option\">\n                <RadioGroup.Option value=\"alice\">Alice</RadioGroup.Option>\n                <RadioGroup.Option value=\"bob\">Bob</RadioGroup.Option>\n                <RadioGroup.Option value=\"charlie\">Charlie</RadioGroup.Option>\n              </RadioGroup>\n            </form>\n          )\n        }\n\n        render(<Example />)\n\n        // Focus the RadioGroup\n        await press(Keys.Tab)\n\n        // Press enter (which should submit the form)\n        await press(Keys.Enter)\n\n        // Verify the form was submitted\n        expect(submits).toHaveBeenCalledTimes(1)\n        expect(submits).toHaveBeenCalledWith([['option', 'bob']])\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to change the current radio group value when clicking on a radio option',\n    suppressConsoleLogs(async () => {\n      let changeFn = jest.fn()\n      render(\n        <>\n          <button>Before</button>\n          <RadioGroup value={undefined} onChange={changeFn}>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n          <button>After</button>\n        </>\n      )\n\n      await click(getByText('Home delivery'))\n\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n    })\n  )\n\n  it(\n    'should be a no-op when clicking on the same item',\n    suppressConsoleLogs(async () => {\n      let changeFn = jest.fn()\n      function Example() {\n        let [value, setValue] = useState(undefined)\n\n        return (\n          <>\n            <button>Before</button>\n            <RadioGroup\n              value={value}\n              onChange={(v) => {\n                setValue(v)\n                changeFn(v)\n              }}\n            >\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>After</button>\n          </>\n        )\n      }\n      render(<Example />)\n\n      await click(getByText('Home delivery'))\n      await click(getByText('Home delivery'))\n      await click(getByText('Home delivery'))\n      await click(getByText('Home delivery'))\n\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenCalledTimes(1)\n    })\n  )\n})\n\ndescribe('Form compatibility', () => {\n  it(\n    'should be possible to set the `form`, which is forwarded to the hidden inputs',\n    suppressConsoleLogs(async () => {\n      let submits = jest.fn()\n\n      function Example() {\n        let [value, setValue] = useState(null)\n        return (\n          <div>\n            <RadioGroup form=\"my-form\" value={value} onChange={setValue} name=\"delivery\">\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n\n            <form\n              id=\"my-form\"\n              onSubmit={(event) => {\n                event.preventDefault()\n                submits([...new FormData(event.currentTarget).entries()])\n              }}\n            >\n              <button>Submit</button>\n            </form>\n          </div>\n        )\n      }\n\n      render(<Example />)\n\n      // Choose pickup\n      await click(getByText('Pickup'))\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n    })\n  )\n\n  it(\n    'should be possible to submit a form with a value',\n    suppressConsoleLogs(async () => {\n      let submits = jest.fn()\n\n      function Example() {\n        let [value, setValue] = useState(null)\n        return (\n          <form\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <RadioGroup value={value} onChange={setValue} name=\"delivery\">\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n              <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n              <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n            </RadioGroup>\n            <button>Submit</button>\n          </form>\n        )\n      }\n\n      render(<Example />)\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n      // Choose home delivery\n      await click(getByText('Home delivery'))\n\n      // Submit the form again\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n      // Choose pickup\n      await click(getByText('Pickup'))\n\n      // Submit the form again\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n    })\n  )\n\n  it('should not submit the data if the RadioGroup is disabled', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [value, setValue] = useState('home-delivery')\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <RadioGroup value={value} onChange={setValue} name=\"delivery\" disabled>\n            <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n            <RadioGroup.Option value=\"pickup\">Pickup</RadioGroup.Option>\n            <RadioGroup.Option value=\"home-delivery\">Home delivery</RadioGroup.Option>\n            <RadioGroup.Option value=\"dine-in\">Dine in</RadioGroup.Option>\n          </RadioGroup>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it(\n    'should be possible to submit a form with a complex value object',\n    suppressConsoleLogs(async () => {\n      let submits = jest.fn()\n      let options = [\n        {\n          id: 1,\n          value: 'pickup',\n          label: 'Pickup',\n          extra: { info: 'Some extra info' },\n        },\n        {\n          id: 2,\n          value: 'home-delivery',\n          label: 'Home delivery',\n          extra: { info: 'Some extra info' },\n        },\n        {\n          id: 3,\n          value: 'dine-in',\n          label: 'Dine in',\n          extra: { info: 'Some extra info' },\n        },\n      ]\n\n      function Example() {\n        let [value, setValue] = useState(options[0])\n\n        return (\n          <form\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <RadioGroup value={value} onChange={setValue} name=\"delivery\">\n              <RadioGroup.Label>Pizza Delivery</RadioGroup.Label>\n              {options.map((option) => (\n                <RadioGroup.Option key={option.id} value={option}>\n                  {option.label}\n                </RadioGroup.Option>\n              ))}\n            </RadioGroup>\n            <button>Submit</button>\n          </form>\n        )\n      }\n\n      render(<Example />)\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([\n        ['delivery[id]', '1'],\n        ['delivery[value]', 'pickup'],\n        ['delivery[label]', 'Pickup'],\n        ['delivery[extra][info]', 'Some extra info'],\n      ])\n\n      // Choose home delivery\n      await click(getByText('Home delivery'))\n\n      // Submit the form again\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([\n        ['delivery[id]', '2'],\n        ['delivery[value]', 'home-delivery'],\n        ['delivery[label]', 'Home delivery'],\n        ['delivery[extra][info]', 'Some extra info'],\n      ])\n\n      // Choose pickup\n      await click(getByText('Pickup'))\n\n      // Submit the form again\n      await click(getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([\n        ['delivery[id]', '1'],\n        ['delivery[value]', 'pickup'],\n        ['delivery[label]', 'Pickup'],\n        ['delivery[extra][info]', 'Some extra info'],\n      ])\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio-group/radio-group.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useReducer,\n  useRef,\n  type ElementType,\n  type MutableRefObject,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useByComparator, type ByComparator } from '../../hooks/use-by-comparator'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useDefaultValue } from '../../hooks/use-default-value'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useDisabled } from '../../internal/disabled'\nimport { FormFields } from '../../internal/form-fields'\nimport { useProvidedId } from '../../internal/id'\nimport type { Expand, Props } from '../../types'\nimport { isDisabledReactIssue7711 } from '../../utils/bugs'\nimport { Focus, FocusResult, focusIn, sortByDomNode } from '../../utils/focus-management'\nimport { attemptSubmit } from '../../utils/form'\nimport { match } from '../../utils/match'\nimport { isActiveElement } from '../../utils/owner'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport {\n  Description,\n  useDescribedBy,\n  useDescriptions,\n  type _internal_ComponentDescription,\n} from '../description/description'\nimport { Keys } from '../keyboard'\nimport { Label, useLabelledBy, useLabels, type _internal_ComponentLabel } from '../label/label'\n\ninterface Option<T = unknown> {\n  id: string\n  element: MutableRefObject<HTMLElement | null>\n  propsRef: MutableRefObject<{ value: T; disabled: boolean }>\n}\n\ninterface StateDefinition<T = unknown> {\n  options: Option<T>[]\n}\n\nenum ActionTypes {\n  RegisterOption,\n  UnregisterOption,\n}\n\ntype Actions =\n  | Expand<{ type: ActionTypes.RegisterOption } & Option>\n  | { type: ActionTypes.UnregisterOption; id: Option['id'] }\n\nlet reducers: {\n  [P in ActionTypes]: (\n    state: StateDefinition,\n    action: Extract<Actions, { type: P }>\n  ) => StateDefinition\n} = {\n  [ActionTypes.RegisterOption](state, action) {\n    let nextOptions = [\n      ...state.options,\n      { id: action.id, element: action.element, propsRef: action.propsRef },\n    ]\n\n    return {\n      ...state,\n      options: sortByDomNode(nextOptions, (option) => option.element.current),\n    }\n  },\n  [ActionTypes.UnregisterOption](state, action) {\n    let options = state.options.slice()\n    let idx = state.options.findIndex((radio) => radio.id === action.id)\n    if (idx === -1) return state\n    options.splice(idx, 1)\n    return { ...state, options }\n  },\n}\n\nlet RadioGroupDataContext = createContext<\n  | ({\n      value: unknown\n      firstOption?: Option\n      containsCheckedOption: boolean\n      disabled: boolean\n      compare(a: unknown, z: unknown): boolean\n      tabIndex: number\n    } & StateDefinition)\n  | null\n>(null)\nRadioGroupDataContext.displayName = 'RadioGroupDataContext'\n\nfunction useData(component: string) {\n  let context = useContext(RadioGroupDataContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <RadioGroup /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useData)\n    throw err\n  }\n  return context\n}\ntype _Data = ReturnType<typeof useData>\n\nlet RadioGroupActionsContext = createContext<{\n  registerOption(option: Option): () => void\n  change(value: unknown): boolean\n} | null>(null)\nRadioGroupActionsContext.displayName = 'RadioGroupActionsContext'\n\nfunction useActions(component: string) {\n  let context = useContext(RadioGroupActionsContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <RadioGroup /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useActions)\n    throw err\n  }\n  return context\n}\ntype _Actions = ReturnType<typeof useActions>\n\nfunction stateReducer<T>(state: StateDefinition<T>, action: Actions) {\n  return match(action.type, reducers, state, action)\n}\n\n// ---\n\nlet DEFAULT_RADIO_GROUP_TAG = 'div' as const\ntype RadioGroupRenderPropArg<TType> = {\n  value: TType\n}\ntype RadioGroupPropsWeControl = 'role' | 'aria-labelledby' | 'aria-describedby'\n\nexport type RadioGroupProps<\n  TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,\n  TType = string,\n> = Props<\n  TTag,\n  RadioGroupRenderPropArg<TType>,\n  RadioGroupPropsWeControl,\n  {\n    value?: TType\n    defaultValue?: TType\n    onChange?: (value: TType) => void\n    by?: ByComparator<TType>\n    disabled?: boolean\n    form?: string\n    name?: string\n  }\n>\n\nfunction RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG, TType = string>(\n  props: RadioGroupProps<TTag, TType>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedDisabled = useDisabled()\n  let {\n    id = `headlessui-radiogroup-${internalId}`,\n    value: controlledValue,\n    form,\n    name,\n    onChange: controlledOnChange,\n    by,\n    disabled = providedDisabled || false,\n    defaultValue: _defaultValue,\n    tabIndex = 0,\n    ...theirProps\n  } = props\n  let compare = useByComparator(by)\n  let [state, dispatch] = useReducer(stateReducer, { options: [] } as StateDefinition<TType>)\n  let options = state.options as Option<TType>[]\n  let [labelledby, LabelProvider] = useLabels()\n  let [describedby, DescriptionProvider] = useDescriptions()\n  let internalRadioGroupRef = useRef<HTMLElement | null>(null)\n  let radioGroupRef = useSyncRefs(internalRadioGroupRef, ref)\n\n  let defaultValue = useDefaultValue(_defaultValue)\n  let [value, onChange] = useControllable(controlledValue, controlledOnChange, defaultValue)\n\n  let firstOption = useMemo(\n    () =>\n      options.find((option) => {\n        if (option.propsRef.current.disabled) return false\n        return true\n      }),\n    [options]\n  )\n  let containsCheckedOption = useMemo(\n    () => options.some((option) => compare(option.propsRef.current.value as TType, value)),\n    [options, value]\n  )\n\n  let triggerChange = useEvent((nextValue: TType) => {\n    if (disabled) return false\n    if (compare(nextValue, value)) return false\n    let nextOption = options.find((option) =>\n      compare(option.propsRef.current.value as TType, nextValue)\n    )?.propsRef.current\n    if (nextOption?.disabled) return false\n\n    onChange?.(nextValue)\n\n    return true\n  })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    let container = internalRadioGroupRef.current\n    if (!container) return\n\n    let all = options\n      .filter((option) => option.propsRef.current.disabled === false)\n      .map((radio) => radio.element.current) as HTMLElement[]\n\n    switch (event.key) {\n      case Keys.Enter:\n        attemptSubmit(event.currentTarget)\n        break\n      case Keys.ArrowLeft:\n      case Keys.ArrowUp:\n        {\n          event.preventDefault()\n          event.stopPropagation()\n\n          let result = focusIn(all, Focus.Previous | Focus.WrapAround)\n\n          if (result === FocusResult.Success) {\n            let activeOption = options.find((option) => isActiveElement(option.element.current))\n            if (activeOption) triggerChange(activeOption.propsRef.current.value)\n          }\n        }\n        break\n\n      case Keys.ArrowRight:\n      case Keys.ArrowDown:\n        {\n          event.preventDefault()\n          event.stopPropagation()\n\n          let result = focusIn(all, Focus.Next | Focus.WrapAround)\n\n          if (result === FocusResult.Success) {\n            let activeOption = options.find((option) => isActiveElement(option.element.current))\n            if (activeOption) triggerChange(activeOption.propsRef.current.value)\n          }\n        }\n        break\n\n      case Keys.Space:\n        {\n          event.preventDefault()\n          event.stopPropagation()\n\n          let activeOption = options.find((option) => isActiveElement(option.element.current))\n          if (activeOption) triggerChange(activeOption.propsRef.current.value)\n        }\n        break\n    }\n  })\n\n  let registerOption = useEvent((option: Option) => {\n    dispatch({ type: ActionTypes.RegisterOption, ...option })\n    return () => dispatch({ type: ActionTypes.UnregisterOption, id: option.id })\n  })\n\n  let radioGroupData = useMemo<_Data>(\n    () => ({ value, firstOption, containsCheckedOption, disabled, compare, tabIndex, ...state }),\n    [value, firstOption, containsCheckedOption, disabled, compare, tabIndex, state]\n  )\n  let radioGroupActions = useMemo<_Actions>(\n    () => ({ registerOption, change: triggerChange }),\n    [registerOption, triggerChange]\n  )\n\n  let ourProps = {\n    ref: radioGroupRef,\n    id,\n    role: 'radiogroup',\n    'aria-labelledby': labelledby,\n    'aria-describedby': describedby,\n    onKeyDown: handleKeyDown,\n  }\n\n  let slot = useSlot<RadioGroupRenderPropArg<TType>>({ value })\n\n  let reset = useCallback(() => {\n    if (defaultValue === undefined) return\n    return triggerChange(defaultValue)\n  }, [triggerChange, defaultValue])\n\n  let render = useRender()\n\n  return (\n    <DescriptionProvider name=\"RadioGroup.Description\">\n      <LabelProvider name=\"RadioGroup.Label\">\n        <RadioGroupActionsContext.Provider value={radioGroupActions}>\n          <RadioGroupDataContext.Provider value={radioGroupData}>\n            {name != null && (\n              <FormFields\n                disabled={disabled}\n                data={{ [name]: value || 'on' }}\n                overrides={{ type: 'radio', checked: value != null }}\n                form={form}\n                onReset={reset}\n              />\n            )}\n\n            {render({\n              ourProps,\n              theirProps,\n              slot,\n              defaultTag: DEFAULT_RADIO_GROUP_TAG,\n              name: 'RadioGroup',\n            })}\n          </RadioGroupDataContext.Provider>\n        </RadioGroupActionsContext.Provider>\n      </LabelProvider>\n    </DescriptionProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_OPTION_TAG = 'div' as const\ntype OptionRenderPropArg = {\n  checked: boolean\n  /** @deprecated use `focus` instead */\n  active: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n  disabled: boolean\n}\ntype OptionPropsWeControl =\n  | 'aria-checked'\n  | 'aria-describedby'\n  | 'aria-labelledby'\n  | 'role'\n  | 'tabIndex'\n\nexport type RadioOptionProps<TTag extends ElementType, TType> = Props<\n  TTag,\n  OptionRenderPropArg,\n  OptionPropsWeControl,\n  {\n    value: TType\n    disabled?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction OptionFn<\n  TTag extends ElementType = typeof DEFAULT_OPTION_TAG,\n  // TODO: One day we will be able to infer this type from the generic in RadioGroup itself.\n  // But today is not that day..\n  TType = Parameters<typeof RadioGroupRoot>[0]['value'],\n>(props: RadioOptionProps<TTag, TType>, ref: Ref<HTMLElement>) {\n  let data = useData('RadioGroup.Option')\n  let actions = useActions('RadioGroup.Option')\n\n  let internalId = useId()\n  let {\n    id = `headlessui-radiogroup-option-${internalId}`,\n    value,\n    disabled = data.disabled || false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n\n  let internalOptionRef = useRef<HTMLElement | null>(null)\n  let optionRef = useSyncRefs(internalOptionRef, ref)\n\n  let [labelledby, LabelProvider] = useLabels()\n  let [describedby, DescriptionProvider] = useDescriptions()\n\n  let propsRef = useLatestValue({ value, disabled })\n\n  useIsoMorphicEffect(\n    () => actions.registerOption({ id, element: internalOptionRef, propsRef }),\n    [id, actions, internalOptionRef, propsRef]\n  )\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()\n    if (!actions.change(value)) return\n    internalOptionRef.current?.focus()\n  })\n\n  let isFirstOption = data.firstOption?.id === id\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let checked = data.compare(data.value as TType, value)\n  let ourProps = mergeProps(\n    {\n      ref: optionRef,\n      id,\n      role: 'radio',\n      'aria-checked': checked ? 'true' : 'false',\n      'aria-labelledby': labelledby,\n      'aria-describedby': describedby,\n      'aria-disabled': disabled ? true : undefined,\n      tabIndex: (() => {\n        if (disabled) return -1\n        if (checked) return data.tabIndex\n        if (!data.containsCheckedOption && isFirstOption) return data.tabIndex\n        return -1\n      })(),\n      onClick: disabled ? undefined : handleClick,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps\n  )\n\n  let slot = useSlot<OptionRenderPropArg>({\n    checked,\n    disabled,\n    active: focus,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let render = useRender()\n\n  return (\n    <DescriptionProvider name=\"RadioGroup.Description\">\n      <LabelProvider name=\"RadioGroup.Label\">\n        {render({\n          ourProps,\n          theirProps,\n          slot,\n          defaultTag: DEFAULT_OPTION_TAG,\n          name: 'RadioGroup.Option',\n        })}\n      </LabelProvider>\n    </DescriptionProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_RADIO_TAG = 'span' as const\ntype RadioRenderPropArg = {\n  checked: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n  disabled: boolean\n}\ntype RadioPropsWeControl =\n  | 'aria-checked'\n  | 'aria-describedby'\n  | 'aria-labelledby'\n  | 'role'\n  | 'tabIndex'\n\nexport type RadioProps<TTag extends ElementType = typeof DEFAULT_RADIO_TAG, TType = string> = Props<\n  TTag,\n  RadioRenderPropArg,\n  RadioPropsWeControl,\n  {\n    value: TType\n    disabled?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction RadioFn<\n  TTag extends ElementType = typeof DEFAULT_RADIO_TAG,\n  // TODO: One day we will be able to infer this type from the generic in RadioGroup itself.\n  // But today is not that day..\n  TType = Parameters<typeof RadioGroupRoot>[0]['value'],\n>(props: RadioProps<TTag, TType>, ref: Ref<HTMLElement>) {\n  let data = useData('Radio')\n  let actions = useActions('Radio')\n\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-radio-${internalId}`,\n    value,\n    disabled = data.disabled || providedDisabled || false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let internalRadioRef = useRef<HTMLElement | null>(null)\n  let radioRef = useSyncRefs(internalRadioRef, ref)\n\n  let labelledby = useLabelledBy()\n  let describedby = useDescribedBy()\n\n  let propsRef = useLatestValue({ value, disabled })\n\n  useIsoMorphicEffect(\n    () => actions.registerOption({ id, element: internalRadioRef, propsRef }),\n    [id, actions, internalRadioRef, propsRef]\n  )\n\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()\n    if (!actions.change(value)) return\n\n    internalRadioRef.current?.focus()\n  })\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let isFirstOption = data.firstOption?.id === id\n\n  let checked = data.compare(data.value as TType, value)\n  let ourProps = mergeProps(\n    {\n      ref: radioRef,\n      id,\n      role: 'radio',\n      'aria-checked': checked ? 'true' : 'false',\n      'aria-labelledby': labelledby,\n      'aria-describedby': describedby,\n      'aria-disabled': disabled ? true : undefined,\n      tabIndex: (() => {\n        if (disabled) return -1\n        if (checked) return data.tabIndex\n        if (!data.containsCheckedOption && isFirstOption) return data.tabIndex\n        return -1\n      })(),\n      autoFocus,\n      onClick: disabled ? undefined : handleClick,\n    },\n    focusProps,\n    hoverProps\n  )\n  let slot = useSlot<RadioRenderPropArg>({ checked, disabled, hover, focus, autofocus: autoFocus })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_RADIO_TAG,\n    name: 'Radio',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentRadioGroup extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG, TType = string>(\n    props: RadioGroupProps<TTag, TType> & RefProp<typeof RadioGroupFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentRadioOption extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_OPTION_TAG, TType = string>(\n    props: RadioOptionProps<TTag, TType> & RefProp<typeof OptionFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentRadio extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_RADIO_TAG, TType = string>(\n    props: RadioProps<TTag, TType> & RefProp<typeof RadioFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentRadioLabel extends _internal_ComponentLabel {}\nexport interface _internal_ComponentRadioDescription extends _internal_ComponentDescription {}\n\nlet RadioGroupRoot = forwardRefWithAs(RadioGroupFn) as _internal_ComponentRadioGroup\n/** @deprecated use `<Radio>` instead of `<RadioGroupOption>` */\nexport let RadioGroupOption = forwardRefWithAs(OptionFn) as _internal_ComponentRadioOption\nexport let Radio = forwardRefWithAs(RadioFn) as _internal_ComponentRadio\n/** @deprecated use `<Label>` instead of `<RadioGroupLabel>` */\nexport let RadioGroupLabel = Label as _internal_ComponentRadioLabel\n/** @deprecated use `<Description>` instead of `<RadioGroupDescription>` */\nexport let RadioGroupDescription = Description as _internal_ComponentRadioDescription\n\nexport let RadioGroup = Object.assign(RadioGroupRoot, {\n  /** @deprecated use `<Radio>` instead of `<RadioGroup.Option>` */\n  Option: RadioGroupOption,\n  /** @deprecated use `<Radio>` instead of `<RadioGroup.Radio>` */\n  Radio: Radio,\n  /** @deprecated use `<Label>` instead of `<RadioGroup.Label>` */\n  Label: RadioGroupLabel,\n  /** @deprecated use `<Description>` instead of `<RadioGroup.Description>` */\n  Description: RadioGroupDescription,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio-group-description/radio-group-description.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../radio-group/radio-group'\nexport { RadioGroupDescription } from '../radio-group/radio-group'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio-group-label/radio-group-label.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../radio-group/radio-group'\nexport { RadioGroupLabel } from '../radio-group/radio-group'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/radio-group-option/radio-group-option.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../radio-group/radio-group'\nexport { RadioGroupOption } from '../radio-group/radio-group'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/select/select.test.tsx",
    "content": "import React from 'react'\nimport { getSelect } from '../../test-utils/accessibility-assertions'\nimport {\n  commonControlScenarios,\n  commonFormScenarios,\n  commonRenderingScenarios,\n} from '../../test-utils/scenarios'\nimport { Select } from './select'\n\ncommonRenderingScenarios(Select, { getElement: getSelect })\ncommonControlScenarios(Select)\ncommonFormScenarios(\n  (props) => (\n    <Select defaultValue=\"bob\" {...props}>\n      <option value=\"alice\">Alice</option>\n      <option value=\"bob\">Bob</option>\n      <option value=\"charlie\">Charlie</option>\n    </Select>\n  ),\n  {\n    async performUserInteraction(control) {\n      if (control instanceof HTMLSelectElement) {\n        control.value = 'alice'\n      }\n    },\n  }\n)\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/select/select.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { type ElementType, type Ref } from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useId } from '../../hooks/use-id'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useDisabled } from '../../internal/disabled'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { useLabelledBy } from '../label/label'\n\nlet DEFAULT_SELECT_TAG = 'select' as const\n\ntype SelectRenderPropArg = {\n  disabled: boolean\n  hover: boolean\n  focus: boolean\n  active: boolean\n  autofocus: boolean\n  invalid: boolean\n}\ntype SelectPropsWeControl = 'aria-labelledby' | 'aria-describedby'\n\nexport type SelectProps<TTag extends ElementType = typeof DEFAULT_SELECT_TAG> = Props<\n  TTag,\n  SelectRenderPropArg,\n  SelectPropsWeControl,\n  {\n    disabled?: boolean\n    invalid?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction SelectFn<TTag extends ElementType = typeof DEFAULT_SELECT_TAG>(\n  props: SelectProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-select-${internalId}`,\n    disabled = providedDisabled || false,\n    invalid = false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let ourProps = mergeProps(\n    {\n      ref,\n      id,\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      'aria-invalid': invalid ? 'true' : undefined,\n      disabled: disabled || undefined,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let slot = useSlot<SelectRenderPropArg>({\n    disabled,\n    invalid,\n    hover,\n    focus,\n    active,\n    autofocus: autoFocus,\n  })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_SELECT_TAG,\n    name: 'Select',\n  })\n}\n\nexport interface _internal_ComponentSelect extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_SELECT_TAG>(\n    props: SelectProps<TTag> & RefProp<typeof SelectFn>\n  ): React.JSX.Element\n}\n\nexport let Select = forwardRefWithAs(SelectFn) as _internal_ComponentSelect\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/switch/switch.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { useState } from 'react'\nimport {\n  assertActiveElement,\n  assertSwitch,\n  getByText,\n  getSwitch,\n  getSwitchLabel,\n  SwitchState,\n} from '../../test-utils/accessibility-assertions'\nimport { click, focus, Keys, mouseEnter, press } from '../../test-utils/interactions'\nimport { Switch } from './switch'\n\ndescribe('Safe guards', () => {\n  it('should be possible to render a Switch without crashing', () => {\n    render(<Switch checked={false} onChange={console.log} />)\n  })\n})\n\ndescribe('Rendering', () => {\n  it('should be possible to render an (on) Switch using a render prop', () => {\n    render(\n      <Switch checked={true} onChange={console.log}>\n        {({ checked }) => <span>{checked ? 'On' : 'Off'}</span>}\n      </Switch>\n    )\n\n    assertSwitch({ state: SwitchState.On, textContent: 'On' })\n  })\n\n  it('should be possible to render an (off) Switch using a render prop', () => {\n    render(\n      <Switch checked={false} onChange={console.log}>\n        {({ checked }) => <span>{checked ? 'On' : 'Off'}</span>}\n      </Switch>\n    )\n\n    assertSwitch({ state: SwitchState.Off, textContent: 'Off' })\n  })\n\n  it('should be possible to render an (on) Switch using an `as` prop', () => {\n    render(<Switch as=\"span\" checked={true} onChange={console.log} />)\n    assertSwitch({ state: SwitchState.On, tag: 'span' })\n  })\n\n  it('should be possible to render an (off) Switch using an `as` prop', () => {\n    render(<Switch as=\"span\" checked={false} onChange={console.log} />)\n    assertSwitch({ state: SwitchState.Off, tag: 'span' })\n  })\n\n  it('should be possible to use the switch contents as the label', () => {\n    render(\n      <Switch checked={false} onChange={console.log}>\n        <span>Enable notifications</span>\n      </Switch>\n    )\n    assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })\n  })\n\n  describe('`tabIndex` attribute', () => {\n    it('should have a default tabIndex of `0`', () => {\n      render(\n        <Switch checked={false} onChange={console.log}>\n          <span>Enable notifications</span>\n        </Switch>\n      )\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '0' },\n      })\n    })\n\n    it('should be possible to override the `tabIndex`', () => {\n      render(\n        <Switch checked={false} onChange={console.log} tabIndex={3}>\n          <span>Enable notifications</span>\n        </Switch>\n      )\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '3' },\n      })\n    })\n\n    it('should not be possible to override the `tabIndex` to `-1`', () => {\n      render(\n        <Switch checked={false} onChange={console.log} tabIndex={-1}>\n          <span>Enable notifications</span>\n        </Switch>\n      )\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '0' },\n      })\n    })\n  })\n\n  describe('`type` attribute', () => {\n    it('should set the `type` to \"button\" by default', async () => {\n      render(\n        <Switch checked={false} onChange={console.log}>\n          Trigger\n        </Switch>\n      )\n\n      expect(getSwitch()).toHaveAttribute('type', 'button')\n    })\n\n    it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n      render(\n        <Switch checked={false} onChange={console.log} type=\"submit\">\n          Trigger\n        </Switch>\n      )\n\n      expect(getSwitch()).toHaveAttribute('type', 'submit')\n    })\n\n    it('should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"', async () => {\n      let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n        <button ref={ref} {...props} />\n      ))\n\n      render(\n        <Switch checked={false} onChange={console.log} as={CustomButton}>\n          Trigger\n        </Switch>\n      )\n\n      expect(getSwitch()).toHaveAttribute('type', 'button')\n    })\n\n    it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n      render(\n        <Switch checked={false} onChange={console.log} as=\"div\">\n          Trigger\n        </Switch>\n      )\n\n      expect(getSwitch()).not.toHaveAttribute('type')\n    })\n\n    it('should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"', async () => {\n      let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n        <div ref={ref} {...props} />\n      ))\n\n      render(\n        <Switch checked={false} onChange={console.log} as={CustomButton}>\n          Trigger\n        </Switch>\n      )\n\n      expect(getSwitch()).not.toHaveAttribute('type')\n    })\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Switch name=\"notifications\" />\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case, non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to use in an uncontrolled way with a value', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Switch name=\"notifications\" value=\"enabled\" />\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'enabled' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case, non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Switch name=\"notifications\" defaultChecked />\n          <button id=\"submit\">submit</button>\n        </form>\n      )\n\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on by default\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to reset to the default value if the form is reset', async () => {\n      let handleSubmission = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          }}\n        >\n          <Switch name=\"assignee\" value=\"bob\" defaultChecked />\n          <button id=\"submit\">submit</button>\n          <button type=\"reset\" id=\"reset\">\n            reset\n          </button>\n        </form>\n      )\n\n      // Bob is the defaultValue\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Toggle the switch\n      await click(getSwitch())\n\n      // Bob should not be active anymore\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // Bob should be submitted again\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      render(<Switch name=\"notifications\" onChange={handleChange} />)\n\n      // Toggle\n      await click(getSwitch())\n\n      // Toggle\n      await click(getSwitch())\n\n      // Toggle\n      await click(getSwitch())\n\n      // Change handler should have been called 3 times\n      expect(handleChange).toHaveBeenNthCalledWith(1, true)\n      expect(handleChange).toHaveBeenNthCalledWith(2, false)\n      expect(handleChange).toHaveBeenNthCalledWith(3, true)\n    })\n  })\n})\n\ndescribe('Render composition', () => {\n  it('should be possible to render a Switch.Group, Switch and Switch.Label', () => {\n    render(\n      <Switch.Group>\n        <Switch checked={false} onChange={console.log} />\n        <Switch.Label>Enable notifications</Switch.Label>\n      </Switch.Group>\n    )\n\n    assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Label (before the Switch)', () => {\n    render(\n      <Switch.Group>\n        <Switch.Label>Label B</Switch.Label>\n        <Switch checked={false} onChange={console.log}>\n          Label A\n        </Switch>\n      </Switch.Group>\n    )\n\n    // Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive\n    // technologies.\n    //\n    // Thus: Label A should not be part of the \"label\" in this case\n    assertSwitch({ state: SwitchState.Off, label: 'Label B' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Label (after the Switch)', () => {\n    render(\n      <Switch.Group>\n        <Switch checked={false} onChange={console.log}>\n          Label A\n        </Switch>\n        <Switch.Label>Label B</Switch.Label>\n      </Switch.Group>\n    )\n\n    // Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive\n    // technologies.\n    //\n    // Thus: Label A should not be part of the \"label\" in this case\n    assertSwitch({ state: SwitchState.Off, label: 'Label B' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Description (before the Switch)', async () => {\n    render(\n      <Switch.Group>\n        <Switch.Description>This is an important feature</Switch.Description>\n        <Switch checked={false} onChange={console.log} />\n      </Switch.Group>\n    )\n\n    assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Description (after the Switch)', () => {\n    render(\n      <Switch.Group>\n        <Switch checked={false} onChange={console.log} />\n        <Switch.Description>This is an important feature</Switch.Description>\n      </Switch.Group>\n    )\n\n    assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch, Switch.Label and Switch.Description', () => {\n    render(\n      <Switch.Group>\n        <Switch.Label>Label A</Switch.Label>\n        <Switch checked={false} onChange={console.log} />\n        <Switch.Description>This is an important feature</Switch.Description>\n      </Switch.Group>\n    )\n\n    assertSwitch({\n      state: SwitchState.Off,\n      label: 'Label A',\n      description: 'This is an important feature',\n    })\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Space` key', () => {\n    it('should be possible to toggle the Switch with Space', async () => {\n      let handleChange = jest.fn()\n      function Example() {\n        let [state, setState] = useState(false)\n        return (\n          <Switch\n            checked={state}\n            onChange={(value) => {\n              setState(value)\n              handleChange(value)\n            }}\n          />\n        )\n      }\n\n      render(<Example />)\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      await focus(getSwitch())\n\n      // Toggle\n      await press(Keys.Space)\n\n      // Ensure state is on\n      assertSwitch({ state: SwitchState.On })\n\n      // Toggle\n      await press(Keys.Space)\n\n      // Ensure state is off\n      assertSwitch({ state: SwitchState.Off })\n    })\n  })\n\n  describe('`Enter` key', () => {\n    it('should not be possible to use Enter to toggle the Switch', async () => {\n      let handleChange = jest.fn()\n      render(<Switch checked={false} onChange={handleChange} />)\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      await focus(getSwitch())\n\n      // Try to toggle\n      await press(Keys.Enter)\n\n      expect(handleChange).not.toHaveBeenCalled()\n    })\n\n    it('should submit the form on `Enter`', async () => {\n      let submits = jest.fn()\n\n      function Example() {\n        let [value, setValue] = useState(true)\n\n        return (\n          <form\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <Switch checked={value} onChange={setValue} name=\"option\" />\n            <button>Submit</button>\n          </form>\n        )\n      }\n\n      render(<Example />)\n\n      // Focus the input field\n      await focus(getSwitch())\n      assertActiveElement(getSwitch())\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'on']])\n    })\n\n    it('should submit the form on `Enter` (when no submit button was found)', async () => {\n      let submits = jest.fn()\n\n      function Example() {\n        let [value, setValue] = useState(true)\n\n        return (\n          <form\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <Switch checked={value} onChange={setValue} name=\"option\" />\n          </form>\n        )\n      }\n\n      render(<Example />)\n\n      // Focus the input field\n      await focus(getSwitch())\n      assertActiveElement(getSwitch())\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'on']])\n    })\n  })\n\n  describe('`Tab` key', () => {\n    it('should be possible to tab away from the Switch', async () => {\n      render(\n        <div>\n          <Switch checked={false} onChange={console.log} />\n          <button id=\"btn\">Other element</button>\n        </div>\n      )\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      await focus(getSwitch())\n\n      // Expect the switch to be active\n      assertActiveElement(getSwitch())\n\n      // Toggle\n      await press(Keys.Tab)\n\n      // Expect the button to be active\n      assertActiveElement(document.getElementById('btn'))\n    })\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it('should be possible to toggle the Switch with a click', async () => {\n    let handleChange = jest.fn()\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <Switch\n          checked={state}\n          onChange={(value) => {\n            setState(value)\n            handleChange(value)\n          }}\n        />\n      )\n    }\n\n    render(<Example />)\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitch())\n\n    // Ensure state is on\n    assertSwitch({ state: SwitchState.On })\n\n    // Toggle\n    await click(getSwitch())\n\n    // Ensure state is off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  it('should be possible to toggle the Switch with a click on the Label', async () => {\n    let handleChange = jest.fn()\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <Switch.Group>\n          <Switch\n            checked={state}\n            onChange={(value) => {\n              setState(value)\n              handleChange(value)\n            }}\n          />\n          <Switch.Label>The label</Switch.Label>\n        </Switch.Group>\n      )\n    }\n\n    render(<Example />)\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure the switch is focused\n    assertActiveElement(getSwitch())\n\n    // Ensure state is on\n    assertSwitch({ state: SwitchState.On })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure the switch is focused\n    assertActiveElement(getSwitch())\n\n    // Ensure state is off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  it('should not be possible to toggle the Switch with a click on the Label (passive)', async () => {\n    let handleChange = jest.fn()\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <Switch.Group>\n          <Switch\n            checked={state}\n            onChange={(value) => {\n              setState(value)\n              handleChange(value)\n            }}\n          />\n          <Switch.Label passive>The label</Switch.Label>\n        </Switch.Group>\n      )\n    }\n\n    render(<Example />)\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure state is still off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  xit('should be possible to hover the label and trigger a hover on the switch', async () => {\n    // This test doesn't work in JSDOM :(\n    // Keeping it here for reference when we can test this in a real browser\n    function Example() {\n      let [state] = useState(false)\n      return (\n        <Switch.Group>\n          <style>{`.bg{background-color:rgba(0,255,0)}.bg-on-hover:hover{background-color:rgba(255,0,0)}`}</style>\n          <Switch checked={state} className=\"bg bg-on-hover\" />\n          <Switch.Label>The label</Switch.Label>\n        </Switch.Group>\n      )\n    }\n\n    render(<Example />)\n\n    // Verify the switch is not hovered\n    expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(0, 255, 0)')\n\n    // Hover over the *label*\n    await mouseEnter(getSwitchLabel())\n\n    // Make sure the switch gets hover styles\n    expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(255, 0, 0)')\n  })\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <div>\n          <Switch.Group>\n            <Switch form=\"my-form\" checked={state} onChange={setState} name=\"notifications\" />\n            <Switch.Label>Enable notifications</Switch.Label>\n          </Switch.Group>\n\n          <form\n            id=\"my-form\"\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <button>Submit</button>\n          </form>\n        </div>\n      )\n    }\n\n    render(<Example />)\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['notifications', 'on']])\n  })\n\n  it('should be possible to submit a form with an boolean value', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Switch.Group>\n            <Switch checked={state} onChange={setState} name=\"notifications\" />\n            <Switch.Label>Enable notifications</Switch.Label>\n          </Switch.Group>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['notifications', 'on']])\n  })\n\n  it('should be possible to submit a form with a provided string value', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [state, setState] = useState(false)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <Switch.Group>\n            <Switch checked={state} onChange={setState} name=\"fruit\" value=\"apple\" />\n            <Switch.Label>Apple</Switch.Label>\n          </Switch.Group>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['fruit', 'apple']])\n  })\n\n  it('should not submit the data if the Switch is disabled', async () => {\n    let submits = jest.fn()\n\n    function Example() {\n      let [state, setState] = useState(true)\n      return (\n        <form\n          onSubmit={(event) => {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget).entries()])\n          }}\n        >\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <Switch.Group>\n            <Switch checked={state} onChange={setState} name=\"fruit\" value=\"apple\" disabled />\n            <Switch.Label>Apple</Switch.Label>\n          </Switch.Group>\n          <button>Submit</button>\n        </form>\n      )\n    }\n\n    render(<Example />)\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/switch/switch.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  Fragment,\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useRef,\n  useState,\n  type ElementType,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useDefaultValue } from '../../hooks/use-default-value'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { useDisabled } from '../../internal/disabled'\nimport { FormFields } from '../../internal/form-fields'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport { isDisabledReactIssue7711 } from '../../utils/bugs'\nimport * as DOM from '../../utils/dom'\nimport { attemptSubmit } from '../../utils/form'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport {\n  Description,\n  useDescribedBy,\n  useDescriptions,\n  type _internal_ComponentDescription,\n} from '../description/description'\nimport { Keys } from '../keyboard'\nimport { Label, useLabelledBy, useLabels, type _internal_ComponentLabel } from '../label/label'\n\ninterface StateDefinition {\n  switch: HTMLButtonElement | null\n  setSwitch(element: HTMLButtonElement): void\n}\n\nlet GroupContext = createContext<StateDefinition | null>(null)\nGroupContext.displayName = 'GroupContext'\n\n// ---\n\nlet DEFAULT_GROUP_TAG = Fragment\n\nexport type SwitchGroupProps<TTag extends ElementType = typeof DEFAULT_GROUP_TAG> = Props<TTag>\n\nfunction GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n  props: SwitchGroupProps<TTag>\n) {\n  let [switchElement, setSwitchElement] = useState<HTMLButtonElement | null>(null)\n  let [labelledby, LabelProvider] = useLabels()\n  let [describedby, DescriptionProvider] = useDescriptions()\n\n  let context = useMemo<StateDefinition>(\n    () => ({ switch: switchElement, setSwitch: setSwitchElement }),\n    [switchElement, setSwitchElement]\n  )\n\n  let ourProps = {}\n  let theirProps = props\n\n  let render = useRender()\n\n  return (\n    <DescriptionProvider name=\"Switch.Description\" value={describedby}>\n      <LabelProvider\n        name=\"Switch.Label\"\n        value={labelledby}\n        props={{\n          htmlFor: context.switch?.id,\n          onClick(event: React.MouseEvent<HTMLLabelElement>) {\n            if (!switchElement) return\n            if (DOM.isHTMLLabelElement(event.currentTarget)) {\n              event.preventDefault()\n            }\n            switchElement.click()\n            switchElement.focus({ preventScroll: true })\n          },\n        }}\n      >\n        <GroupContext.Provider value={context}>\n          {render({\n            ourProps,\n            theirProps,\n            slot: {},\n            defaultTag: DEFAULT_GROUP_TAG,\n            name: 'Switch.Group',\n          })}\n        </GroupContext.Provider>\n      </LabelProvider>\n    </DescriptionProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_SWITCH_TAG = 'button' as const\ntype SwitchRenderPropArg = {\n  checked: boolean\n  hover: boolean\n  focus: boolean\n  active: boolean\n  autofocus: boolean\n  changing: boolean\n  disabled: boolean\n}\ntype SwitchPropsWeControl = 'aria-checked' | 'aria-describedby' | 'aria-labelledby' | 'role'\n\nexport type SwitchProps<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG> = Props<\n  TTag,\n  SwitchRenderPropArg,\n  SwitchPropsWeControl,\n  {\n    checked?: boolean\n    defaultChecked?: boolean\n    onChange?: (checked: boolean) => void\n    name?: string\n    value?: string\n    form?: string\n    autoFocus?: boolean\n    disabled?: boolean\n    tabIndex?: number\n  }\n>\n\nfunction SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(\n  props: SwitchProps<TTag>,\n  ref: Ref<HTMLButtonElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-switch-${internalId}`,\n    disabled = providedDisabled || false,\n    checked: controlledChecked,\n    defaultChecked: _defaultChecked,\n    onChange: controlledOnChange,\n    name,\n    value,\n    form,\n    autoFocus = false,\n    ...theirProps\n  } = props\n  let groupContext = useContext(GroupContext)\n  let [switchElement, setSwitchElement] = useState<HTMLButtonElement | null>(null)\n  let internalSwitchRef = useRef<HTMLButtonElement | null>(null)\n  let switchRef = useSyncRefs(\n    internalSwitchRef,\n    ref,\n    groupContext === null ? null : groupContext.setSwitch,\n    setSwitchElement\n  )\n\n  let defaultChecked = useDefaultValue(_defaultChecked)\n  let [checked, onChange] = useControllable(\n    controlledChecked,\n    controlledOnChange,\n    defaultChecked ?? false\n  )\n\n  let d = useDisposables()\n  let [changing, setChanging] = useState(false)\n  let toggle = useEvent(() => {\n    setChanging(true)\n    onChange?.(!checked)\n\n    d.nextFrame(() => {\n      setChanging(false)\n    })\n  })\n  let handleClick = useEvent((event: ReactMouseEvent) => {\n    if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()\n    event.preventDefault()\n    toggle()\n  })\n  let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    if (event.key === Keys.Space) {\n      event.preventDefault()\n      toggle()\n    } else if (event.key === Keys.Enter) {\n      attemptSubmit(event.currentTarget)\n    }\n  })\n\n  // This is needed so that we can \"cancel\" the click event when we use the `Enter` key on a button.\n  let handleKeyPress = useEvent((event: ReactKeyboardEvent<HTMLElement>) => event.preventDefault())\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<SwitchRenderPropArg>({\n    checked,\n    disabled,\n    hover,\n    focus,\n    active,\n    autofocus: autoFocus,\n    changing,\n  })\n\n  let ourProps = mergeProps(\n    {\n      id,\n      ref: switchRef,\n      role: 'switch',\n      type: useResolveButtonType(props, switchElement),\n      tabIndex: props.tabIndex === -1 ? 0 : props.tabIndex ?? 0,\n      'aria-checked': checked,\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      disabled: disabled || undefined,\n      autoFocus,\n      onClick: handleClick,\n      onKeyUp: handleKeyUp,\n      onKeyPress: handleKeyPress,\n    },\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let reset = useCallback(() => {\n    if (defaultChecked === undefined) return\n    return onChange?.(defaultChecked)\n  }, [onChange, defaultChecked])\n\n  let render = useRender()\n\n  return (\n    <>\n      {name != null && (\n        <FormFields\n          disabled={disabled}\n          data={{ [name]: value || 'on' }}\n          overrides={{ type: 'checkbox', checked }}\n          form={form}\n          onReset={reset}\n        />\n      )}\n      {render({ ourProps, theirProps, slot, defaultTag: DEFAULT_SWITCH_TAG, name: 'Switch' })}\n    </>\n  )\n}\n\n// ---\n\nexport interface _internal_ComponentSwitch extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(\n    props: SwitchProps<TTag> & RefProp<typeof SwitchFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentSwitchGroup extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(\n    props: SwitchGroupProps<TTag> & RefProp<typeof GroupFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentSwitchLabel extends _internal_ComponentLabel {}\nexport interface _internal_ComponentSwitchDescription extends _internal_ComponentDescription {}\n\nlet SwitchRoot = forwardRefWithAs(SwitchFn) as _internal_ComponentSwitch\n/** @deprecated use `<Field>` instead of `<SwitchGroup>` */\nexport let SwitchGroup = GroupFn as _internal_ComponentSwitchGroup\n/** @deprecated use `<Label>` instead of `<SwitchLabel>` */\nexport let SwitchLabel = Label as _internal_ComponentSwitchLabel\n/** @deprecated use `<Description>` instead of `<SwitchDescription>` */\nexport let SwitchDescription = Description as _internal_ComponentSwitchDescription\n\nexport let Switch = Object.assign(SwitchRoot, {\n  /** @deprecated use `<Field>` instead of `<Switch.Group>` */\n  Group: SwitchGroup,\n  /** @deprecated use `<Label>` instead of `<Switch.Label>` */\n  Label: SwitchLabel,\n  /** @deprecated use `<Description>` instead of `<Switch.Description>` */\n  Description: SwitchDescription,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/switch-description/switch-description.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../switch/switch'\nexport { SwitchDescription } from '../switch/switch'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/switch-group/switch-group.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../switch/switch'\nexport { SwitchGroup } from '../switch/switch'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/switch-label/switch-label.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../switch/switch'\nexport { SwitchLabel } from '../switch/switch'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tab/tab.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../tabs/tabs'\nexport { Tab } from '../tabs/tabs'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tab-group/tab-group.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../tabs/tabs'\nexport { TabGroup } from '../tabs/tabs'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tab-list/tab-list.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../tabs/tabs'\nexport { TabList } from '../tabs/tabs'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tab-panel/tab-panel.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../tabs/tabs'\nexport { TabPanel } from '../tabs/tabs'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tab-panels/tab-panels.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../tabs/tabs'\nexport { TabPanels } from '../tabs/tabs'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tabs/tabs.ssr.test.tsx",
    "content": "import React from 'react'\nimport { renderHydrate, renderSSR } from '../../test-utils/ssr'\nimport { Tab } from './tabs'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nlet spy: jest.SpyInstance<void, Parameters<typeof console.error>>\nbeforeEach(() => (spy = jest.spyOn(console, 'error').mockImplementation(() => {})))\nafterEach(() => spy.mockRestore())\n\nfunction Example(props: { defaultIndex?: number; selectedIndex?: number }) {\n  return (\n    <Tab.Group {...props}>\n      <Tab.List>\n        <Tab>Tab 1</Tab>\n        <Tab>Tab 2</Tab>\n        <Tab>Tab 3</Tab>\n      </Tab.List>\n\n      <Tab.Panels>\n        <Tab.Panel>Content 1</Tab.Panel>\n        <Tab.Panel>Content 2</Tab.Panel>\n        <Tab.Panel>Content 3</Tab.Panel>\n      </Tab.Panels>\n    </Tab.Group>\n  )\n}\n\ndescribe('Rendering', () => {\n  describe('SSR', () => {\n    it('should be possible to server side render the first Tab and Panel', async () => {\n      let { contents } = await renderSSR(<Example />)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n\n    it('should be possible to server side render the defaultIndex Tab and Panel', async () => {\n      let { contents } = await renderSSR(<Example defaultIndex={1} />)\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n\n    it('should be possible to server side render the selectedIndex=0 Tab and Panel', async () => {\n      let { contents } = await renderSSR(<Example selectedIndex={0} />)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n\n    it('should be possible to server side render the selectedIndex=1 Tab and Panel', async () => {\n      let { contents } = await renderSSR(<Example selectedIndex={1} />)\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n  })\n\n  // The hydration tests don't work in React 18 due to some bug in Testing Library maybe?\n  // Skipping for now\n  // TODO: Figure out once 2.0 alpha is released\n  describe.skip.each([{ strict: true }, { strict: false }])('Hydration: %p', (opts) => {\n    it('should be possible to server side render the first Tab and Panel by default', async () => {\n      const { contents } = await renderHydrate(<Example />, opts)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n      expect(spy).not.toHaveBeenCalled()\n    })\n\n    it('should be possible to server side render the first Tab and Panel', async () => {\n      const { contents } = await renderHydrate(<Example defaultIndex={0} />, opts)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n      expect(spy).not.toHaveBeenCalled()\n    })\n\n    it('should be possible to server side render the defaultIndex Tab and Panel', async () => {\n      const { contents } = await renderHydrate(<Example defaultIndex={1} />, opts)\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n      expect(spy).not.toHaveBeenCalled()\n    })\n\n    it('should be possible to server side render the selectedIndex=0 Tab and Panel', async () => {\n      let { contents } = await renderHydrate(<Example selectedIndex={0} />, opts)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n      expect(spy).not.toHaveBeenCalled()\n    })\n\n    it('should be possible to server side render the selectedIndex=1 Tab and Panel', async () => {\n      let { contents } = await renderHydrate(<Example selectedIndex={1} />, opts)\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n      expect(spy).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tabs/tabs.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { createElement, useState } from 'react'\nimport {\n  assertActiveElement,\n  assertTabs,\n  getByText,\n  getTabs,\n} from '../../test-utils/accessibility-assertions'\nimport { Keys, click, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Dialog } from '../dialog/dialog'\nimport { Tab } from './tabs'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\ndescribe('safeguards', () => {\n  it.each([\n    ['Tab.List', Tab.List],\n    ['Tab', Tab],\n    ['Tab.Panels', Tab.Panels],\n    ['Tab.Panel', Tab.Panel],\n  ])(\n    'should error when we are using a <%s /> without a parent <Tab.Group /> component',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(createElement(Component as any))).toThrow(\n        `<${name} /> is missing a parent <Tab.Group /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render Tab.Group without crashing',\n    suppressConsoleLogs(async () => {\n      render(\n        <Tab.Group>\n          <Tab.List>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </Tab.List>\n\n          <Tab.Panels>\n            <Tab.Panel>Content 1</Tab.Panel>\n            <Tab.Panel>Content 2</Tab.Panel>\n            <Tab.Panel>Content 3</Tab.Panel>\n          </Tab.Panels>\n        </Tab.Group>\n      )\n\n      assertTabs({ active: 0 })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  it(\n    'should be possible to render the Tab.Panels first, then the Tab.List',\n    suppressConsoleLogs(async () => {\n      render(\n        <Tab.Group>\n          <Tab.Panels>\n            <Tab.Panel>Content 1</Tab.Panel>\n            <Tab.Panel>Content 2</Tab.Panel>\n            <Tab.Panel>Content 3</Tab.Panel>\n          </Tab.Panels>\n\n          <Tab.List>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </Tab.List>\n        </Tab.Group>\n      )\n\n      assertTabs({ active: 0 })\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when performing actions',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [hide, setHide] = useState(false)\n\n        return (\n          <>\n            <button onClick={() => setHide((v) => !v)}>toggle</button>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                {!hide && <Tab>Tab 2</Tab>}\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                {!hide && <Tab.Panel>Content 2</Tab.Panel>}\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      await click(getByText('toggle')) // Remove Tab 2\n      await click(getByText('toggle')) // Re-add Tab 2\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n    })\n  )\n\n  it(\n    'should guarantee the order when injecting new tabs dynamically',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [tabs, setTabs] = useState<string[]>([])\n\n        return (\n          <Tab.Group>\n            <Tab.List>\n              {tabs.map((t, i) => (\n                <Tab key={t}>Tab {i + 1}</Tab>\n              ))}\n              <Tab>Insert new</Tab>\n            </Tab.List>\n            <Tab.Panels>\n              {tabs.map((t) => (\n                <Tab.Panel key={t}>{t}</Tab.Panel>\n              ))}\n              <Tab.Panel>\n                <button\n                  onClick={() => {\n                    setTabs((old) => [...old, `Panel ${old.length + 1}`])\n                  }}\n                >\n                  Insert\n                </button>\n              </Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n      }\n\n      render(<Example />)\n\n      assertTabs({ active: 0, tabContents: 'Insert new', panelContents: 'Insert' })\n\n      // Add some new tabs\n      await click(getByText('Insert'))\n\n      // We should still be on the tab we were on\n      assertTabs({ active: 1, tabContents: 'Insert new', panelContents: 'Insert' })\n    })\n  )\n\n  it(\n    'should use the `selectedIndex` when injecting new tabs dynamically',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [tabs, setTabs] = useState<string[]>(['A', 'B', 'C'])\n\n        return (\n          <>\n            <Tab.Group selectedIndex={1}>\n              <Tab.List>\n                {tabs.map((t) => (\n                  <Tab key={t}>Tab {t}</Tab>\n                ))}\n              </Tab.List>\n              <Tab.Panels>\n                {tabs.map((t) => (\n                  <Tab.Panel key={t}>Panel {t}</Tab.Panel>\n                ))}\n              </Tab.Panels>\n            </Tab.Group>\n            <button\n              onClick={() => {\n                setTabs((old) => {\n                  let copy = old.slice()\n                  copy.splice(1, 0, 'D')\n                  return copy\n                })\n              }}\n            >\n              Insert\n            </button>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      assertTabs({ active: 1, tabContents: 'Tab B', panelContents: 'Panel B' })\n\n      // Add some new tabs\n      await click(getByText('Insert'))\n\n      // We should still be at the same tab position, but the tab itself changed\n      assertTabs({ active: 1, tabContents: 'Tab D', panelContents: 'Panel D' })\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when reversing the tabs and panels themselves, then performing actions (controlled component)',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [selectedIndex, setSelectedIndex] = useState(1)\n        let [tabs, setTabs] = useState([0, 1, 2])\n\n        return (\n          <>\n            <button\n              onClick={() => {\n                setTabs((tabs) => tabs.slice().reverse())\n              }}\n            >\n              reverse\n            </button>\n            <Tab.Group selectedIndex={selectedIndex} onChange={setSelectedIndex}>\n              <Tab.List>\n                {tabs.map((tab) => (\n                  <Tab key={tab}>Tab {tab}</Tab>\n                ))}\n              </Tab.List>\n\n              <Tab.Panels>\n                {tabs.map((tab) => (\n                  <Tab.Panel key={tab}>Content {tab}</Tab.Panel>\n                ))}\n              </Tab.Panels>\n            </Tab.Group>\n            <p id=\"selectedIndex\">{selectedIndex}</p>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      let selectedIndexElement = document.getElementById('selectedIndex')\n\n      assertTabs({ active: 1 })\n\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed now\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed again now (back to normal)\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when reversing the tabs and panels themselves, then performing actions (uncontrolled component)',\n    suppressConsoleLogs(async () => {\n      function Example() {\n        let [tabs, setTabs] = useState([0, 1, 2])\n\n        return (\n          <>\n            <button\n              onClick={() => {\n                setTabs((tabs) => tabs.slice().reverse())\n              }}\n            >\n              reverse\n            </button>\n            <Tab.Group>\n              {({ selectedIndex }) => (\n                <>\n                  <Tab.List>\n                    {tabs.map((tab) => (\n                      <Tab key={tab}>Tab {tab}</Tab>\n                    ))}\n                  </Tab.List>\n\n                  <Tab.Panels>\n                    {tabs.map((tab) => (\n                      <Tab.Panel key={tab}>Content {tab}</Tab.Panel>\n                    ))}\n                  </Tab.Panels>\n\n                  <p id=\"selectedIndex\">{selectedIndex}</p>\n                </>\n              )}\n            </Tab.Group>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      let selectedIndexElement = document.getElementById('selectedIndex')\n\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed now\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed again now (back to normal)\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n    })\n  )\n\n  describe('`renderProps`', () => {\n    it(\n      'should be possible to render using as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              <Tab as={React.Fragment}>\n                <button>Tab 1</button>\n              </Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n              <Tab.Panel>Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        assertTabs({ active: 0, tabContents: 'Tab 1', panelContents: 'Content 1' })\n      })\n    )\n\n    it(\n      'should be possible to render using multiple as={Fragment}',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              <Tab as={React.Fragment}>\n                <button>Tab 1</button>\n              </Tab>\n              <Tab as={React.Fragment}>\n                <button>Tab 2</button>\n              </Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        assertTabs({ active: 0, tabContents: 'Tab 1', panelContents: 'Content 1' })\n      })\n    )\n\n    it(\n      'should expose the `selectedIndex` on the `Tab.Group` component',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            {(data) => (\n              <>\n                <pre id=\"exposed\">{JSON.stringify(data)}</pre>\n\n                <Tab.List>\n                  <Tab>Tab 1</Tab>\n                  <Tab>Tab 2</Tab>\n                  <Tab>Tab 3</Tab>\n                </Tab.List>\n\n                <Tab.Panels>\n                  <Tab.Panel>Content 1</Tab.Panel>\n                  <Tab.Panel>Content 2</Tab.Panel>\n                  <Tab.Panel>Content 3</Tab.Panel>\n                </Tab.Panels>\n              </>\n            )}\n          </Tab.Group>\n        )\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 0 })\n        )\n\n        await click(getByText('Tab 2'))\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 1 })\n        )\n      })\n    )\n\n    it(\n      'should expose the `selectedIndex` on the `Tab.List` component',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              {(data) => (\n                <>\n                  <pre id=\"exposed\">{JSON.stringify(data)}</pre>\n                  <Tab>Tab 1</Tab>\n                  <Tab>Tab 2</Tab>\n                  <Tab>Tab 3</Tab>\n                </>\n              )}\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n              <Tab.Panel>Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 0 })\n        )\n\n        await click(getByText('Tab 2'))\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 1 })\n        )\n      })\n    )\n\n    it(\n      'should expose the `selectedIndex` on the `Tab.Panels` component',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              {(data) => (\n                <>\n                  <pre id=\"exposed\">{JSON.stringify(data)}</pre>\n                  <Tab.Panel>Content 1</Tab.Panel>\n                  <Tab.Panel>Content 2</Tab.Panel>\n                  <Tab.Panel>Content 3</Tab.Panel>\n                </>\n              )}\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 0 })\n        )\n\n        await click(getByText('Tab 2'))\n\n        expect(document.getElementById('exposed')).toHaveTextContent(\n          JSON.stringify({ selectedIndex: 1 })\n        )\n      })\n    )\n\n    it(\n      'should expose the `selected` state on the `Tab` components',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              <Tab>\n                {(data) => (\n                  <>\n                    <pre data-tab={0}>{JSON.stringify(data)}</pre>\n                    <span>Tab 1</span>\n                  </>\n                )}\n              </Tab>\n              <Tab>\n                {(data) => (\n                  <>\n                    <pre data-tab={1}>{JSON.stringify(data)}</pre>\n                    <span>Tab 2</span>\n                  </>\n                )}\n              </Tab>\n              <Tab>\n                {(data) => (\n                  <>\n                    <pre data-tab={2}>{JSON.stringify(data)}</pre>\n                    <span>Tab 3</span>\n                  </>\n                )}\n              </Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n              <Tab.Panel>Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        expect(document.querySelector('[data-tab=\"0\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: true,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n        expect(document.querySelector('[data-tab=\"1\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: false,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n        expect(document.querySelector('[data-tab=\"2\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: false,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n\n        await click(getTabs()[1])\n\n        expect(document.querySelector('[data-tab=\"0\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: false,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n        expect(document.querySelector('[data-tab=\"1\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: true,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n        expect(document.querySelector('[data-tab=\"2\"]')).toHaveTextContent(\n          JSON.stringify({\n            selected: false,\n            hover: false,\n            active: false,\n            focus: false,\n            autofocus: false,\n            disabled: false,\n          })\n        )\n      })\n    )\n\n    it(\n      'should expose the `selected` state on the `Tab.Panel` components',\n      suppressConsoleLogs(async () => {\n        render(\n          <Tab.Group>\n            <Tab.List>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel unmount={false}>\n                {(data) => (\n                  <>\n                    <pre data-panel={0}>{JSON.stringify(data)}</pre>\n                    <span>Content 1</span>\n                  </>\n                )}\n              </Tab.Panel>\n              <Tab.Panel unmount={false}>\n                {(data) => (\n                  <>\n                    <pre data-panel={1}>{JSON.stringify(data)}</pre>\n                    <span>Content 2</span>\n                  </>\n                )}\n              </Tab.Panel>\n              <Tab.Panel unmount={false}>\n                {(data) => (\n                  <>\n                    <pre data-panel={2}>{JSON.stringify(data)}</pre>\n                    <span>Content 3</span>\n                  </>\n                )}\n              </Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        )\n\n        expect(document.querySelector('[data-panel=\"0\"]')).toHaveTextContent(\n          JSON.stringify({ selected: true, focus: false })\n        )\n        expect(document.querySelector('[data-panel=\"1\"]')).toHaveTextContent(\n          JSON.stringify({ selected: false, focus: false })\n        )\n        expect(document.querySelector('[data-panel=\"2\"]')).toHaveTextContent(\n          JSON.stringify({ selected: false, focus: false })\n        )\n\n        await click(getByText('Tab 2'))\n\n        expect(document.querySelector('[data-panel=\"0\"]')).toHaveTextContent(\n          JSON.stringify({ selected: false, focus: false })\n        )\n        expect(document.querySelector('[data-panel=\"1\"]')).toHaveTextContent(\n          JSON.stringify({ selected: true, focus: false })\n        )\n        expect(document.querySelector('[data-panel=\"2\"]')).toHaveTextContent(\n          JSON.stringify({ selected: false, focus: false })\n        )\n      })\n    )\n  })\n\n  describe('`defaultIndex`', () => {\n    it(\n      'should jump to the nearest tab when the defaultIndex is out of bounds (-2)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={-2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should jump to the nearest tab when the defaultIndex is out of bounds (+5)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={5}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 2 })\n        assertActiveElement(getByText('Tab 3'))\n      })\n    )\n\n    it(\n      'should jump to the next available tab when the defaultIndex is a disabled tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={0}>\n              <Tab.List>\n                <Tab disabled>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 1 })\n        assertActiveElement(getByText('Tab 2'))\n      })\n    )\n\n    it(\n      'should jump to the next available tab when the defaultIndex is a disabled tab and wrap around',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab disabled>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should not change the Tab if the defaultIndex changes',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [defaultIndex, setDefaultIndex] = useState(1)\n\n          return (\n            <>\n              <Tab.Group defaultIndex={defaultIndex}>\n                <Tab.List>\n                  <Tab>Tab 1</Tab>\n                  <Tab>Tab 2</Tab>\n                  <Tab>Tab 3</Tab>\n                </Tab.List>\n\n                <Tab.Panels>\n                  <Tab.Panel>Content 1</Tab.Panel>\n                  <Tab.Panel>Content 2</Tab.Panel>\n                  <Tab.Panel>Content 3</Tab.Panel>\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button>after</button>\n              <button onClick={() => setDefaultIndex(0)}>change</button>\n            </>\n          )\n        }\n\n        render(<Example />)\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 1 })\n        assertActiveElement(getByText('Tab 2'))\n\n        await click(getByText('Tab 3'))\n\n        assertTabs({ active: 2 })\n        assertActiveElement(getByText('Tab 3'))\n\n        // Change default index\n        await click(getByText('change'))\n\n        // Nothing should change...\n        assertTabs({ active: 2 })\n      })\n    )\n\n    it(\n      'should select first tab if no tabs were provided originally',\n      suppressConsoleLogs(async () => {\n        function Example({ defaultIndex = undefined }: { defaultIndex?: number } = {}) {\n          let [tabs, setTabs] = useState<string[]>([])\n\n          return (\n            <>\n              <Tab.Group defaultIndex={defaultIndex}>\n                <Tab.List>\n                  {tabs.map((tab, index) => (\n                    <Tab key={index}>{tab}</Tab>\n                  ))}\n                </Tab.List>\n                <Tab.Panels>\n                  {tabs.map((tab, index) => (\n                    <Tab.Panel key={index}>content: {tab}</Tab.Panel>\n                  ))}\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button onClick={() => setTabs(['tab 1', 'tab 2', 'tab 3'])}>change</button>\n            </>\n          )\n        }\n\n        render(<Example defaultIndex={0} />)\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should select first tab if no tabs were provided originally (with a defaultIndex of 1)',\n      suppressConsoleLogs(async () => {\n        function Example({ defaultIndex = undefined }: { defaultIndex?: number } = {}) {\n          let [tabs, setTabs] = useState<string[]>([])\n\n          return (\n            <>\n              <Tab.Group defaultIndex={defaultIndex}>\n                <Tab.List>\n                  {tabs.map((tab, index) => (\n                    <Tab key={index}>{tab}</Tab>\n                  ))}\n                </Tab.List>\n                <Tab.Panels>\n                  {tabs.map((tab, index) => (\n                    <Tab.Panel key={index}>content: {tab}</Tab.Panel>\n                  ))}\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button onClick={() => setTabs(['tab 1', 'tab 2', 'tab 3'])}>change</button>\n            </>\n          )\n        }\n\n        render(<Example defaultIndex={1} />)\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should select first tab if no tabs were provided originally (with a defaultIndex of 1)',\n      suppressConsoleLogs(async () => {\n        function Example({ defaultIndex = undefined }: { defaultIndex?: number } = {}) {\n          let [tabs, setTabs] = useState<string[]>([])\n\n          return (\n            <>\n              <Tab.Group defaultIndex={defaultIndex}>\n                <Tab.List>\n                  {tabs.map((tab, index) => (\n                    <Tab key={index}>{tab}</Tab>\n                  ))}\n                </Tab.List>\n                <Tab.Panels>\n                  {tabs.map((tab, index) => (\n                    <Tab.Panel key={index}>content: {tab}</Tab.Panel>\n                  ))}\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button onClick={() => setTabs(['tab 1', 'tab 2', 'tab 3'])}>change 1</button>\n              <button onClick={() => setTabs([])}>change 2</button>\n              <button onClick={() => setTabs(['tab 1', 'tab 2', 'tab 3'])}>change 3</button>\n            </>\n          )\n        }\n\n        render(<Example defaultIndex={1} />)\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change 1'))\n        await click(getByText('change 2'))\n        await click(getByText('change 3'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 1 })\n      })\n    )\n  })\n\n  describe('`selectedIndex`', () => {\n    it(\n      'should not change the tab in a controlled component if you do not respond to the onChange',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        function ControlledTabs() {\n          let [selectedIndex, setSelectedIndex] = useState(0)\n\n          return (\n            <>\n              <Tab.Group\n                selectedIndex={selectedIndex}\n                onChange={(value) => {\n                  handleChange(value)\n                }}\n              >\n                <Tab.List>\n                  <Tab>Tab 1</Tab>\n                  <Tab>Tab 2</Tab>\n                  <Tab>Tab 3</Tab>\n                </Tab.List>\n\n                <Tab.Panels>\n                  <Tab.Panel>Content 1</Tab.Panel>\n                  <Tab.Panel>Content 2</Tab.Panel>\n                  <Tab.Panel>Content 3</Tab.Panel>\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button>after</button>\n              <button onClick={() => setSelectedIndex((prev) => prev + 1)}>setSelectedIndex</button>\n            </>\n          )\n        }\n\n        render(<ControlledTabs />)\n\n        assertActiveElement(document.body)\n\n        // test controlled behavior\n        await click(getByText('setSelectedIndex'))\n        assertTabs({ active: 1 })\n        await click(getByText('setSelectedIndex'))\n        assertTabs({ active: 2 })\n\n        // test uncontrolled behavior again\n        await click(getByText('Tab 1'))\n        assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n        await click(getByText('Tab 2'))\n        assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n        await click(getByText('Tab 3'))\n        assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n        await click(getByText('Tab 1'))\n        expect(handleChange).toHaveBeenCalledTimes(3) // We did see the 'onChange' calls, but only 3 because clicking Tab 3 is already the active one which means that this doesn't trigger the onChange\n        assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n      })\n    )\n\n    it(\n      'should be possible to change active tab controlled and uncontrolled',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        function ControlledTabs() {\n          let [selectedIndex, setSelectedIndex] = useState(0)\n\n          return (\n            <>\n              <Tab.Group\n                selectedIndex={selectedIndex}\n                onChange={(value) => {\n                  setSelectedIndex(value)\n                  handleChange(value)\n                }}\n              >\n                <Tab.List>\n                  <Tab>Tab 1</Tab>\n                  <Tab>Tab 2</Tab>\n                  <Tab>Tab 3</Tab>\n                </Tab.List>\n\n                <Tab.Panels>\n                  <Tab.Panel>Content 1</Tab.Panel>\n                  <Tab.Panel>Content 2</Tab.Panel>\n                  <Tab.Panel>Content 3</Tab.Panel>\n                </Tab.Panels>\n              </Tab.Group>\n\n              <button>after</button>\n              <button onClick={() => setSelectedIndex((prev) => prev + 1)}>setSelectedIndex</button>\n            </>\n          )\n        }\n\n        render(<ControlledTabs />)\n\n        assertActiveElement(document.body)\n\n        // test uncontrolled behavior\n        await click(getByText('Tab 2'))\n        expect(handleChange).toHaveBeenCalledTimes(1)\n        expect(handleChange).toHaveBeenNthCalledWith(1, 1)\n        assertTabs({ active: 1 })\n\n        // test controlled behavior\n        await click(getByText('setSelectedIndex'))\n        assertTabs({ active: 2 })\n\n        // test uncontrolled behavior again\n        await click(getByText('Tab 2'))\n        expect(handleChange).toHaveBeenCalledTimes(2)\n        expect(handleChange).toHaveBeenNthCalledWith(2, 1)\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should jump to the nearest tab when the selectedIndex is out of bounds (-2)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group selectedIndex={-2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should jump to the nearest tab when the selectedIndex is out of bounds (+5)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group selectedIndex={5}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 2 })\n        assertActiveElement(getByText('Tab 3'))\n      })\n    )\n\n    it(\n      'should jump to the next available tab when the selectedIndex is a disabled tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group selectedIndex={0}>\n              <Tab.List>\n                <Tab disabled>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 1 })\n        assertActiveElement(getByText('Tab 2'))\n      })\n    )\n\n    it(\n      'should jump to the next available tab when the selectedIndex is a disabled tab and wrap around',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab disabled>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should prefer selectedIndex over defaultIndex',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group selectedIndex={0} defaultIndex={2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should wrap around when overflowing the index when using a controlled component',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [selectedIndex, setSelectedIndex] = useState(0)\n\n          return (\n            <Tab.Group selectedIndex={selectedIndex} onChange={setSelectedIndex}>\n              {({ selectedIndex }) => (\n                <>\n                  <Tab.List>\n                    <Tab>Tab 1</Tab>\n                    <Tab>Tab 2</Tab>\n                    <Tab>Tab 3</Tab>\n                  </Tab.List>\n                  <Tab.Panels>\n                    <Tab.Panel>Content 1</Tab.Panel>\n                    <Tab.Panel>Content 2</Tab.Panel>\n                    <Tab.Panel>Content 3</Tab.Panel>\n                  </Tab.Panels>\n                  <button onClick={() => setSelectedIndex(selectedIndex + 1)}>Next</button>\n                </>\n              )}\n            </Tab.Group>\n          )\n        }\n        render(<Example />)\n\n        assertActiveElement(document.body)\n\n        await click(getByText('Next'))\n        assertTabs({ active: 1 })\n\n        await click(getByText('Next'))\n        assertTabs({ active: 2 })\n\n        await click(getByText('Next'))\n        assertTabs({ active: 0 })\n\n        await click(getByText('Next'))\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should wrap around when underflowing the index when using a controlled component',\n      suppressConsoleLogs(async () => {\n        function Example() {\n          let [selectedIndex, setSelectedIndex] = useState(0)\n\n          return (\n            <Tab.Group selectedIndex={selectedIndex} onChange={setSelectedIndex}>\n              {({ selectedIndex }) => (\n                <>\n                  <Tab.List>\n                    <Tab>Tab 1</Tab>\n                    <Tab>Tab 2</Tab>\n                    <Tab>Tab 3</Tab>\n                  </Tab.List>\n                  <Tab.Panels>\n                    <Tab.Panel>Content 1</Tab.Panel>\n                    <Tab.Panel>Content 2</Tab.Panel>\n                    <Tab.Panel>Content 3</Tab.Panel>\n                  </Tab.Panels>\n                  <button onClick={() => setSelectedIndex(selectedIndex - 1)}>Previous</button>\n                </>\n              )}\n            </Tab.Group>\n          )\n        }\n        render(<Example />)\n\n        assertActiveElement(document.body)\n\n        await click(getByText('Previous'))\n        assertTabs({ active: 2 })\n\n        await click(getByText('Previous'))\n        assertTabs({ active: 1 })\n\n        await click(getByText('Previous'))\n        assertTabs({ active: 0 })\n\n        await click(getByText('Previous'))\n        assertTabs({ active: 2 })\n      })\n    )\n  })\n\n  describe(`'Tab'`, () => {\n    describe('`type` attribute', () => {\n      it(\n        'should set the `type` to \"button\" by default',\n        suppressConsoleLogs(async () => {\n          render(\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Trigger</Tab>\n              </Tab.List>\n            </Tab.Group>\n          )\n\n          expect(getTabs()[0]).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it(\n        'should not set the `type` to \"button\" if it already contains a `type`',\n        suppressConsoleLogs(async () => {\n          render(\n            <Tab.Group>\n              <Tab.List>\n                <Tab type=\"submit\">Trigger</Tab>\n              </Tab.List>\n            </Tab.Group>\n          )\n\n          expect(getTabs()[0]).toHaveAttribute('type', 'submit')\n        })\n      )\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          let CustomButton = React.forwardRef<HTMLButtonElement>((props, ref) => (\n            <button ref={ref} {...props} />\n          ))\n\n          render(\n            <Tab.Group>\n              <Tab.List>\n                <Tab as={CustomButton}>Trigger</Tab>\n              </Tab.List>\n            </Tab.Group>\n          )\n\n          expect(getTabs()[0]).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it(\n        'should not set the type if the \"as\" prop is not a \"button\"',\n        suppressConsoleLogs(async () => {\n          render(\n            <Tab.Group>\n              <Tab.List>\n                <Tab as=\"div\">Trigger</Tab>\n              </Tab.List>\n            </Tab.Group>\n          )\n\n          expect(getTabs()[0]).not.toHaveAttribute('type')\n        })\n      )\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          let CustomButton = React.forwardRef<HTMLDivElement>((props, ref) => (\n            <div ref={ref} {...props} />\n          ))\n\n          render(\n            <Tab.Group>\n              <Tab.List>\n                <Tab as={CustomButton}>Trigger</Tab>\n              </Tab.List>\n            </Tab.Group>\n          )\n\n          expect(getTabs()[0]).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to tab to the default initial first tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 0 })\n        assertActiveElement(getByText('Tab 1'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Content 1'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('after'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Content 1'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Tab 1'))\n      })\n    )\n\n    it(\n      'should be possible to tab to the default index tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n\n        assertTabs({ active: 1 })\n        assertActiveElement(getByText('Tab 2'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Content 2'))\n\n        await press(Keys.Tab)\n        assertActiveElement(getByText('after'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Content 2'))\n\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Tab 2'))\n      })\n    )\n  })\n\n  describe('`ArrowRight` key', () => {\n    it(\n      'should be possible to go to the next item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 2 })\n      })\n    )\n\n    it(\n      'should be possible to go to the next item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n      })\n    )\n\n    it(\n      'should wrap around at the end (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should wrap around at the end (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 2 })\n        await press(Keys.Enter)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should not be possible to go right when in vertical mode (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowRight)\n        // no-op\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should not be possible to go right when in vertical mode (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowRight)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n        // no-op\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it(\n      'should be possible to go to the previous item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should be possible to go to the previous item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 2 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should wrap around at the beginning (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should wrap around at the beginning (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 2 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 2 })\n        await press(Keys.Enter)\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should not be possible to go left when in vertical mode (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowLeft)\n        // no-op\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should not be possible to go left when in vertical mode (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowLeft)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n\n        // no-op\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n  })\n\n  describe('`ArrowDown` key', () => {\n    it(\n      'should be possible to go to the next item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 2, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should be possible to go to the next item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 1, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 2, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should wrap around at the end (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 1, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should wrap around at the end (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 1, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 2, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should not be possible to go down when in horizontal mode (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowDown)\n        // no-op\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should not be possible to go down when in horizontal mode (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowDown)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n\n        // no-op\n        assertTabs({ active: 0 })\n      })\n    )\n  })\n\n  describe('`ArrowUp` key', () => {\n    it(\n      'should be possible to go to the previous item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should be possible to go to the previous item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 2, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 1, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 0, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should wrap around at the beginning (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} vertical>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 1, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should wrap around at the beginning (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={2} vertical manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 2, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 1, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 0, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 0, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 2, orientation: 'vertical' })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 2, orientation: 'vertical' })\n        await press(Keys.Enter)\n        assertTabs({ active: 1, orientation: 'vertical' })\n      })\n    )\n\n    it(\n      'should not be possible to go left when in vertical mode (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowUp)\n        // no-op\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should not be possible to go left when in vertical mode (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 0 })\n\n        await press(Keys.ArrowUp)\n        assertTabs({ active: 0 })\n        await press(Keys.Enter)\n\n        // no-op\n        assertTabs({ active: 0 })\n      })\n    )\n  })\n\n  describe('`Home` key', () => {\n    it(\n      'should be possible to go to the first focusable item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.Home)\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should be possible to go to the first focusable item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.Home)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 0 })\n      })\n    )\n  })\n\n  describe('`PageUp` key', () => {\n    it(\n      'should be possible to go to the first focusable item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.PageUp)\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should be possible to go to the first focusable item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.PageUp)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 0 })\n      })\n    )\n  })\n\n  describe('`End` key', () => {\n    it(\n      'should be possible to go to the first focusable item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.End)\n        assertTabs({ active: 2 })\n      })\n    )\n\n    it(\n      'should be possible to go to the first focusable item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.End)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n      })\n    )\n  })\n\n  describe('`PageDown` key', () => {\n    it(\n      'should be possible to go to the first focusable item (activation = `auto`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1}>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.PageDown)\n        assertTabs({ active: 2 })\n      })\n    )\n\n    it(\n      'should be possible to go to the first focusable item (activation = `manual`)',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group defaultIndex={1} manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        await press(Keys.Tab)\n        assertTabs({ active: 1 })\n\n        await press(Keys.PageDown)\n        assertTabs({ active: 1 })\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n      })\n    )\n  })\n\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to activate the focused tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        getByText('Tab 3')?.focus()\n\n        assertActiveElement(getByText('Tab 3'))\n        assertTabs({ active: 0 })\n\n        await press(Keys.Enter)\n        assertTabs({ active: 2 })\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to activate the focused tab',\n      suppressConsoleLogs(async () => {\n        render(\n          <>\n            <Tab.Group manual>\n              <Tab.List>\n                <Tab>Tab 1</Tab>\n                <Tab>Tab 2</Tab>\n                <Tab>Tab 3</Tab>\n              </Tab.List>\n\n              <Tab.Panels>\n                <Tab.Panel>Content 1</Tab.Panel>\n                <Tab.Panel>Content 2</Tab.Panel>\n                <Tab.Panel>Content 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n\n            <button>after</button>\n          </>\n        )\n\n        assertActiveElement(document.body)\n\n        getByText('Tab 3')?.focus()\n\n        assertActiveElement(getByText('Tab 3'))\n        assertTabs({ active: 0 })\n\n        await press(Keys.Space)\n        assertTabs({ active: 2 })\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to click on a tab to focus it',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <Tab.Group defaultIndex={1}>\n            <Tab.List>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n              <Tab.Panel>Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n\n          <button>after</button>\n        </>\n      )\n\n      assertActiveElement(document.body)\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 0 })\n\n      await click(getByText('Tab 3'))\n      assertTabs({ active: 2 })\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 1 })\n    })\n  )\n\n  it(\n    'should be a no-op when clicking on a disabled tab',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <Tab.Group defaultIndex={1}>\n            <Tab.List>\n              <Tab disabled>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel>Content 1</Tab.Panel>\n              <Tab.Panel>Content 2</Tab.Panel>\n              <Tab.Panel>Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n\n          <button>after</button>\n        </>\n      )\n\n      assertActiveElement(document.body)\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await click(getByText('Tab 1'))\n      // No-op, Tab 2 is still active\n      assertTabs({ active: 1 })\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  it(\n    'should be possible to go to the next item containing a Dialog component',\n    suppressConsoleLogs(async () => {\n      render(\n        <>\n          <Tab.Group>\n            <Tab.List>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </Tab.List>\n\n            <Tab.Panels>\n              <Tab.Panel data-panel=\"0\">Content 1</Tab.Panel>\n              <Tab.Panel data-panel=\"1\">\n                <>\n                  <button>open</button>\n                  <Dialog open={false} onClose={console.log} />\n                </>\n              </Tab.Panel>\n              <Tab.Panel data-panel=\"2\">Content 3</Tab.Panel>\n            </Tab.Panels>\n          </Tab.Group>\n        </>\n      )\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      // Navigate to Dialog tab\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n\n      // Focus on to the Dialog panel\n      await press(Keys.Tab)\n      assertActiveElement(document.querySelector('[data-panel=\"1\"]'))\n\n      // Focus on to the Dialog trigger button\n      await press(Keys.Tab)\n      assertActiveElement(getByText('open'))\n\n      // Focus back to the panel\n      await press(shift(Keys.Tab))\n      assertActiveElement(document.querySelector('[data-panel=\"1\"]'))\n\n      // Focus back to tabs\n      await press(shift(Keys.Tab))\n      assertTabs({ active: 1 })\n\n      // Navigate to the next tab\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n\n      // Focus on to the content panel\n      await press(Keys.Tab)\n      assertActiveElement(document.querySelector('[data-panel=\"2\"]'))\n    })\n  )\n})\n\nit(\n  'should trigger the `onChange` when the tab changes',\n  suppressConsoleLogs(async () => {\n    let changes = jest.fn()\n\n    render(\n      <>\n        <Tab.Group onChange={changes}>\n          <Tab.List>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </Tab.List>\n\n          <Tab.Panels>\n            <Tab.Panel>Content 1</Tab.Panel>\n            <Tab.Panel>Content 2</Tab.Panel>\n            <Tab.Panel>Content 3</Tab.Panel>\n          </Tab.Panels>\n        </Tab.Group>\n\n        <button>after</button>\n      </>\n    )\n\n    await click(getByText('Tab 2'))\n    await click(getByText('Tab 3'))\n    await click(getByText('Tab 2'))\n    await click(getByText('Tab 1'))\n\n    expect(changes).toHaveBeenCalledTimes(4)\n\n    expect(changes).toHaveBeenNthCalledWith(1, 1)\n    expect(changes).toHaveBeenNthCalledWith(2, 2)\n    expect(changes).toHaveBeenNthCalledWith(3, 1)\n    expect(changes).toHaveBeenNthCalledWith(4, 0)\n  })\n)\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tabs/tabs.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  createContext,\n  useContext,\n  useMemo,\n  useReducer,\n  useRef,\n  useState,\n  type ElementType,\n  type MutableRefObject,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type Ref,\n} from 'react'\nimport { useActivePress } from '../../hooks/use-active-press'\nimport { useEvent } from '../../hooks/use-event'\nimport { useId } from '../../hooks/use-id'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { FocusSentinel } from '../../internal/focus-sentinel'\nimport { Hidden } from '../../internal/hidden'\nimport type { Props } from '../../types'\nimport { Focus, FocusResult, focusIn, sortByDomNode } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { getActiveElement } from '../../utils/owner'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { StableCollection, useStableCollectionIndex } from '../../utils/stable-collection'\nimport { Keys } from '../keyboard'\n\nenum Direction {\n  Forwards,\n  Backwards,\n}\n\nenum Ordering {\n  Less = -1,\n  Equal = 0,\n  Greater = 1,\n}\n\ninterface StateDefinition {\n  info: MutableRefObject<{ isControlled: boolean }>\n  selectedIndex: number\n\n  tabs: MutableRefObject<HTMLElement | null>[]\n  panels: MutableRefObject<HTMLElement | null>[]\n}\n\nenum ActionTypes {\n  SetSelectedIndex,\n\n  RegisterTab,\n  UnregisterTab,\n\n  RegisterPanel,\n  UnregisterPanel,\n}\n\ntype Actions =\n  | { type: ActionTypes.SetSelectedIndex; index: number }\n  | { type: ActionTypes.RegisterTab; tab: MutableRefObject<HTMLElement | null> }\n  | { type: ActionTypes.UnregisterTab; tab: MutableRefObject<HTMLElement | null> }\n  | { type: ActionTypes.RegisterPanel; panel: MutableRefObject<HTMLElement | null> }\n  | { type: ActionTypes.UnregisterPanel; panel: MutableRefObject<HTMLElement | null> }\n\nlet reducers: {\n  [P in ActionTypes]: (\n    state: StateDefinition,\n    action: Extract<Actions, { type: P }>\n  ) => StateDefinition\n} = {\n  [ActionTypes.SetSelectedIndex](state, action) {\n    let tabs = sortByDomNode(state.tabs, (tab) => tab.current)\n    let panels = sortByDomNode(state.panels, (panel) => panel.current)\n\n    let focusableTabs = tabs.filter((tab) => !tab.current?.hasAttribute('disabled'))\n\n    let nextState = { ...state, tabs, panels }\n\n    if (\n      // Underflow\n      action.index < 0 ||\n      // Overflow\n      action.index > tabs.length - 1\n    ) {\n      let direction = match(Math.sign(action.index - state.selectedIndex), {\n        [Ordering.Less]: () => Direction.Backwards,\n        [Ordering.Equal]: () => {\n          return match(Math.sign(action.index), {\n            [Ordering.Less]: () => Direction.Forwards,\n            [Ordering.Equal]: () => Direction.Forwards,\n            [Ordering.Greater]: () => Direction.Backwards,\n          })\n        },\n        [Ordering.Greater]: () => Direction.Forwards,\n      })\n\n      // If there are no focusable tabs then.\n      // We won't change the selected index\n      // because it's likely the user is\n      // lazy loading tabs and there's\n      // nothing to focus on yet\n      if (focusableTabs.length === 0) {\n        return nextState\n      }\n\n      let nextSelectedIndex = match(direction, {\n        [Direction.Forwards]: () => tabs.indexOf(focusableTabs[0]),\n        [Direction.Backwards]: () => tabs.indexOf(focusableTabs[focusableTabs.length - 1]),\n      })\n\n      return {\n        ...nextState,\n        selectedIndex: nextSelectedIndex === -1 ? state.selectedIndex : nextSelectedIndex,\n      }\n    }\n\n    // Middle\n    let before = tabs.slice(0, action.index)\n    let after = tabs.slice(action.index)\n\n    let next = [...after, ...before].find((tab) => focusableTabs.includes(tab))\n    if (!next) return nextState\n\n    let selectedIndex = tabs.indexOf(next) ?? state.selectedIndex\n    if (selectedIndex === -1) selectedIndex = state.selectedIndex\n\n    return { ...nextState, selectedIndex }\n  },\n  [ActionTypes.RegisterTab](state, action) {\n    if (state.tabs.includes(action.tab)) return state\n    let activeTab = state.tabs[state.selectedIndex]\n\n    let adjustedTabs = sortByDomNode([...state.tabs, action.tab], (tab) => tab.current)\n    let selectedIndex = state.selectedIndex\n\n    // When the component is uncontrolled, then we want to maintain the actively\n    // selected tab even if new tabs are inserted or removed before the active\n    // tab.\n    //\n    // When the component is controlled, then we don't want to do this and\n    // instead we want to select the tab based on the `selectedIndex` prop.\n    if (!state.info.current.isControlled) {\n      selectedIndex = adjustedTabs.indexOf(activeTab)\n      if (selectedIndex === -1) selectedIndex = state.selectedIndex\n    }\n\n    return { ...state, tabs: adjustedTabs, selectedIndex }\n  },\n  [ActionTypes.UnregisterTab](state, action) {\n    return { ...state, tabs: state.tabs.filter((tab) => tab !== action.tab) }\n  },\n  [ActionTypes.RegisterPanel](state, action) {\n    if (state.panels.includes(action.panel)) return state\n    return {\n      ...state,\n      panels: sortByDomNode([...state.panels, action.panel], (panel) => panel.current),\n    }\n  },\n  [ActionTypes.UnregisterPanel](state, action) {\n    return { ...state, panels: state.panels.filter((panel) => panel !== action.panel) }\n  },\n}\n\nlet TabsDataContext = createContext<\n  | ({\n      orientation: 'horizontal' | 'vertical'\n      activation: 'auto' | 'manual'\n    } & StateDefinition)\n  | null\n>(null)\nTabsDataContext.displayName = 'TabsDataContext'\n\nfunction useData(component: string) {\n  let context = useContext(TabsDataContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Tab.Group /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useData)\n    throw err\n  }\n  return context\n}\ntype _Data = ReturnType<typeof useData>\n\nlet TabsActionsContext = createContext<{\n  registerTab(tab: MutableRefObject<HTMLElement | null>): () => void\n  registerPanel(panel: MutableRefObject<HTMLElement | null>): () => void\n  change(index: number): void\n} | null>(null)\nTabsActionsContext.displayName = 'TabsActionsContext'\n\nfunction useActions(component: string) {\n  let context = useContext(TabsActionsContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Tab.Group /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useActions)\n    throw err\n  }\n  return context\n}\ntype _Actions = ReturnType<typeof useActions>\n\nfunction stateReducer(state: StateDefinition, action: Actions) {\n  return match(action.type, reducers, state, action)\n}\n\n// ---\n\nlet DEFAULT_TABS_TAG = 'div' as const\ntype TabsRenderPropArg = {\n  selectedIndex: number\n}\ntype TabsPropsWeControl = never\n\nexport type TabGroupProps<TTag extends ElementType = typeof DEFAULT_TABS_TAG> = Props<\n  TTag,\n  TabsRenderPropArg,\n  TabsPropsWeControl,\n  {\n    defaultIndex?: number\n    onChange?: (index: number) => void\n    selectedIndex?: number\n    vertical?: boolean\n    manual?: boolean\n  }\n>\n\nfunction GroupFn<TTag extends ElementType = typeof DEFAULT_TABS_TAG>(\n  props: TabGroupProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let {\n    defaultIndex = 0,\n    vertical = false,\n    manual = false,\n    onChange,\n    selectedIndex = null,\n    ...theirProps\n  } = props\n  const orientation = vertical ? 'vertical' : 'horizontal'\n  const activation = manual ? 'manual' : 'auto'\n\n  let isControlled = selectedIndex !== null\n\n  let info = useLatestValue({ isControlled })\n\n  let tabsRef = useSyncRefs(ref)\n  let [state, dispatch] = useReducer(stateReducer, {\n    info,\n    selectedIndex: selectedIndex ?? defaultIndex,\n    tabs: [],\n    panels: [],\n  })\n  let slot = useSlot<TabsRenderPropArg>({ selectedIndex: state.selectedIndex })\n  let onChangeRef = useLatestValue(onChange || (() => {}))\n  let stableTabsRef = useLatestValue(state.tabs)\n\n  let tabsData = useMemo<_Data>(\n    () => ({ orientation, activation, ...state }),\n    [orientation, activation, state]\n  )\n\n  let registerTab = useEvent((tab) => {\n    dispatch({ type: ActionTypes.RegisterTab, tab })\n    return () => dispatch({ type: ActionTypes.UnregisterTab, tab })\n  })\n\n  let registerPanel = useEvent((panel) => {\n    dispatch({ type: ActionTypes.RegisterPanel, panel })\n    return () => dispatch({ type: ActionTypes.UnregisterPanel, panel })\n  })\n\n  let change = useEvent((index: number) => {\n    if (realSelectedIndex.current !== index) {\n      onChangeRef.current(index)\n    }\n\n    if (!isControlled) {\n      dispatch({ type: ActionTypes.SetSelectedIndex, index })\n    }\n  })\n\n  let realSelectedIndex = useLatestValue(isControlled ? props.selectedIndex : state.selectedIndex)\n  let tabsActions = useMemo<_Actions>(() => ({ registerTab, registerPanel, change }), [])\n\n  useIsoMorphicEffect(() => {\n    dispatch({ type: ActionTypes.SetSelectedIndex, index: selectedIndex ?? defaultIndex })\n  }, [selectedIndex /* Deliberately skipping defaultIndex */])\n\n  useIsoMorphicEffect(() => {\n    if (realSelectedIndex.current === undefined) return\n    if (state.tabs.length <= 0) return\n\n    // TODO: Figure out a way to detect this without the slow sort on every render. Might be fine\n    //       unless you have a lot of tabs.\n    let sorted = sortByDomNode(state.tabs, (tab) => tab.current)\n    let didOrderChange = sorted.some((tab, i) => state.tabs[i] !== tab)\n\n    if (didOrderChange) {\n      change(sorted.indexOf(state.tabs[realSelectedIndex.current]))\n    }\n  })\n\n  let ourProps = { ref: tabsRef }\n\n  let render = useRender()\n\n  return (\n    <StableCollection>\n      <TabsActionsContext.Provider value={tabsActions}>\n        <TabsDataContext.Provider value={tabsData}>\n          {tabsData.tabs.length <= 0 && (\n            <FocusSentinel\n              onFocus={() => {\n                for (let tab of stableTabsRef.current) {\n                  if (tab.current?.tabIndex === 0) {\n                    tab.current?.focus()\n                    return true\n                  }\n                }\n\n                return false\n              }}\n            />\n          )}\n          {render({\n            ourProps,\n            theirProps,\n            slot,\n            defaultTag: DEFAULT_TABS_TAG,\n            name: 'Tabs',\n          })}\n        </TabsDataContext.Provider>\n      </TabsActionsContext.Provider>\n    </StableCollection>\n  )\n}\n\n// ---\n\nlet DEFAULT_LIST_TAG = 'div' as const\ntype ListRenderPropArg = {\n  selectedIndex: number\n}\ntype ListPropsWeControl = 'aria-orientation' | 'role'\n\nexport type TabListProps<TTag extends ElementType = typeof DEFAULT_LIST_TAG> = Props<\n  TTag,\n  ListRenderPropArg,\n  ListPropsWeControl,\n  {\n    //\n  }\n>\n\nfunction ListFn<TTag extends ElementType = typeof DEFAULT_LIST_TAG>(\n  props: TabListProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { orientation, selectedIndex } = useData('Tab.List')\n  let listRef = useSyncRefs(ref)\n\n  let slot = useSlot<ListRenderPropArg>({ selectedIndex })\n\n  let theirProps = props\n  let ourProps = {\n    ref: listRef,\n    role: 'tablist',\n    'aria-orientation': orientation,\n  }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_LIST_TAG,\n    name: 'Tabs.List',\n  })\n}\n\n// ---\n\nlet DEFAULT_TAB_TAG = 'button' as const\ntype TabRenderPropArg = {\n  hover: boolean\n  focus: boolean\n  active: boolean\n  autofocus: boolean\n  selected: boolean\n  disabled: boolean\n}\ntype TabPropsWeControl = 'aria-controls' | 'aria-selected' | 'role' | 'tabIndex'\n\nexport type TabProps<TTag extends ElementType = typeof DEFAULT_TAB_TAG> = Props<\n  TTag,\n  TabRenderPropArg,\n  TabPropsWeControl,\n  {\n    autoFocus?: boolean\n    disabled?: boolean\n  }\n>\n\nfunction TabFn<TTag extends ElementType = typeof DEFAULT_TAB_TAG>(\n  props: TabProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let {\n    id = `headlessui-tabs-tab-${internalId}`,\n    disabled = false,\n    autoFocus = false,\n    ...theirProps\n  } = props\n\n  let { orientation, activation, selectedIndex, tabs, panels } = useData('Tab')\n  let actions = useActions('Tab')\n  let data = useData('Tab')\n\n  let [tabElement, setTabElement] = useState<HTMLElement | null>(null)\n  let internalTabRef = useRef<HTMLElement | null>(null)\n  let tabRef = useSyncRefs(internalTabRef, ref, setTabElement)\n\n  useIsoMorphicEffect(() => actions.registerTab(internalTabRef), [actions, internalTabRef])\n\n  let mySSRIndex = useStableCollectionIndex('tabs')\n\n  let myIndex = tabs.indexOf(internalTabRef)\n  if (myIndex === -1) myIndex = mySSRIndex\n  let selected = myIndex === selectedIndex\n\n  let activateUsing = useEvent((cb: () => FocusResult) => {\n    let result = cb()\n    if (result === FocusResult.Success && activation === 'auto') {\n      let newTab = getActiveElement(internalTabRef.current)\n      let idx = data.tabs.findIndex((tab) => tab.current === newTab)\n      if (idx !== -1) actions.change(idx)\n    }\n    return result\n  })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLElement>) => {\n    let list = tabs.map((tab) => tab.current).filter(Boolean) as HTMLElement[]\n\n    if (event.key === Keys.Space || event.key === Keys.Enter) {\n      event.preventDefault()\n      event.stopPropagation()\n\n      actions.change(myIndex)\n      return\n    }\n\n    switch (event.key) {\n      case Keys.Home:\n      case Keys.PageUp:\n        event.preventDefault()\n        event.stopPropagation()\n\n        return activateUsing(() => focusIn(list, Focus.First))\n\n      case Keys.End:\n      case Keys.PageDown:\n        event.preventDefault()\n        event.stopPropagation()\n\n        return activateUsing(() => focusIn(list, Focus.Last))\n    }\n\n    let result = activateUsing(() => {\n      return match(orientation, {\n        vertical() {\n          if (event.key === Keys.ArrowUp) return focusIn(list, Focus.Previous | Focus.WrapAround)\n          if (event.key === Keys.ArrowDown) return focusIn(list, Focus.Next | Focus.WrapAround)\n          return FocusResult.Error\n        },\n        horizontal() {\n          if (event.key === Keys.ArrowLeft) return focusIn(list, Focus.Previous | Focus.WrapAround)\n          if (event.key === Keys.ArrowRight) return focusIn(list, Focus.Next | Focus.WrapAround)\n          return FocusResult.Error\n        },\n      })\n    })\n\n    if (result === FocusResult.Success) {\n      return event.preventDefault()\n    }\n  })\n\n  let ready = useRef(false)\n  let handleSelection = useEvent(() => {\n    if (ready.current) return\n    ready.current = true\n\n    internalTabRef.current?.focus({ preventScroll: true })\n    actions.change(myIndex)\n\n    microTask(() => {\n      ready.current = false\n    })\n  })\n\n  // This is important because we want to only focus the tab when it gets focus\n  // OR it finished the click event (mouseup). However, if you perform a `click`,\n  // then you will first get the `focus` and then get the `click` event.\n  let handleMouseDown = useEvent((event: ReactMouseEvent<HTMLElement>) => {\n    event.preventDefault()\n  })\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n  let { pressed: active, pressProps } = useActivePress({ disabled })\n\n  let slot = useSlot<TabRenderPropArg>({\n    selected,\n    hover,\n    active,\n    focus,\n    autofocus: autoFocus,\n    disabled,\n  })\n\n  let ourProps = mergeProps(\n    {\n      ref: tabRef,\n      onKeyDown: handleKeyDown,\n      onMouseDown: handleMouseDown,\n      onClick: handleSelection,\n      id,\n      role: 'tab',\n      type: useResolveButtonType(props, tabElement),\n      'aria-controls': panels[myIndex]?.current?.id,\n      'aria-selected': selected,\n      tabIndex: selected ? 0 : -1,\n      disabled: disabled || undefined,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps,\n    pressProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_TAB_TAG,\n    name: 'Tabs.Tab',\n  })\n}\n\n// ---\n\nlet DEFAULT_PANELS_TAG = 'div' as const\ntype PanelsRenderPropArg = {\n  selectedIndex: number\n}\n\nexport type TabPanelsProps<TTag extends ElementType = typeof DEFAULT_PANELS_TAG> = Props<\n  TTag,\n  PanelsRenderPropArg\n>\n\nfunction PanelsFn<TTag extends ElementType = typeof DEFAULT_PANELS_TAG>(\n  props: TabPanelsProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { selectedIndex } = useData('Tab.Panels')\n  let panelsRef = useSyncRefs(ref)\n\n  let slot = useSlot<PanelsRenderPropArg>({ selectedIndex })\n\n  let theirProps = props\n  let ourProps = { ref: panelsRef }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_PANELS_TAG,\n    name: 'Tabs.Panels',\n  })\n}\n\n// ---\n\nlet DEFAULT_PANEL_TAG = 'div' as const\ntype PanelRenderPropArg = {\n  selected: boolean\n  focus: boolean\n}\ntype PanelPropsWeControl = 'role' | 'aria-labelledby'\nlet PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type TabPanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = Props<\n  TTag,\n  PanelRenderPropArg,\n  PanelPropsWeControl,\n  PropsForFeatures<typeof PanelRenderFeatures> & { id?: string; tabIndex?: number }\n>\n\nfunction PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n  props: TabPanelProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let { id = `headlessui-tabs-panel-${internalId}`, tabIndex = 0, ...theirProps } = props\n  let { selectedIndex, tabs, panels } = useData('Tab.Panel')\n  let actions = useActions('Tab.Panel')\n\n  let internalPanelRef = useRef<HTMLElement | null>(null)\n  let panelRef = useSyncRefs(internalPanelRef, ref)\n\n  useIsoMorphicEffect(() => actions.registerPanel(internalPanelRef), [actions, internalPanelRef])\n\n  let mySSRIndex = useStableCollectionIndex('panels')\n\n  let myIndex = panels.indexOf(internalPanelRef)\n  if (myIndex === -1) myIndex = mySSRIndex\n\n  let selected = myIndex === selectedIndex\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing()\n  let slot = useSlot<PanelRenderPropArg>({ selected, focus })\n\n  let ourProps = mergeProps(\n    {\n      ref: panelRef,\n      id,\n      role: 'tabpanel',\n      'aria-labelledby': tabs[myIndex]?.current?.id,\n      tabIndex: selected ? tabIndex : -1,\n    },\n    focusProps\n  )\n\n  let render = useRender()\n\n  if (!selected && (theirProps.unmount ?? true) && !(theirProps.static ?? false)) {\n    return <Hidden aria-hidden=\"true\" {...ourProps} />\n  }\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_PANEL_TAG,\n    features: PanelRenderFeatures,\n    visible: selected,\n    name: 'Tabs.Panel',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentTab extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TAB_TAG>(\n    props: TabProps<TTag> & RefProp<typeof TabFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTabGroup extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TABS_TAG>(\n    props: TabGroupProps<TTag> & RefProp<typeof GroupFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTabList extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_LIST_TAG>(\n    props: TabListProps<TTag> & RefProp<typeof ListFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTabPanels extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANELS_TAG>(\n    props: TabPanelsProps<TTag> & RefProp<typeof PanelsFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTabPanel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n    props: TabPanelProps<TTag> & RefProp<typeof PanelFn>\n  ): React.JSX.Element\n}\n\nlet TabRoot = forwardRefWithAs(TabFn) as _internal_ComponentTab\nexport let TabGroup = forwardRefWithAs(GroupFn) as _internal_ComponentTabGroup\nexport let TabList = forwardRefWithAs(ListFn) as _internal_ComponentTabList\nexport let TabPanels = forwardRefWithAs(PanelsFn) as _internal_ComponentTabPanels\nexport let TabPanel = forwardRefWithAs(PanelFn) as _internal_ComponentTabPanel\n\nexport let Tab = Object.assign(TabRoot, {\n  /** @deprecated use `<TabGroup>` instead of `<Tab.Group>` */\n  Group: TabGroup,\n  /** @deprecated use `<TabList>` instead of `<Tab.List>` */\n  List: TabList,\n  /** @deprecated use `<TabPanels>` instead of `<Tab.Panels>` */\n  Panels: TabPanels,\n  /** @deprecated use `<TabPanel>` instead of `<Tab.Panel>` */\n  Panel: TabPanel,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/textarea/textarea.test.tsx",
    "content": "import { getTextarea } from '../../test-utils/accessibility-assertions'\nimport { focus, type, word } from '../../test-utils/interactions'\nimport {\n  commonControlScenarios,\n  commonFormScenarios,\n  commonRenderingScenarios,\n} from '../../test-utils/scenarios'\nimport { Textarea } from './textarea'\n\ncommonRenderingScenarios(Textarea, { getElement: getTextarea })\ncommonControlScenarios(Textarea)\ncommonFormScenarios(Textarea, {\n  async performUserInteraction(control) {\n    await focus(control)\n    await type(word('alice'))\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/textarea/textarea.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport { type ElementType, type Ref } from 'react'\nimport { useId } from '../../hooks/use-id'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useDisabled } from '../../internal/disabled'\nimport { useProvidedId } from '../../internal/id'\nimport type { Props } from '../../types'\nimport {\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type RefProp,\n} from '../../utils/render'\nimport { useDescribedBy } from '../description/description'\nimport { useLabelledBy } from '../label/label'\n\nlet DEFAULT_TEXTAREA_TAG = 'textarea' as const\n\ntype TextareaRenderPropArg = {\n  disabled: boolean\n  hover: boolean\n  focus: boolean\n  autofocus: boolean\n  invalid: boolean\n}\ntype TextareaPropsWeControl = 'aria-labelledby' | 'aria-describedby'\n\nexport type TextareaProps<TTag extends ElementType = typeof DEFAULT_TEXTAREA_TAG> = Props<\n  TTag,\n  TextareaRenderPropArg,\n  TextareaPropsWeControl,\n  {\n    disabled?: boolean\n    invalid?: boolean\n    autoFocus?: boolean\n  }\n>\n\nfunction TextareaFn<TTag extends ElementType = typeof DEFAULT_TEXTAREA_TAG>(\n  props: TextareaProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let internalId = useId()\n  let providedId = useProvidedId()\n  let providedDisabled = useDisabled()\n  let {\n    id = providedId || `headlessui-textarea-${internalId}`,\n    disabled = providedDisabled || false,\n    autoFocus = false,\n    invalid = false,\n    ...theirProps\n  } = props\n\n  let labelledBy = useLabelledBy()\n  let describedBy = useDescribedBy()\n\n  let { isFocused: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let ourProps = mergeProps(\n    {\n      ref,\n      id,\n      'aria-labelledby': labelledBy,\n      'aria-describedby': describedBy,\n      'aria-invalid': invalid ? 'true' : undefined,\n      disabled: disabled || undefined,\n      autoFocus,\n    },\n    focusProps,\n    hoverProps\n  )\n\n  let slot = useSlot<TextareaRenderPropArg>({\n    disabled,\n    invalid,\n    hover,\n    focus,\n    autofocus: autoFocus,\n  })\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_TEXTAREA_TAG,\n    name: 'Textarea',\n  })\n}\n\nexport interface _internal_ComponentTextarea extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TEXTAREA_TAG>(\n    props: TextareaProps<TTag> & RefProp<typeof TextareaFn>\n  ): React.JSX.Element\n}\n\nexport let Textarea = forwardRefWithAs(TextareaFn) as _internal_ComponentTextarea\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/tooltip/tooltip.tsx",
    "content": "'use client'\n\nimport { useFocusRing } from '@react-aria/focus'\nimport { useHover } from '@react-aria/interactions'\nimport React, {\n  Fragment,\n  createContext,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useReducer,\n  useRef,\n  useSyncExternalStore,\n  type ElementType,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type Ref,\n} from 'react'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useSlot } from '../../hooks/use-slot'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport {\n  FloatingProvider,\n  useFloatingPanel,\n  useFloatingReference,\n  useResolvedAnchor,\n  type AnchorProps,\n} from '../../internal/floating'\nimport { State, useOpenClosed } from '../../internal/open-closed'\nimport type { Props } from '../../types'\nimport { match } from '../../utils/match'\nimport {\n  RenderFeatures,\n  forwardRefWithAs,\n  mergeProps,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\nimport { Description, useDescribedBy, useDescriptions } from '../description/description'\nimport { Keys } from '../keyboard'\nimport { Portal } from '../portal/portal'\n\nenum TooltipState {\n  // Completely hidden\n  Hidden,\n\n  // Will be visible after a delay\n  Initiated,\n\n  // Completely visible\n  Visible,\n\n  // Visible, but will be hidden after a delay\n  Hiding,\n}\n\nenum When {\n  // Show the tooltip after a delay\n  Delayed,\n\n  // Show the tooltip immediately\n  Immediate,\n}\n\ntype ActiveTooltipId = string | null\nclass TooltipStore {\n  private _state: ActiveTooltipId = null\n  private _listeners: ((state: ActiveTooltipId) => void)[] = []\n\n  subscribe = (listener: (state: ActiveTooltipId) => void) => {\n    this._listeners.push(listener)\n    return () => {\n      this._listeners = this._listeners.filter((x) => x !== listener)\n    }\n  }\n\n  getSnapshot = () => {\n    return this._state\n  }\n\n  getServerSnapshot = () => {\n    return this._state\n  }\n\n  setTooltipId = (state: ActiveTooltipId) => {\n    if (this._state === state) return\n    this._state = state\n    this._listeners.forEach((listener) => listener(state))\n  }\n}\n\nlet tooltipStore = new TooltipStore()\n\ninterface StateDefinition {\n  id: string\n  tooltipState: TooltipState\n}\n\nenum ActionTypes {\n  ShowTooltip,\n  HideTooltip,\n}\n\ntype Actions =\n  | { type: ActionTypes.ShowTooltip; when: When }\n  | { type: ActionTypes.HideTooltip; when: When }\n\nlet reducers: {\n  [P in ActionTypes]: (\n    state: StateDefinition,\n    action: Extract<Actions, { type: P }>\n  ) => StateDefinition\n} = {\n  [ActionTypes.ShowTooltip](state, action) {\n    return {\n      ...state,\n      tooltipState: match(state.tooltipState, {\n        [TooltipState.Hidden]: match(action.when, {\n          [When.Immediate]: TooltipState.Visible,\n          [When.Delayed]: TooltipState.Initiated,\n        }),\n        [TooltipState.Initiated]: match(action.when, {\n          [When.Immediate]: TooltipState.Visible,\n          [When.Delayed]: TooltipState.Initiated,\n        }),\n        [TooltipState.Visible]: TooltipState.Visible,\n        [TooltipState.Hiding]: TooltipState.Visible,\n      }),\n    }\n  },\n  [ActionTypes.HideTooltip](state, action) {\n    return {\n      ...state,\n      tooltipState: match(state.tooltipState, {\n        [TooltipState.Hidden]: TooltipState.Hidden,\n        [TooltipState.Initiated]: TooltipState.Hidden,\n        [TooltipState.Visible]: match(action.when, {\n          [When.Immediate]: TooltipState.Hidden,\n          [When.Delayed]: TooltipState.Hiding,\n        }),\n        [TooltipState.Hiding]: match(action.when, {\n          [When.Immediate]: TooltipState.Hidden,\n          [When.Delayed]: TooltipState.Hiding,\n        }),\n      }),\n    }\n  },\n}\n\nlet TooltipActionsContext = createContext<{\n  showTooltip(when: When): void\n  hideTooltip(when: When): void\n} | null>(null)\nTooltipActionsContext.displayName = 'TooltipActionsContext'\n\nfunction useActions(component: string) {\n  let context = useContext(TooltipActionsContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Tooltip /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useActions)\n    throw err\n  }\n  return context\n}\ntype _Actions = ReturnType<typeof useActions>\n\nlet TooltipDataContext = createContext<({ visible: boolean } & StateDefinition) | null>(null)\nTooltipDataContext.displayName = 'TooltipDataContext'\n\nfunction useData(component: string) {\n  let context = useContext(TooltipDataContext)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Tooltip /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useData)\n    throw err\n  }\n  return context\n}\ntype _Data = ReturnType<typeof useData>\n\nfunction stateReducer(state: StateDefinition, action: Actions) {\n  return match(action.type, reducers, state, action)\n}\n\n// ---\n\nlet DEFAULT_TOOLTIP_TAG = Fragment\n\ntype TooltipRenderPropArg = {}\ntype TooltipPropsWeControl = never\n\nexport type TooltipProps<TTag extends ElementType = typeof DEFAULT_TOOLTIP_TAG> = Props<\n  TTag,\n  TooltipRenderPropArg,\n  TooltipPropsWeControl,\n  {\n    showDelayMs?: number\n    hideDelayMs?: number\n  }\n>\n\nfunction TooltipFn<TTag extends ElementType = typeof DEFAULT_TOOLTIP_TAG>(\n  props: TooltipProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let {\n    id = `headlessui-tooltip-${useId()}`,\n    showDelayMs = 750,\n    hideDelayMs = 300,\n    ...theirProps\n  } = props\n\n  let activeTooltipId = useSyncExternalStore(\n    tooltipStore.subscribe,\n    tooltipStore.getSnapshot,\n    tooltipStore.getServerSnapshot\n  )\n  let [state, dispatch] = useReducer(stateReducer, {\n    id,\n    tooltipState: TooltipState.Hidden,\n  })\n\n  let [describedBy, DescriptionProvider] = useDescriptions()\n\n  let d = useDisposables()\n  useEffect(() => {\n    d.dispose()\n\n    match(state.tooltipState, {\n      [TooltipState.Hidden]() {\n        //\n      },\n      [TooltipState.Initiated]() {\n        d.setTimeout(() => showTooltip(When.Immediate), showDelayMs)\n      },\n      [TooltipState.Visible]() {\n        //\n      },\n      [TooltipState.Hiding]() {\n        d.setTimeout(() => hideTooltip(When.Immediate), hideDelayMs)\n      },\n    })\n  }, [d, state.tooltipState, showDelayMs, hideDelayMs])\n\n  let showTooltip = useEvent((when: When) => {\n    // In this case, showing the tooltip should be delayed, however if another tooltip is already\n    // active then we can make the tooltip show up immediately such that the end use doesn't have to\n    // wait again.\n    if (when === When.Delayed && activeTooltipId !== null && activeTooltipId !== id) {\n      when = When.Immediate\n    }\n\n    // This tooltip should be immediately visible, therefore it should be the active tooltip.\n    if (when === When.Immediate) {\n      tooltipStore.setTooltipId(id)\n    }\n\n    dispatch({ type: ActionTypes.ShowTooltip, when })\n  })\n  let hideTooltip = useEvent((when: When) => {\n    // We are the current active tooltip and we need to be hidden immediately, therefore there\n    // should not be any active tooltip anymore.\n    if (activeTooltipId === id && when === When.Immediate) {\n      tooltipStore.setTooltipId(null)\n    }\n\n    dispatch({ type: ActionTypes.HideTooltip, when })\n  })\n  let tooltipRef = useSyncRefs(ref)\n\n  let ourProps = { ref: tooltipRef }\n\n  let slot = useSlot<TooltipRenderPropArg>({})\n\n  let data = useMemo<_Data>(\n    () => ({\n      visible:\n        activeTooltipId === state.id &&\n        match(state.tooltipState, {\n          [TooltipState.Hidden]: false,\n          [TooltipState.Initiated]: false,\n          [TooltipState.Visible]: true,\n          [TooltipState.Hiding]: true,\n        }),\n      ...state,\n    }),\n    [activeTooltipId, state]\n  )\n  let actions = useMemo<_Actions>(() => ({ showTooltip, hideTooltip }), [showTooltip, hideTooltip])\n\n  let render = useRender()\n\n  return (\n    <DescriptionProvider value={describedBy}>\n      <FloatingProvider>\n        <TooltipActionsContext.Provider value={actions}>\n          <TooltipDataContext.Provider value={data}>\n            {render({\n              ourProps,\n              theirProps,\n              slot,\n              defaultTag: DEFAULT_TOOLTIP_TAG,\n              name: 'Tooltip',\n            })}\n          </TooltipDataContext.Provider>\n        </TooltipActionsContext.Provider>\n      </FloatingProvider>\n    </DescriptionProvider>\n  )\n}\n\n// ---\n\nlet DEFAULT_TRIGGER_TAG = Fragment\n\ntype TriggerRenderPropArg = { hover: boolean; focus: boolean; autofocus: boolean }\ntype TriggerPropsWeControl = 'aria-describedby'\n\nexport type TooltipTriggerProps<TTag extends ElementType = typeof DEFAULT_TRIGGER_TAG> = Props<\n  TTag,\n  TriggerRenderPropArg,\n  TriggerPropsWeControl,\n  { autoFocus?: boolean; disabled?: boolean }\n>\n\nfunction TriggerFn<TTag extends ElementType = typeof DEFAULT_TRIGGER_TAG>(\n  props: TooltipTriggerProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { disabled = false, autoFocus = false, ...theirProps } = props\n  let data = useData('TooltipTrigger')\n  let actions = useActions('TooltipTrigger')\n  let describedBy = useDescribedBy()\n  let internalButtonRef = useRef<HTMLElement | null>(null)\n  let triggerRef = useSyncRefs(internalButtonRef, ref, useFloatingReference())\n\n  let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })\n  let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled })\n\n  let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {\n    switch (event.key) {\n      case Keys.Enter:\n      case Keys.Escape:\n      case Keys.Space:\n        if (data.tooltipState === TooltipState.Visible) {\n          return actions.hideTooltip(When.Immediate)\n        }\n        break\n    }\n  })\n\n  let handleFocus = useEvent(() => {\n    actions.showTooltip(When.Immediate)\n  })\n\n  let handleBlur = useEvent(() => {\n    actions.hideTooltip(When.Immediate)\n  })\n\n  let handleMouseDown = useEvent(() => {\n    actions.hideTooltip(When.Immediate)\n  })\n\n  let handleMouseEnter = useEvent(() => {\n    actions.showTooltip(When.Delayed)\n  })\n\n  let handleMouseLeave = useEvent(() => {\n    actions.hideTooltip(When.Delayed)\n  })\n\n  let handleMouseMove = useEvent(() => {\n    if (data.tooltipState === TooltipState.Hiding) {\n      actions.showTooltip(When.Immediate)\n    }\n  })\n\n  let slot = useSlot<TriggerRenderPropArg>({ hover, focus, autofocus: autoFocus })\n  let ourProps = mergeProps(\n    {\n      ref: triggerRef,\n      'aria-describedby': data.visible ? describedBy : undefined,\n      onKeyDown: handleKeyDown,\n      onFocus: handleFocus,\n      onBlur: handleBlur,\n      onMouseDown: handleMouseDown,\n      onMouseEnter: handleMouseEnter,\n      onMouseLeave: handleMouseLeave,\n      onMouseMove: handleMouseMove,\n    },\n    focusProps,\n    hoverProps\n  )\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot,\n    defaultTag: DEFAULT_TRIGGER_TAG,\n    name: 'TooltipTrigger',\n  })\n}\n\n// ---\n\nlet DEFAULT_PANEL_TAG = Description\n\ntype PanelRenderPropArg = {}\ntype PanelPropsWeControl = 'role'\nlet PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static\n\nexport type TooltipPanelProps<TTag extends ElementType = typeof DEFAULT_PANEL_TAG> = Props<\n  TTag,\n  PanelRenderPropArg,\n  PanelPropsWeControl,\n  { anchor?: AnchorProps } & PropsForFeatures<typeof PanelRenderFeatures>\n>\n\nfunction PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n  props: TooltipPanelProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { anchor: rawAnchor, ...theirProps } = props\n  let data = useData('TooltipPanel')\n\n  let usesOpenClosedState = useOpenClosed()\n  let visible = (() => {\n    if (usesOpenClosedState !== null) {\n      return (usesOpenClosedState & State.Open) === State.Open\n    }\n\n    return data.visible\n  })()\n\n  let internalPanelRef = useRef<HTMLElement | null>(null)\n  let anchor = useResolvedAnchor(rawAnchor ?? { to: 'top', padding: 8, gap: 8, offset: -4 })\n  let [floatingRef, style] = useFloatingPanel(visible ? anchor : undefined)\n  let panelRef = useSyncRefs(internalPanelRef, ref, floatingRef)\n\n  let ourProps = {\n    ref: panelRef,\n    role: 'tooltip',\n    ...(style ? { style } : {}),\n  }\n\n  let slot = useSlot<PanelRenderPropArg>({})\n\n  let render = useRender()\n\n  return render({\n    ourProps: {\n      ...ourProps,\n      as: Fragment,\n      children: (\n        <Portal>\n          {/** @ts-ignore TODO: Figure out why `panelRef` is not working from a TypeScript perspective. */}\n          <Description ref={panelRef} {...theirProps} />\n        </Portal>\n      ),\n    },\n    theirProps: {},\n    slot,\n    defaultTag: Fragment,\n    features: PanelRenderFeatures,\n    visible,\n    name: 'TooltipPanel',\n  })\n}\n\n// ---\n\nexport interface _internal_ComponentTooltip extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TOOLTIP_TAG>(\n    props: TooltipProps<TTag> & RefProp<typeof TooltipFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTrigger extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TRIGGER_TAG>(\n    props: TooltipTriggerProps<TTag> & RefProp<typeof TriggerFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentPanel extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(\n    props: TooltipPanelProps<TTag> & RefProp<typeof PanelFn>\n  ): React.JSX.Element\n}\n\nexport let Tooltip = forwardRefWithAs(TooltipFn) as _internal_ComponentTooltip\nexport let TooltipTrigger = forwardRefWithAs(TriggerFn) as _internal_ComponentTrigger\nexport let TooltipPanel = forwardRefWithAs(PanelFn) as _internal_ComponentPanel\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transition/__snapshots__/transition.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Setup API nested should be possible to change the underlying DOM tag of the Transition component and Transition.Child components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <article>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </article>\n</div>\n`;\n\nexports[`Setup API nested should be possible to change the underlying DOM tag of the Transition.Child components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </div>\n</div>\n`;\n\nexports[`Setup API nested should be possible to nest transition components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <div>\n      Sidebar\n    </div>\n    <div>\n      Content\n    </div>\n  </div>\n</div>\n`;\n\nexports[`Setup API nested should be possible to use render props on the Transition and Transition.Child components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <article>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </article>\n</div>\n`;\n\nexports[`Setup API nested should be possible to use render props on the Transition.Child components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </div>\n</div>\n`;\n\nexports[`Setup API nested should yell at us when we forget to wrap the \\`<Transition.Child />\\` in a parent <Transition /> component 1`] = `\"A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.\"`;\n\nexports[`Setup API nested should yell at us when we forgot to forward a ref on the Transition component 1`] = `\"Did you forget to passthrough the \\`ref\\` to the actual DOM node?\"`;\n\nexports[`Setup API nested should yell at us when we forgot to forward the ref on one of the Transition.Child components 1`] = `\"Did you forget to passthrough the \\`ref\\` to the actual DOM node?\"`;\n\nexports[`Setup API shallow should be possible to change the underlying DOM tag 1`] = `\n<span>\n  Children\n</span>\n`;\n\nexports[`Setup API shallow should be possible to use a render prop 1`] = `\n<span>\n  Children\n</span>\n`;\n\nexports[`Setup API shallow should passthrough all the props (that we do not use internally) 1`] = `\n<div\n  class=\"text-blue-400\"\n  id=\"root\"\n>\n  Children\n</div>\n`;\n\nexports[`Setup API shallow should passthrough all the props (that we do not use internally) even when using an \\`as\\` prop 1`] = `\n<a\n  class=\"text-blue-400\"\n  href=\"/\"\n>\n  Children\n</a>\n`;\n\nexports[`Setup API shallow should render another component if the \\`as\\` prop is used and its children by default 1`] = `\n<a>\n  Children\n</a>\n`;\n\nexports[`Setup API shallow should render nothing when the show prop is false 1`] = `null`;\n\nexports[`Setup API shallow should yell at us when we forget to forward the ref when using a render prop 1`] = `\"Did you forget to passthrough the \\`ref\\` to the actual DOM node?\"`;\n\nexports[`Setup API transition classes should be possible to passthrough the transition classes 1`] = `\n<div>\n  Children\n</div>\n`;\n\nexports[`Setup API transition classes should be possible to passthrough the transition classes and immediately apply the enter transitions when appear is set to true 1`] = `\n<div\n  class=\"enter enter-from\"\n  data-closed=\"\"\n  data-enter=\"\"\n  data-transition=\"\"\n>\n  Children\n</div>\n`;\n\nexports[`should yell at us when we forget the required show prop 1`] = `\"A <Transition /> is used but it is missing a \\`show={true | false}\\` prop.\"`;\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transition/transition.ssr.test.tsx",
    "content": "import React, { Fragment } from 'react'\nimport { renderSSR } from '../../test-utils/ssr'\nimport { Transition } from './transition'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\ndescribe('Rendering', () => {\n  describe('SSR', () => {\n    it('A transition without appear=true does not insert classes during SSR', async () => {\n      let result = await renderSSR(\n        <Transition\n          as={Fragment}\n          show={true}\n          enter=\"enter\"\n          enterFrom=\"enter-from\"\n          enterTo=\"enter-to\"\n        >\n          <div className=\"inner\"></div>\n        </Transition>\n      )\n\n      let div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner')\n\n      await result.hydrate()\n\n      div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner')\n    })\n\n    it('should not overwrite className of children when as=Fragment', async () => {\n      let result = await renderSSR(\n        <Transition\n          as={Fragment}\n          show={true}\n          appear={true}\n          enter=\"enter\"\n          enterFrom=\"enter-from\"\n          enterTo=\"enter-to\"\n        >\n          <div className=\"inner\"></div>\n        </Transition>\n      )\n\n      let div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner enter enter-from')\n\n      await result.hydrate()\n\n      div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner enter enter-from')\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transition/transition.test.tsx",
    "content": "import { fireEvent, render } from '@testing-library/react'\nimport React, { act, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { getByText } from '../../test-utils/accessibility-assertions'\nimport { executeTimeline } from '../../test-utils/execute-timeline'\nimport { click } from '../../test-utils/interactions'\nimport { createSnapshot } from '../../test-utils/snapshot'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Transition } from './transition'\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nit('should not steal the ref from the child', async () => {\n  let fn = jest.fn()\n  render(\n    <Transition show={true}>\n      <div ref={fn}>...</div>\n    </Transition>\n  )\n\n  await nextFrame()\n\n  expect(fn).toHaveBeenCalled()\n})\n\nit('should render without crashing', () => {\n  render(\n    <Transition show={true}>\n      <div className=\"hello\">Children</div>\n    </Transition>\n  )\n})\n\nit(\n  'should yell at us when we forget the required show prop',\n  suppressConsoleLogs(() => {\n    expect.assertions(1)\n\n    expect(() => {\n      render(\n        <Transition>\n          <div className=\"hello\">Children</div>\n        </Transition>\n      )\n    }).toThrowErrorMatchingSnapshot()\n  })\n)\n\ndescribe('Setup API', () => {\n  describe('shallow', () => {\n    it('should passthrough all the props (that we do not use internally)', () => {\n      let { container } = render(\n        /**\n         * Renders a Fragment by default and forwards props. But not possible to\n         * type in TypeScript land. This is also discouraged, but it works.\n         */\n        // @ts-expect-error\n        <Transition show={true} id=\"root\" className=\"text-blue-400\">\n          <div>Children</div>\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should render another component if the `as` prop is used and its children by default', () => {\n      let { container } = render(\n        <Transition show={true} as=\"a\">\n          Children\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should passthrough all the props (that we do not use internally) even when using an `as` prop', () => {\n      let { container } = render(\n        <Transition show={true} as=\"a\" href=\"/\" className=\"text-blue-400\">\n          Children\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should render nothing when the show prop is false', () => {\n      let { container } = render(\n        <Transition show={false}>\n          <div>Children</div>\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag', () => {\n      let { container } = render(\n        <Transition show={true} as=\"span\">\n          Children\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to use a render prop', () => {\n      let { container } = render(<Transition show={true}>{() => <span>Children</span>}</Transition>)\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it(\n      'should yell at us when we forget to forward the ref when using a render prop',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        function Dummy(props: any) {\n          return <span {...props}>Children</span>\n        }\n\n        expect(() => {\n          render(\n            <Transition show={true} enter=\"duration-200\">\n              {() => <Dummy />}\n            </Transition>\n          )\n        }).toThrowErrorMatchingSnapshot()\n      })\n    )\n  })\n\n  describe('nested', () => {\n    it(\n      'should yell at us when we forget to wrap the `<Transition.Child />` in a parent <Transition /> component',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        expect(() => {\n          render(\n            <div className=\"My Page\">\n              <Transition.Child>\n                <div>Oops</div>\n              </Transition.Child>\n            </div>\n          )\n        }).toThrowErrorMatchingSnapshot()\n      })\n    )\n\n    it('should be possible to render a Transition.Child without children', () => {\n      render(\n        <Transition show={true}>\n          <Transition.Child>\n            <div className=\"transition\" />\n          </Transition.Child>\n        </Transition>\n      )\n      expect(document.getElementsByClassName('transition')).not.toBeNull()\n    })\n\n    it('should be possible to use a Transition.Root and a Transition.Child', () => {\n      render(\n        <Transition.Root show={true}>\n          <Transition.Child>\n            <div className=\"transition\" />\n          </Transition.Child>\n        </Transition.Root>\n      )\n      expect(document.getElementsByClassName('transition')).not.toBeNull()\n    })\n\n    it('should be possible to nest transition components', () => {\n      let { container } = render(\n        <div className=\"My Page\">\n          <Transition show={true}>\n            <div>\n              <Transition.Child>\n                <div>Sidebar</div>\n              </Transition.Child>\n              <Transition.Child>\n                <div>Content</div>\n              </Transition.Child>\n            </div>\n          </Transition>\n        </div>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag of the Transition.Child components', () => {\n      let { container } = render(\n        <div className=\"My Page\">\n          <Transition show={true}>\n            <div>\n              <Transition.Child as=\"aside\">Sidebar</Transition.Child>\n              <Transition.Child as=\"section\">Content</Transition.Child>\n            </div>\n          </Transition>\n        </div>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag of the Transition component and Transition.Child components', () => {\n      let { container } = render(\n        <div className=\"My Page\">\n          <Transition show={true} as=\"article\">\n            <Transition.Child as=\"aside\">Sidebar</Transition.Child>\n            <Transition.Child as=\"section\">Content</Transition.Child>\n          </Transition>\n        </div>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to use render props on the Transition.Child components', () => {\n      let { container } = render(\n        <div className=\"My Page\">\n          <Transition show={true}>\n            <div>\n              <Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>\n              <Transition.Child>{() => <section>Content</section>}</Transition.Child>\n            </div>\n          </Transition>\n        </div>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to use render props on the Transition and Transition.Child components', () => {\n      let { container } = render(\n        <div className=\"My Page\">\n          <Transition show={true}>\n            {() => (\n              <article>\n                <Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>\n                <Transition.Child>{() => <section>Content</section>}</Transition.Child>\n              </article>\n            )}\n          </Transition>\n        </div>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it(\n      'should yell at us when we forgot to forward the ref on one of the Transition.Child components',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        function Dummy(props: any) {\n          return <div {...props} />\n        }\n\n        expect(() => {\n          render(\n            <div className=\"My Page\">\n              <Transition show={true}>\n                <div>\n                  <Transition.Child enter=\"duration-200\">\n                    {() => <Dummy>Sidebar</Dummy>}\n                  </Transition.Child>\n                  <Transition.Child enter=\"duration-200\">\n                    {() => <Dummy>Content</Dummy>}\n                  </Transition.Child>\n                </div>\n              </Transition>\n            </div>\n          )\n        }).toThrowErrorMatchingSnapshot()\n      })\n    )\n\n    it(\n      'should not error when the `Transition` component does not contain any props that need to be forwarded',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        expect(() => {\n          render(\n            <div className=\"My Page\">\n              <Transition show={true}>\n                <Transition.Child>\n                  <div>Sidebar</div>\n                </Transition.Child>\n                <Transition.Child>\n                  <div>Content</div>\n                </Transition.Child>\n              </Transition>\n            </div>\n          )\n        }).not.toThrow()\n      })\n    )\n\n    it(\n      'should yell at us when we forgot to forward a ref on the Transition component',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        function Dummy(props: any) {\n          return <div {...props} />\n        }\n\n        expect(() => {\n          render(\n            <div className=\"My Page\">\n              <Transition show={true} enter=\"duration-200\">\n                {() => (\n                  <Dummy>\n                    <Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>\n                    <Transition.Child>{() => <section>Content</section>}</Transition.Child>\n                  </Dummy>\n                )}\n              </Transition>\n            </div>\n          )\n        }).toThrowErrorMatchingSnapshot()\n      })\n    )\n  })\n\n  describe('transition classes', () => {\n    it('should support new lines in class lists', async () => {\n      function Example() {\n        let [show, setShow] = useState(true)\n\n        return (\n          <div>\n            <button onClick={() => setShow((v) => !v)}>toggle</button>\n\n            <Transition show={show} as=\"div\" className={`foo1\\nfoo2`} enter=\"enter\" leave=\"leave\">\n              Children\n            </Transition>\n          </div>\n        )\n      }\n\n      let { container } = await act(() => render(<Example />))\n\n      expect(container.firstChild).toMatchInlineSnapshot(`\n        <div>\n          <button>\n            toggle\n          </button>\n          <div\n            class=\"foo1\n        foo2\"\n          >\n            Children\n          </div>\n        </div>\n      `)\n\n      await click(getByText('toggle'))\n\n      expect(container.firstChild).toMatchInlineSnapshot(`\n         <div>\n           <button>\n             toggle\n           </button>\n           <div\n             class=\"foo1\n         foo2 leave\"\n             data-leave=\"\"\n             data-transition=\"\"\n             style=\"\"\n           >\n             Children\n           </div>\n         </div>\n      `)\n    })\n\n    it('should be possible to passthrough the transition classes', () => {\n      let { container } = render(\n        <Transition\n          show={true}\n          enter=\"enter\"\n          enterFrom=\"enter-from\"\n          enterTo=\"enter-to\"\n          leave=\"leave\"\n          leaveFrom=\"leave-from\"\n          leaveTo=\"leave-to\"\n        >\n          <div>Children</div>\n        </Transition>\n      )\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to passthrough the transition classes and immediately apply the enter transitions when appear is set to true', async () => {\n      let container = createSnapshot()\n\n      function Example() {\n        let ref = container.use()\n\n        return (\n          <Transition\n            ref={ref}\n            show={true}\n            appear={true}\n            enter=\"enter\"\n            enterFrom=\"enter-from\"\n            enterTo=\"enter-to\"\n            leave=\"leave\"\n            leaveFrom=\"leave-from\"\n            leaveTo=\"leave-to\"\n          >\n            <div>Children</div>\n          </Transition>\n        )\n      }\n\n      await act(() => render(<Example />))\n\n      expect(container).toBeDefined()\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n  })\n})\n\ndescribe('Transitions', () => {\n  describe('shallow transitions', () => {\n    xit('should transition in completely (duration defined in milliseconds)', async () => {\n      let enterDuration = 50\n\n      function Example() {\n        let [show, setShow] = useState(false)\n\n        return (\n          <>\n            <style>{`.enter { transition-duration: ${enterDuration}ms; } .from { opacity: 0%; } .to { opacity: 100%; }`}</style>\n\n            <Transition show={show} enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n              <span>Hello!</span>\n            </Transition>\n\n            <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      let timeline = await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    xit('should transition in completely (duration defined in seconds)', async () => {\n      let enterDuration = 100\n\n      function Example() {\n        let [show, setShow] = useState(false)\n\n        return (\n          <>\n            <style>{`.enter { transition-duration: ${\n              enterDuration / 1000\n            }s; } .from { opacity: 0%; } .to { opacity: 100%; }`}</style>\n\n            <Transition show={show} enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n              <span>Hello!</span>\n            </Transition>\n\n            <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      let timeline = await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    xit('should transition in completely (duration defined in seconds) in (render strategy = hidden)', async () => {\n      let enterDuration = 100\n\n      function Example() {\n        let [show, setShow] = useState(false)\n\n        return (\n          <>\n            <style>{`.enter { transition-duration: ${\n              enterDuration / 1000\n            }s; } .from { opacity: 0%; } .to { opacity: 100%; }`}</style>\n\n            <Transition show={show} unmount={false} enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n              <span>Hello!</span>\n            </Transition>\n\n            <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      let timeline = await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    xit('should transition in completely', async () => {\n      let enterDuration = 100\n\n      function Example() {\n        let [show, setShow] = useState(false)\n\n        return (\n          <>\n            <style>{`.enter { transition-duration: ${enterDuration}ms; } .from { opacity: 0%; } .to { opacity: 100%; }`}</style>\n\n            <Transition show={show} enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n              <span>Hello!</span>\n            </Transition>\n\n            <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      let timeline = await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    xit(\n      'should transition out completely',\n      suppressConsoleLogs(async () => {\n        let leaveDuration = 100\n\n        function Example() {\n          let [show, setShow] = useState(true)\n\n          return (\n            <>\n              <style>{`.leave { transition-duration: ${leaveDuration}ms; } .from { opacity: 100%; } .to { opacity: 0%; }`}</style>\n\n              <Transition show={show} leave=\"leave\" leaveFrom=\"from\" leaveTo=\"to\">\n                <span>Hello!</span>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    xit(\n      'should transition out completely (render strategy = hidden)',\n      suppressConsoleLogs(async () => {\n        let leaveDuration = 50\n\n        function Example() {\n          let [show, setShow] = useState(true)\n\n          return (\n            <>\n              <style>{`.leave { transition-duration: ${leaveDuration}ms; } .from { opacity: 0%; } .to { opacity: 100%; }`}</style>\n\n              <Transition show={show} unmount={false} leave=\"leave\" leaveFrom=\"from\" leaveTo=\"to\">\n                <span>Hello!</span>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    xit(\n      'should transition in and out completely',\n      suppressConsoleLogs(async () => {\n        let enterDuration = 100\n        let leaveDuration = 250\n\n        function Example() {\n          let [show, setShow] = useState(false)\n\n          return (\n            <>\n              <style>{`.enter { transition-duration: ${enterDuration}ms; } .enter-from { opacity: 0%; } .enter-to { opacity: 100%; }`}</style>\n              <style>{`.leave { transition-duration: ${leaveDuration}ms; } .leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n\n              <Transition\n                show={show}\n                enter=\"enter\"\n                enterFrom=\"enter-from\"\n                enterTo=\"enter-to\"\n                leave=\"leave\"\n                leaveFrom=\"leave-from\"\n                leaveTo=\"leave-to\"\n              >\n                <span>Hello!</span>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to show\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(enterDuration)\n          },\n\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    xit(\n      'should transition in and out completely (render strategy = hidden)',\n      suppressConsoleLogs(async () => {\n        let enterDuration = 50\n        let leaveDuration = 75\n\n        function Example() {\n          let [show, setShow] = useState(false)\n\n          return (\n            <>\n              <style>{`.enter { transition-duration: ${enterDuration}ms; } .enter-from { opacity: 0%; } .enter-to { opacity: 100%; }`}</style>\n              <style>{`.leave { transition-duration: ${leaveDuration}ms; } .leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n\n              <Transition\n                show={show}\n                unmount={false}\n                enter=\"enter\"\n                enterFrom=\"enter-from\"\n                enterTo=\"enter-to\"\n                leave=\"leave\"\n                leaveFrom=\"leave-from\"\n                leaveTo=\"leave-to\"\n              >\n                <span>Hello!</span>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to show\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(enterDuration)\n          },\n\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n\n          // Toggle to show\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n  })\n\n  describe('nested transitions', () => {\n    xit(\n      'should not unmount the whole tree when some children are still transitioning',\n      suppressConsoleLogs(async () => {\n        let slowLeaveDuration = 500\n        let fastLeaveDuration = 150\n\n        function Example() {\n          let [show, setShow] = useState(true)\n\n          return (\n            <>\n              <style>{`.leave-slow { transition-duration: ${slowLeaveDuration}ms; } .leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n              <style>{`.leave-fast { transition-duration: ${fastLeaveDuration}ms; }`}</style>\n\n              <Transition show={show}>\n                <Transition.Child leave=\"leave-fast\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                  <div>I am fast</div>\n                </Transition.Child>\n                <Transition.Child leave=\"leave-slow\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                  <div>I am slow</div>\n                </Transition.Child>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return [\n              null, // Initial render\n              null, // Setup leave classes\n              fastLeaveDuration, // Done with fast leave\n              slowLeaveDuration - fastLeaveDuration, // Done with slow leave (which starts at the same time, but it is compared with previous render snapshot so we have to subtract those)\n            ]\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    xit(\n      'should not unmount the whole tree when some children are still transitioning',\n      suppressConsoleLogs(async () => {\n        let slowLeaveDuration = 150\n        let fastLeaveDuration = 50\n\n        function Example() {\n          let [show, setShow] = useState(true)\n\n          return (\n            <>\n              <style>{`.leave-slow { transition-duration: ${slowLeaveDuration}ms; } .leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n              <style>{`.leave-fast { transition-duration: ${fastLeaveDuration}ms; }`}</style>\n\n              <Transition show={show}>\n                <Transition.Child leave=\"leave-fast\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                  <span>I am fast</span>\n                  <Transition show={show} leave=\"leave-slow\">\n                    <div>I am my own root component and I don't talk to the parent</div>\n                  </Transition>\n                </Transition.Child>\n                <Transition.Child leave=\"leave-slow\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                  <div>I am slow</div>\n                </Transition.Child>\n              </Transition>\n\n              <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n                Toggle\n              </button>\n            </>\n          )\n        }\n\n        let timeline = await executeTimeline(<Example />, [\n          // Toggle to hide\n          ({ getByTestId }) => {\n            fireEvent.click(getByTestId('toggle'))\n            return [\n              null, // Initial render\n              null, // Setup leave classes\n              fastLeaveDuration, // Done with fast leave\n              slowLeaveDuration - fastLeaveDuration, // Done with slow leave (which starts at the same time, but it is compared with previous render snapshot so we have to subtract those)\n            ]\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n  })\n})\n\ndescribe('Events', () => {\n  xit(\n    'should fire events for all the stages',\n    suppressConsoleLogs(async () => {\n      let eventHandler = jest.fn()\n      let enterDuration = 50\n      let leaveDuration = 75\n\n      function Example() {\n        let [show, setShow] = useState(false)\n        let start = useRef(Date.now())\n\n        useLayoutEffect(() => {\n          start.current = Date.now()\n        }, [])\n\n        return (\n          <>\n            <style>{`.enter { transition-duration: ${enterDuration}ms; } .enter-from { opacity: 0%; } .enter-to { opacity: 100%; }`}</style>\n            <style>{`.leave { transition-duration: ${leaveDuration}ms; } .leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n\n            <Transition\n              show={show}\n              // Events\n              beforeEnter={() => eventHandler('beforeEnter', Date.now() - start.current)}\n              afterEnter={() => eventHandler('afterEnter', Date.now() - start.current)}\n              beforeLeave={() => eventHandler('beforeLeave', Date.now() - start.current)}\n              afterLeave={() => eventHandler('afterLeave', Date.now() - start.current)}\n              // Class names\n              enter=\"enter\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            >\n              <span>Hello!</span>\n            </Transition>\n\n            <button data-testid=\"toggle\" onClick={() => setShow((v) => !v)}>\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      let timeline = await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n        // Toggle to hide\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(leaveDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n\n      expect(eventHandler).toHaveBeenCalledTimes(4)\n      expect(eventHandler.mock.calls.map(([name]) => name)).toEqual([\n        // Order is important here\n        'beforeEnter',\n        'afterEnter',\n        'beforeLeave',\n        'afterLeave',\n      ])\n\n      let enterHookDiff = eventHandler.mock.calls[1][1] - eventHandler.mock.calls[0][1]\n      expect(enterHookDiff).toBeGreaterThanOrEqual(enterDuration)\n      expect(enterHookDiff).toBeLessThanOrEqual(enterDuration * 3)\n\n      let leaveHookDiff = eventHandler.mock.calls[3][1] - eventHandler.mock.calls[2][1]\n      expect(leaveHookDiff).toBeGreaterThanOrEqual(leaveDuration)\n      expect(leaveHookDiff).toBeLessThanOrEqual(leaveDuration * 3)\n    })\n  )\n\n  it(\n    'should fire events in the correct order',\n    suppressConsoleLogs(async () => {\n      let eventHandler = jest.fn()\n      let enterDuration = 50\n      let leaveDuration = 75\n\n      function Example() {\n        let [show, setShow] = useState(false)\n        let start = useRef(Date.now())\n\n        useLayoutEffect(() => {\n          start.current = Date.now()\n        }, [])\n\n        function mark(input: string) {\n          eventHandler(input)\n        }\n\n        function getProps(name: string) {\n          return {\n            // Events\n            beforeEnter: () => mark(`${name}: beforeEnter`),\n            afterEnter: () => mark(`${name}: afterEnter`),\n            beforeLeave: () => mark(`${name}: beforeLeave`),\n            afterLeave: () => mark(`${name}: afterLeave`),\n\n            // Class names\n            enter: `${name} enter`,\n            enterFrom: 'enter-from',\n            enterTo: 'enter-to',\n            leave: `${name} leave`,\n            leaveFrom: 'leave-from',\n            leaveTo: 'leave-to',\n          }\n        }\n\n        return (\n          <>\n            <style>{`.enter-from { opacity: 0%; } .enter-to { opacity: 100%; }`}</style>\n            <style>{`.leave-from { opacity: 100%; } .leave-to { opacity: 0%; }`}</style>\n\n            <style>{`.root.enter { transition-duration: ${enterDuration}ms; }`}</style>\n            <style>{`.root.leave { transition-duration: ${leaveDuration}ms; }`}</style>\n\n            <style>{`.child-1.enter { transition-duration: ${enterDuration * 1.5}ms; }`}</style>\n            <style>{`.child-1.leave { transition-duration: ${leaveDuration * 1.5}ms; }`}</style>\n\n            <style>{`.child-2.enter { transition-duration: ${enterDuration * 2}ms; }`}</style>\n            <style>{`.child-2.leave { transition-duration: ${leaveDuration * 2}ms; }`}</style>\n\n            <style>{`.child-2-1.enter { transition-duration: ${enterDuration * 3}ms; }`}</style>\n            <style>{`.child-2-1.leave { transition-duration: ${leaveDuration * 3}ms; }`}</style>\n\n            <style>{`.child-2-2.enter { transition-duration: ${enterDuration * 2.5}ms; }`}</style>\n            <style>{`.child-2-2.leave { transition-duration: ${leaveDuration * 2.5}ms; }`}</style>\n\n            <Transition show={show} {...getProps('root')}>\n              <div>\n                <Transition.Child {...getProps('child-1')}>\n                  <div>Child 1.</div>\n                </Transition.Child>\n                <Transition.Child {...getProps('child-2')}>\n                  <div>\n                    <div>Child 2.</div>\n                    <Transition.Child {...getProps('child-2-1')}>\n                      <div>Child 2.1.</div>\n                    </Transition.Child>\n                    <Transition.Child {...getProps('child-2-2')}>\n                      <div>Child 2.2.</div>\n                    </Transition.Child>\n                  </div>\n                </Transition.Child>\n              </div>\n            </Transition>\n\n            <button\n              data-testid=\"toggle\"\n              onClick={() => {\n                eventHandler(`action(${show ? 'HIDE' : 'SHOW'})`)\n                setShow((v) => !v)\n              }}\n            >\n              Toggle\n            </button>\n          </>\n        )\n      }\n\n      await executeTimeline(<Example />, [\n        // Toggle to show\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration * 3)\n        },\n        // Toggle to hide\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(leaveDuration * 3)\n        },\n        // Toggle to show (again)\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration * 3)\n        },\n        // Toggle to hide (again)\n        ({ getByTestId }) => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(leaveDuration * 3)\n        },\n      ])\n\n      expect(eventHandler.mock.calls.flat()).toEqual([\n        'action(SHOW)',\n\n        'root: beforeEnter',\n        'child-1: beforeEnter',\n        'child-2: beforeEnter',\n        'child-2-1: beforeEnter',\n        'child-2-2: beforeEnter',\n\n        'child-1: afterEnter',\n        'child-2-1: afterEnter',\n        'child-2-2: afterEnter',\n        'child-2: afterEnter',\n        'root: afterEnter',\n\n        'action(HIDE)',\n\n        'child-1: beforeLeave',\n        'child-2-1: beforeLeave',\n        'child-2-2: beforeLeave',\n        'child-2: beforeLeave',\n        'root: beforeLeave',\n\n        'child-1: afterLeave',\n        'child-2-1: afterLeave',\n        'child-2-2: afterLeave',\n        'child-2: afterLeave',\n        'root: afterLeave',\n\n        'action(SHOW)',\n\n        'root: beforeEnter',\n        'child-1: beforeEnter',\n        'child-2: beforeEnter',\n        'child-2-1: beforeEnter',\n        'child-2-2: beforeEnter',\n\n        'child-1: afterEnter',\n        'child-2-1: afterEnter',\n        'child-2-2: afterEnter',\n        'child-2: afterEnter',\n        'root: afterEnter',\n\n        'action(HIDE)',\n\n        'child-1: beforeLeave',\n        'child-2-1: beforeLeave',\n        'child-2-2: beforeLeave',\n        'child-2: beforeLeave',\n        'root: beforeLeave',\n\n        'child-1: afterLeave',\n        'child-2-1: afterLeave',\n        'child-2-2: afterLeave',\n        'child-2: afterLeave',\n        'root: afterLeave',\n      ])\n    })\n  )\n\n  it(\n    'should fire only one event for a given component change',\n    suppressConsoleLogs(async () => {\n      let eventHandler = jest.fn()\n      let enterDuration = 50\n      let leaveDuration = 75\n\n      function Example() {\n        let [show, setShow] = useState(false)\n        let [start, setStart] = useState(Date.now())\n\n        useEffect(() => setStart(Date.now()), [])\n\n        return (\n          <>\n            <style>{`\n              .enter-1 { transition-duration: ${enterDuration * 1}ms; }\n              .enter-2 { transition-duration: ${enterDuration * 2}ms; }\n              .enter-from { opacity: 0%; }\n              .enter-to { opacity: 100%; }\n\n              .leave-1 { transition-duration: ${leaveDuration * 1}ms; }\n              .leave-2 { transition-duration: ${leaveDuration * 2}ms; }\n              .leave-from { opacity: 100%; }\n              .leave-to { opacity: 0%; }\n            `}</style>\n            <Transition.Root\n              show={show}\n              as=\"div\"\n              beforeEnter={() => eventHandler('beforeEnter', Date.now() - start)}\n              afterEnter={() => eventHandler('afterEnter', Date.now() - start)}\n              beforeLeave={() => eventHandler('beforeLeave', Date.now() - start)}\n              afterLeave={() => eventHandler('afterLeave', Date.now() - start)}\n              enter=\"enter-2\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave-2\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            >\n              <Transition.Child\n                enter=\"enter-1\"\n                enterFrom=\"enter-from\"\n                enterTo=\"enter-to\"\n                leave=\"leave-1\"\n                leaveFrom=\"leave-from\"\n                leaveTo=\"leave-to\"\n              >\n                <div />\n              </Transition.Child>\n              <Transition.Child\n                enter=\"enter-1\"\n                enterFrom=\"enter-from\"\n                enterTo=\"enter-to\"\n                leave=\"leave-1\"\n                leaveFrom=\"leave-from\"\n                leaveTo=\"leave-to\"\n              >\n                <button data-testid=\"hide\" onClick={() => setShow(false)}>\n                  Hide\n                </button>\n              </Transition.Child>\n            </Transition.Root>\n            <button data-testid=\"show\" onClick={() => setShow(true)}>\n              Show\n            </button>\n          </>\n        )\n      }\n\n      render(<Example />)\n\n      fireEvent.click(document.querySelector('[data-testid=show]')!)\n\n      await new Promise((resolve) => setTimeout(resolve, 1000))\n\n      fireEvent.click(document.querySelector('[data-testid=hide]')!)\n\n      await new Promise((resolve) => setTimeout(resolve, 1000))\n\n      expect(eventHandler).toHaveBeenCalledTimes(4)\n      expect(eventHandler.mock.calls.map(([name]) => name)).toEqual([\n        // Order is important here\n        'beforeEnter',\n        'afterEnter',\n        'beforeLeave',\n        'afterLeave',\n      ])\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transition/transition.tsx",
    "content": "'use client'\n\nimport React, {\n  Fragment,\n  createContext,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n  type ElementType,\n  type MutableRefObject,\n  type Ref,\n} from 'react'\nimport { useDisposables } from '../../hooks/use-disposables'\nimport { useEvent } from '../../hooks/use-event'\nimport { useIsMounted } from '../../hooks/use-is-mounted'\nimport { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'\nimport { useLatestValue } from '../../hooks/use-latest-value'\nimport { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'\nimport { useSyncRefs } from '../../hooks/use-sync-refs'\nimport { transitionDataAttributes, useTransition } from '../../hooks/use-transition'\nimport { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'\nimport type { Props, ReactTag } from '../../types'\nimport { classNames } from '../../utils/class-names'\nimport { match } from '../../utils/match'\nimport {\n  RenderFeatures,\n  RenderStrategy,\n  compact,\n  forwardRefWithAs,\n  isFragment,\n  useRender,\n  type HasDisplayName,\n  type PropsForFeatures,\n  type RefProp,\n} from '../../utils/render'\n\ntype ContainerElement = MutableRefObject<HTMLElement | null>\n\ntype TransitionDirection = 'enter' | 'leave'\n\n/**\n * Check if we should forward the ref to the child element or not. This is to\n * prevent crashes when the `as` prop is a Fragment _and_ the component just acts\n * as a state container (aka, there is no actual transition happening).\n *\n * E.g.:\n *\n * ```tsx\n * <Transition show={true}>\n *   <Transition.Child enter=\"duration-100\"><div>Child 1</div></Transition.Child>\n *   <Transition.Child enter=\"duration-200\"><div>Child 2</div></Transition.Child>\n * </Transition>\n * ```\n *\n * In this scenario, the child components are transitioning, but the\n * `Transition` parent, which is a `Fragment`, is not. So we should not forward\n * the ref to the `Fragment`.\n */\nfunction shouldForwardRef<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n  props: TransitionRootProps<TTag>\n) {\n  return (\n    // If we have any of the enter/leave classes\n    Boolean(\n      props.enter ||\n        props.enterFrom ||\n        props.enterTo ||\n        props.leave ||\n        props.leaveFrom ||\n        props.leaveTo\n    ) ||\n    // If the `as` prop is not a Fragment\n    !isFragment(props.as ?? DEFAULT_TRANSITION_CHILD_TAG) ||\n    // If we have a single child, then we can forward the ref directly\n    React.Children.count(props.children) === 1\n  )\n}\n\ninterface TransitionContextValues {\n  show: boolean\n  appear: boolean\n  initial: boolean\n}\nlet TransitionContext = createContext<TransitionContextValues | null>(null)\nTransitionContext.displayName = 'TransitionContext'\n\nenum TreeStates {\n  Visible = 'visible',\n  Hidden = 'hidden',\n}\n\nexport interface TransitionClasses {\n  enter?: string\n  enterFrom?: string\n  enterTo?: string\n  /**\n   * @deprecated The `enterTo` and `leaveTo` classes stay applied after the transition has finished.\n   */\n  entered?: string\n  leave?: string\n  leaveFrom?: string\n  leaveTo?: string\n}\n\nexport interface TransitionEvents {\n  beforeEnter?: () => void\n  afterEnter?: () => void\n  beforeLeave?: () => void\n  afterLeave?: () => void\n}\n\ntype TransitionChildPropsWeControl = never\n\nexport type TransitionChildProps<TTag extends ReactTag> = Props<\n  TTag,\n  TransitionChildRenderPropArg,\n  TransitionChildPropsWeControl,\n  PropsForFeatures<typeof TransitionChildRenderFeatures> &\n    TransitionClasses &\n    TransitionEvents & { transition?: boolean; appear?: boolean }\n>\n\nfunction useTransitionContext() {\n  let context = useContext(TransitionContext)\n\n  if (context === null) {\n    throw new Error(\n      'A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.'\n    )\n  }\n\n  return context\n}\n\nfunction useParentNesting() {\n  let context = useContext(NestingContext)\n\n  if (context === null) {\n    throw new Error(\n      'A <Transition.Child /> is used but it is missing a parent <Transition /> or <Transition.Root />.'\n    )\n  }\n\n  return context\n}\n\ninterface NestingContextValues {\n  children: MutableRefObject<{ el: ContainerElement; state: TreeStates }[]>\n  register: (el: ContainerElement) => () => void\n  unregister: (el: ContainerElement, strategy?: RenderStrategy) => void\n  onStart: (el: ContainerElement, direction: TransitionDirection, cb: () => void) => void\n  onStop: (el: ContainerElement, direction: TransitionDirection, cb: () => void) => void\n  chains: MutableRefObject<\n    Record<TransitionDirection, [container: ContainerElement, promise: Promise<void>][]>\n  >\n  wait: MutableRefObject<Promise<void>>\n}\n\nlet NestingContext = createContext<NestingContextValues | null>(null)\nNestingContext.displayName = 'NestingContext'\n\nfunction hasChildren(\n  bag: NestingContextValues['children'] | { children: NestingContextValues['children'] }\n): boolean {\n  if ('children' in bag) return hasChildren(bag.children)\n  return (\n    bag.current\n      .filter(({ el }) => el.current !== null)\n      .filter(({ state }) => state === TreeStates.Visible).length > 0\n  )\n}\n\nfunction useNesting(done?: () => void, parent?: NestingContextValues) {\n  let doneRef = useLatestValue(done)\n  let transitionableChildren = useRef<NestingContextValues['children']['current']>([])\n  let mounted = useIsMounted()\n  let d = useDisposables()\n\n  let unregister = useEvent((container: ContainerElement, strategy = RenderStrategy.Hidden) => {\n    let idx = transitionableChildren.current.findIndex(({ el }) => el === container)\n    if (idx === -1) return\n\n    match(strategy, {\n      [RenderStrategy.Unmount]() {\n        transitionableChildren.current.splice(idx, 1)\n      },\n      [RenderStrategy.Hidden]() {\n        transitionableChildren.current[idx].state = TreeStates.Hidden\n      },\n    })\n\n    d.microTask(() => {\n      if (!hasChildren(transitionableChildren) && mounted.current) {\n        doneRef.current?.()\n      }\n    })\n  })\n\n  let register = useEvent((container: ContainerElement) => {\n    let child = transitionableChildren.current.find(({ el }) => el === container)\n    if (!child) {\n      transitionableChildren.current.push({ el: container, state: TreeStates.Visible })\n    } else if (child.state !== TreeStates.Visible) {\n      child.state = TreeStates.Visible\n    }\n\n    return () => unregister(container, RenderStrategy.Unmount)\n  })\n\n  let todos = useRef<(() => void)[]>([])\n  let wait = useRef<Promise<void>>(Promise.resolve())\n\n  let chains = useRef<\n    Record<TransitionDirection, [identifier: ContainerElement, promise: Promise<void>][]>\n  >({ enter: [], leave: [] })\n\n  let onStart = useEvent(\n    (\n      container: ContainerElement,\n      direction: TransitionDirection,\n      cb: (direction: TransitionDirection) => void\n    ) => {\n      // Clear out all existing todos\n      todos.current.splice(0)\n\n      // Remove all existing promises for the current container from the parent because we can\n      // ignore those and use only the new one.\n      if (parent) {\n        parent.chains.current[direction] = parent.chains.current[direction].filter(\n          ([containerInParent]) => containerInParent !== container\n        )\n      }\n\n      // Wait until our own transition is done\n      parent?.chains.current[direction].push([\n        container,\n        new Promise<void>((resolve) => {\n          todos.current.push(resolve)\n        }),\n      ])\n\n      // Wait until our children are done\n      parent?.chains.current[direction].push([\n        container,\n        new Promise<void>((resolve) => {\n          Promise.all(chains.current[direction].map(([_container, promise]) => promise)).then(() =>\n            resolve()\n          )\n        }),\n      ])\n\n      if (direction === 'enter') {\n        wait.current = wait.current.then(() => parent?.wait.current).then(() => cb(direction))\n      } else {\n        cb(direction)\n      }\n    }\n  )\n\n  let onStop = useEvent(\n    (\n      _container: ContainerElement,\n      direction: TransitionDirection,\n      cb: (direction: TransitionDirection) => void\n    ) => {\n      Promise.all(chains.current[direction].splice(0).map(([_container, promise]) => promise)) // Wait for my children\n        .then(() => {\n          todos.current.shift()?.() // I'm ready\n        })\n        .then(() => cb(direction))\n    }\n  )\n\n  return useMemo(\n    () => ({\n      children: transitionableChildren,\n      register,\n      unregister,\n      onStart,\n      onStop,\n      wait,\n      chains,\n    }),\n    [register, unregister, transitionableChildren, onStart, onStop, chains, wait]\n  )\n}\n\n// ---\n\nlet DEFAULT_TRANSITION_CHILD_TAG = Fragment\ntype TransitionChildRenderPropArg = MutableRefObject<HTMLElement>\nlet TransitionChildRenderFeatures = RenderFeatures.RenderStrategy\n\nfunction TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n  props: TransitionChildProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let {\n    // Whether or not to enable transitions on the current element (by exposing\n    // transition data). When set to false, the `Transition` component still\n    // acts as a transition boundary for `TransitionChild` components.\n    transition = true,\n\n    // Event \"handlers\"\n    beforeEnter,\n    afterEnter,\n    beforeLeave,\n    afterLeave,\n\n    // Class names\n    enter,\n    enterFrom,\n    enterTo,\n    entered,\n    leave,\n    leaveFrom,\n    leaveTo,\n\n    ...theirProps\n  } = props as typeof props\n  let [localContainerElement, setLocalContainerElement] = useState<HTMLElement | null>(null)\n  let container = useRef<HTMLElement | null>(null)\n  let requiresRef = shouldForwardRef(props)\n\n  let transitionRef = useSyncRefs(\n    ...(requiresRef ? [container, ref, setLocalContainerElement] : ref === null ? [] : [ref])\n  )\n  let strategy = theirProps.unmount ?? true ? RenderStrategy.Unmount : RenderStrategy.Hidden\n\n  let { show, appear, initial } = useTransitionContext()\n\n  let [treeState, setState] = useState(show ? TreeStates.Visible : TreeStates.Hidden)\n\n  let parentNesting = useParentNesting()\n  let { register, unregister } = parentNesting\n\n  useIsoMorphicEffect(() => register(container), [register, container])\n\n  useIsoMorphicEffect(() => {\n    // If we are in another mode than the Hidden mode then ignore\n    if (strategy !== RenderStrategy.Hidden) return\n    if (!container.current) return\n\n    // Make sure that we are visible\n    if (show && treeState !== TreeStates.Visible) {\n      setState(TreeStates.Visible)\n      return\n    }\n\n    return match(treeState, {\n      [TreeStates.Hidden]: () => unregister(container),\n      [TreeStates.Visible]: () => register(container),\n    })\n  }, [treeState, container, register, unregister, show, strategy])\n\n  let ready = useServerHandoffComplete()\n\n  useIsoMorphicEffect(() => {\n    if (!requiresRef) return\n\n    if (ready && treeState === TreeStates.Visible && container.current === null) {\n      throw new Error('Did you forget to passthrough the `ref` to the actual DOM node?')\n    }\n  }, [container, treeState, ready, requiresRef])\n\n  // Skipping initial transition\n  let skip = initial && !appear\n  let immediate = appear && show && initial\n\n  let isTransitioning = useRef(false)\n\n  let nesting = useNesting(() => {\n    // When all children have been unmounted we can only hide ourselves if and\n    // only if we are not transitioning ourselves. Otherwise we would unmount\n    // before the transitions are finished.\n    if (isTransitioning.current) return\n\n    setState(TreeStates.Hidden)\n    unregister(container)\n  }, parentNesting)\n\n  let start = useEvent((show: boolean) => {\n    isTransitioning.current = true\n    let direction: TransitionDirection = show ? 'enter' : 'leave'\n\n    nesting.onStart(container, direction, (direction) => {\n      if (direction === 'enter') beforeEnter?.()\n      else if (direction === 'leave') beforeLeave?.()\n    })\n  })\n\n  let end = useEvent((show: boolean) => {\n    let direction: TransitionDirection = show ? 'enter' : 'leave'\n\n    isTransitioning.current = false\n    nesting.onStop(container, direction, (direction) => {\n      if (direction === 'enter') afterEnter?.()\n      else if (direction === 'leave') afterLeave?.()\n    })\n\n    if (direction === 'leave' && !hasChildren(nesting)) {\n      // When we don't have children anymore we can safely unregister from the\n      // parent and hide ourselves.\n      setState(TreeStates.Hidden)\n      unregister(container)\n    }\n  })\n\n  useEffect(() => {\n    if (requiresRef && transition) return\n\n    // When we don't transition, then we can complete the transition\n    // immediately.\n    start(show)\n    end(show)\n  }, [show, requiresRef, transition])\n\n  let enabled = (() => {\n    // Should the current component transition? If not, then we can still\n    // orchestrate the child transitions.\n    if (!transition) return false\n\n    // If we don't require a ref, then we can't transition.\n    if (!requiresRef) return false\n\n    // If the server handoff isn't completed yet, we can't transition.\n    if (!ready) return false\n\n    // If we start in a `show` state but without the `appear` prop, then we skip\n    // the initial transition.\n    if (skip) return false\n\n    return true\n  })()\n\n  // Ignoring the `visible` state because this doesn't handle the hierarchy. If\n  // a leave transition on the `<Transition>` is done, but there is still a\n  // child `<TransitionChild>` busy, then `visible` would be `false`, while\n  // `state` would still be `TreeStates.Visible`.\n  let [, transitionData] = useTransition(enabled, localContainerElement, show, { start, end })\n\n  let ourProps = compact({\n    ref: transitionRef,\n    className:\n      classNames(\n        // Incoming classes if any\n        // @ts-expect-error: className may not exist because not\n        // all components accept className (but all HTML elements do)\n        theirProps.className,\n\n        // Apply these classes immediately\n        immediate && enter,\n        immediate && enterFrom,\n\n        // Map data attributes to `enter`, `enterFrom` and `enterTo` classes\n        transitionData.enter && enter,\n        transitionData.enter && transitionData.closed && enterFrom,\n        transitionData.enter && !transitionData.closed && enterTo,\n\n        // Map data attributes to `leave`, `leaveFrom` and `leaveTo` classes\n        transitionData.leave && leave,\n        transitionData.leave && !transitionData.closed && leaveFrom,\n        transitionData.leave && transitionData.closed && leaveTo,\n\n        // Map data attributes to `entered` class (backwards compatibility)\n        !transitionData.transition && show && entered\n      )?.trim() || undefined, // If `className` is an empty string, we can omit it\n    ...transitionDataAttributes(transitionData),\n  })\n\n  let openClosedState = 0\n  if (treeState === TreeStates.Visible) openClosedState |= State.Open\n  if (treeState === TreeStates.Hidden) openClosedState |= State.Closed\n  if (show && treeState === TreeStates.Hidden) openClosedState |= State.Opening\n  if (!show && treeState === TreeStates.Visible) openClosedState |= State.Closing\n\n  let render = useRender()\n\n  return (\n    <NestingContext.Provider value={nesting}>\n      <OpenClosedProvider value={openClosedState}>\n        {render({\n          ourProps,\n          theirProps,\n          defaultTag: DEFAULT_TRANSITION_CHILD_TAG,\n          features: TransitionChildRenderFeatures,\n          visible: treeState === TreeStates.Visible,\n          name: 'Transition.Child',\n        })}\n      </OpenClosedProvider>\n    </NestingContext.Provider>\n  )\n}\n\nexport type TransitionRootProps<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG> =\n  TransitionChildProps<TTag> & {\n    show?: boolean\n    appear?: boolean\n  }\n\nfunction TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n  props: TransitionRootProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { show, appear = false, unmount = true, ...theirProps } = props as typeof props\n  let internalTransitionRef = useRef<HTMLElement | null>(null)\n  let requiresRef = shouldForwardRef(props)\n\n  let transitionRef = useSyncRefs(\n    ...(requiresRef ? [internalTransitionRef, ref] : ref === null ? [] : [ref])\n  )\n\n  // The TransitionChild will also call this hook, and we have to make sure that we are ready.\n  useServerHandoffComplete()\n\n  let usesOpenClosedState = useOpenClosed()\n\n  if (show === undefined && usesOpenClosedState !== null) {\n    show = (usesOpenClosedState & State.Open) === State.Open\n  }\n\n  if (show === undefined) {\n    throw new Error('A <Transition /> is used but it is missing a `show={true | false}` prop.')\n  }\n\n  let [state, setState] = useState(show ? TreeStates.Visible : TreeStates.Hidden)\n\n  let nestingBag = useNesting(() => {\n    if (show) return\n    setState(TreeStates.Hidden)\n  })\n\n  let [initial, setInitial] = useState(true)\n\n  // Change the `initial` value\n  let changes = useRef([show])\n  useIsoMorphicEffect(() => {\n    // We can skip this effect\n    if (initial === false) {\n      return\n    }\n\n    // Track the changes\n    if (changes.current[changes.current.length - 1] !== show) {\n      changes.current.push(show)\n      setInitial(false)\n    }\n  }, [changes, show])\n\n  let transitionBag = useMemo<TransitionContextValues>(\n    () => ({ show, appear, initial }),\n    [show, appear, initial]\n  )\n\n  useIsoMorphicEffect(() => {\n    if (show) {\n      setState(TreeStates.Visible)\n    } else if (!hasChildren(nestingBag) && internalTransitionRef.current !== null) {\n      setState(TreeStates.Hidden)\n    }\n  }, [show, nestingBag])\n\n  let sharedProps = { unmount }\n\n  let beforeEnter = useEvent(() => {\n    if (initial) setInitial(false)\n    props.beforeEnter?.()\n  })\n\n  let beforeLeave = useEvent(() => {\n    if (initial) setInitial(false)\n    props.beforeLeave?.()\n  })\n\n  let render = useRender()\n\n  return (\n    <NestingContext.Provider value={nestingBag}>\n      <TransitionContext.Provider value={transitionBag}>\n        {render({\n          ourProps: {\n            ...sharedProps,\n            as: Fragment,\n            children: (\n              <InternalTransitionChild\n                ref={transitionRef}\n                {...sharedProps}\n                {...theirProps}\n                beforeEnter={beforeEnter}\n                beforeLeave={beforeLeave}\n              />\n            ),\n          },\n          theirProps: {},\n          defaultTag: Fragment,\n          features: TransitionChildRenderFeatures,\n          visible: state === TreeStates.Visible,\n          name: 'Transition',\n        })}\n      </TransitionContext.Provider>\n    </NestingContext.Provider>\n  )\n}\n\nfunction ChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n  props: TransitionChildProps<TTag>,\n  ref: MutableRefObject<HTMLElement>\n) {\n  let hasTransitionContext = useContext(TransitionContext) !== null\n  let hasOpenClosedContext = useOpenClosed() !== null\n\n  return (\n    <>\n      {!hasTransitionContext && hasOpenClosedContext ? (\n        <TransitionRoot ref={ref} {...props} />\n      ) : (\n        <InternalTransitionChild ref={ref} {...props} />\n      )}\n    </>\n  )\n}\n\nexport interface _internal_ComponentTransitionRoot extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n    props: TransitionRootProps<TTag> & RefProp<typeof TransitionRootFn>\n  ): React.JSX.Element\n}\n\nexport interface _internal_ComponentTransitionChild extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(\n    props: TransitionChildProps<TTag> & RefProp<typeof TransitionChildFn>\n  ): React.JSX.Element\n}\n\nlet TransitionRoot = forwardRefWithAs(TransitionRootFn) as _internal_ComponentTransitionRoot\nlet InternalTransitionChild = forwardRefWithAs(\n  TransitionChildFn\n) as _internal_ComponentTransitionChild\nexport let TransitionChild = forwardRefWithAs(ChildFn) as _internal_ComponentTransitionChild\n\nexport let Transition = Object.assign(TransitionRoot, {\n  /** @deprecated use `<TransitionChild>` instead of `<Transition.Child>` */\n  Child: TransitionChild,\n  /** @deprecated use `<Transition>` instead of `<Transition.Root>` */\n  Root: TransitionRoot,\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transition-child/transition-child.tsx",
    "content": "// Next.js barrel file improvements (GENERATED FILE)\nexport type * from '../transition/transition'\nexport { TransitionChild } from '../transition/transition'\n"
  },
  {
    "path": "packages/@headlessui-react/src/components/transitions/transition.tsx",
    "content": "// Backwards compatibility for the `transitions` (plural name) folder\nexport * from '../transition/transition'\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/__mocks__/use-id.ts",
    "content": "import { useState } from 'react'\n\nbeforeEach(() => {\n  id = 0\n})\n\nlet id = 0\nfunction generateId() {\n  return ++id\n}\n\nexport function useId() {\n  const [id] = useState(generateId)\n  return id\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/document-overflow/adjust-scrollbar-padding.ts",
    "content": "import type { ScrollLockStep } from './overflow-store'\n\nexport function adjustScrollbarPadding(): ScrollLockStep {\n  let scrollbarWidthBefore: number\n\n  return {\n    before({ doc }) {\n      let documentElement = doc.documentElement\n      let ownerWindow = doc.defaultView ?? window\n\n      scrollbarWidthBefore = Math.max(0, ownerWindow.innerWidth - documentElement.clientWidth)\n    },\n\n    after({ doc, d }) {\n      let documentElement = doc.documentElement\n\n      // Account for the change in scrollbar width\n      // NOTE: This is a bit of a hack, but it's the only way to do this\n      let scrollbarWidthAfter = Math.max(\n        0,\n        documentElement.clientWidth - documentElement.offsetWidth\n      )\n      let scrollbarWidth = Math.max(0, scrollbarWidthBefore - scrollbarWidthAfter)\n\n      d.style(documentElement, 'paddingRight', `${scrollbarWidth}px`)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/document-overflow/handle-ios-locking.ts",
    "content": "import { disposables } from '../../utils/disposables'\nimport * as DOM from '../../utils/dom'\nimport { isIOS } from '../../utils/platform'\nimport type { ScrollLockStep } from './overflow-store'\n\ninterface ContainerMetadata {\n  containers: (() => HTMLElement[])[]\n}\n\nexport function handleIOSLocking(): ScrollLockStep<ContainerMetadata> {\n  if (!isIOS()) {\n    return {}\n  }\n\n  return {\n    before({ doc, d, meta }) {\n      function inAllowedContainer(el: Element) {\n        for (let resolve of meta().containers) {\n          for (let element of resolve()) {\n            if (element.contains(el)) {\n              return true\n            }\n          }\n        }\n\n        return false\n      }\n\n      d.microTask(() => {\n        // We need to be able to offset the body with the current scroll position. However, if you\n        // have `scroll-behavior: smooth` set, then changing the scrollTop in any way shape or form\n        // will trigger a \"smooth\" scroll and the new position would be incorrect.\n        //\n        // This is why we are forcing the `scroll-behavior: auto` here, and then restoring it later.\n        // We have to be a bit careful, because removing `scroll-behavior: auto` back to\n        // `scroll-behavior: smooth` can start triggering smooth scrolling. Delaying this by a\n        // microTask will guarantee that everything is done such that both enter/exit of the Dialog is\n        // not using smooth scrolling.\n        if (window.getComputedStyle(doc.documentElement).scrollBehavior !== 'auto') {\n          let _d = disposables()\n          _d.style(doc.documentElement, 'scrollBehavior', 'auto')\n          d.add(() => d.microTask(() => _d.dispose()))\n        }\n\n        // Keep track of the current scroll position so that we can restore the scroll position if\n        // it has changed in the meantime.\n        let scrollPosition = window.scrollY ?? window.pageYOffset\n\n        // Relatively hacky, but if you click a link like `<a href=\"#foo\">` in the Dialog, and there\n        // exists an element on the page (outside of the Dialog) with that id, then the browser will\n        // scroll to that position. However, this is not the case if the element we want to scroll to\n        // is higher and the browser needs to scroll up, but it doesn't do that.\n        //\n        // Let's try and capture that element and store it, so that we can later scroll to it once the\n        // Dialog closes.\n        let scrollToElement: Element | null = null\n        d.addEventListener(\n          doc,\n          'click',\n          (e) => {\n            if (!DOM.isHTMLorSVGElement(e.target)) {\n              return\n            }\n\n            try {\n              let anchor = e.target.closest('a')\n              if (!anchor) return\n              let { hash } = new URL(anchor.href)\n              let el = doc.querySelector(hash)\n              if (DOM.isHTMLorSVGElement(el) && !inAllowedContainer(el)) {\n                scrollToElement = el\n              }\n            } catch (err) {}\n          },\n          true\n        )\n\n        // Rely on overscrollBehavior to prevent scrolling outside of the Dialog.\n        d.group((_d) => {\n          d.addEventListener(doc, 'touchstart', (e) => {\n            _d.dispose()\n\n            if (DOM.isHTMLorSVGElement(e.target) && DOM.hasInlineStyle(e.target)) {\n              if (inAllowedContainer(e.target)) {\n                // Find the root of the allowed containers\n                let rootContainer = e.target\n                while (\n                  rootContainer.parentElement &&\n                  inAllowedContainer(rootContainer.parentElement)\n                ) {\n                  rootContainer = rootContainer.parentElement!\n                }\n\n                _d.style(rootContainer, 'overscrollBehavior', 'contain')\n              } else {\n                _d.style(e.target, 'touchAction', 'none')\n              }\n            }\n          })\n        })\n\n        d.addEventListener(\n          doc,\n          'touchmove',\n          (e) => {\n            // Check if we are scrolling inside any of the allowed containers, if not let's cancel the event!\n            if (DOM.isHTMLorSVGElement(e.target)) {\n              // Some inputs like `<input type=range>` use touch events to\n              // allow interaction. We should not prevent this event.\n              if (DOM.isHTMLInputElement(e.target)) {\n                return\n              }\n\n              if (inAllowedContainer(e.target)) {\n                // Even if we are in an allowed container, on iOS the main page can still scroll, we\n                // have to make sure that we `event.preventDefault()` this event to prevent that.\n                //\n                // However, if we happen to scroll on an element that is overflowing, or any of its\n                // parents are overflowing, then we should not call `event.preventDefault()` because\n                // otherwise we are preventing the user from scrolling inside that container which\n                // is not what we want.\n                let scrollableParent = e.target\n                while (\n                  scrollableParent.parentElement &&\n                  // Assumption: We are always used in a Headless UI Portal. Once we reach the\n                  // portal itself, we can stop crawling up the tree.\n                  scrollableParent.dataset.headlessuiPortal !== ''\n                ) {\n                  // Check if the scrollable container is overflowing or not.\n                  //\n                  // NOTE: we could check the `overflow`, `overflow-y` and `overflow-x` properties\n                  // but when there is no overflow happening then the `overscrollBehavior` doesn't\n                  // seem to work and the main page will still scroll. So instead we check if the\n                  // scrollable container is overflowing or not and use that heuristic instead.\n                  if (\n                    scrollableParent.scrollHeight > scrollableParent.clientHeight ||\n                    scrollableParent.scrollWidth > scrollableParent.clientWidth\n                  ) {\n                    break\n                  }\n\n                  scrollableParent = scrollableParent.parentElement\n                }\n\n                // We crawled up the tree until the beginning of the Portal, let's prevent the\n                // event if this is the case. If not, then we are in a container where we are\n                // allowed to scroll so we don't have to prevent the event.\n                if (scrollableParent.dataset.headlessuiPortal === '') {\n                  e.preventDefault()\n                }\n              }\n\n              // We are not in an allowed container, so let's prevent the event.\n              else {\n                e.preventDefault()\n              }\n            }\n          },\n          { passive: false }\n        )\n\n        // Restore scroll position if a scrollToElement was captured.\n        d.add(() => {\n          let newScrollPosition = window.scrollY ?? window.pageYOffset\n\n          // If the scroll position changed, then we can restore it to the previous value. This will\n          // happen if you focus an input field and the browser scrolls for you.\n          if (scrollPosition !== newScrollPosition) {\n            window.scrollTo(0, scrollPosition)\n          }\n\n          // If we captured an element that should be scrolled to, then we can try to do that if the\n          // element is still connected (aka, still in the DOM).\n          if (scrollToElement && scrollToElement.isConnected) {\n            scrollToElement.scrollIntoView({ block: 'nearest' })\n            scrollToElement = null\n          }\n        })\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/document-overflow/overflow-store.ts",
    "content": "import { disposables, type Disposables } from '../../utils/disposables'\nimport { createStore } from '../../utils/store'\nimport { adjustScrollbarPadding } from './adjust-scrollbar-padding'\nimport { handleIOSLocking } from './handle-ios-locking'\nimport { preventScroll } from './prevent-scroll'\n\ninterface DocEntry {\n  doc: Document\n  count: number\n  d: Disposables\n  meta: Set<MetaFn>\n  computedMeta: Record<string, any>\n}\n\nfunction buildMeta(fns: Iterable<MetaFn>) {\n  let tmp = {}\n  for (let fn of fns) {\n    Object.assign(tmp, fn(tmp))\n  }\n  return tmp\n}\n\nexport type MetaFn = (meta: Record<string, any>) => Record<string, any>\n\nexport interface Context<MetaType extends Record<string, any> = any> {\n  doc: Document\n  d: Disposables\n  meta: () => MetaType\n}\n\nexport interface ScrollLockStep<MetaType extends Record<string, any> = any> {\n  before?: (ctx: Context<MetaType>) => void\n  after?: (ctx: Context<MetaType>) => void\n}\n\nexport let overflows = createStore(() => new Map<Document, DocEntry>(), {\n  PUSH(doc: Document, meta: MetaFn) {\n    let entry = this.get(doc) ?? {\n      doc,\n      count: 0,\n      d: disposables(),\n      meta: new Set(),\n      computedMeta: {},\n    }\n\n    entry.count++\n    entry.meta.add(meta)\n    entry.computedMeta = buildMeta(entry.meta)\n    this.set(doc, entry)\n\n    return this\n  },\n\n  POP(doc: Document, meta: MetaFn) {\n    let entry = this.get(doc)\n    if (entry) {\n      entry.count--\n      entry.meta.delete(meta)\n      entry.computedMeta = buildMeta(entry.meta)\n    }\n\n    return this\n  },\n\n  SCROLL_PREVENT(entry: DocEntry) {\n    let ctx = {\n      doc: entry.doc,\n      d: entry.d,\n\n      // The moment we `PUSH`, we also `SCROLL_PREVENT`. But a later `PUSH` will\n      // not re-trigger a `SCROLL_PREVENT` because we are already in a locked\n      // state.\n      //\n      // This `meta()` function is called lazily such that a `PUSH` or `POP`\n      // that happens later can update the meta information. Otherwise we would\n      // use stale meta information.\n      meta() {\n        return entry.computedMeta\n      },\n    }\n\n    let steps: ScrollLockStep<any>[] = [\n      handleIOSLocking(),\n      adjustScrollbarPadding(),\n      preventScroll(),\n    ]\n\n    // Run all `before` actions together\n    steps.forEach(({ before }) => before?.(ctx))\n\n    // Run all `after` actions together\n    steps.forEach(({ after }) => after?.(ctx))\n  },\n\n  SCROLL_ALLOW({ d }: DocEntry) {\n    d.dispose()\n  },\n\n  TEARDOWN({ doc }: DocEntry) {\n    this.delete(doc)\n  },\n})\n\n// Update the document overflow state when the store changes\n// This MUST happen outside of react for this to work properly.\noverflows.subscribe(() => {\n  let docs = overflows.getSnapshot()\n\n  let styles = new Map<Document, string | undefined>()\n\n  // Read data from all the documents\n  for (let [doc] of docs) {\n    styles.set(doc, doc.documentElement.style.overflow)\n  }\n\n  // Write data to all the documents\n  for (let entry of docs.values()) {\n    let isHidden = styles.get(entry.doc) === 'hidden'\n    let isLocked = entry.count !== 0\n    let willChange = (isLocked && !isHidden) || (!isLocked && isHidden)\n\n    if (willChange) {\n      overflows.dispatch(entry.count > 0 ? 'SCROLL_PREVENT' : 'SCROLL_ALLOW', entry)\n    }\n\n    // We have to clean up after ourselves so we don't leak memory\n    // Using a WeakMap would be ideal, but it's not iterable\n    if (entry.count === 0) {\n      overflows.dispatch('TEARDOWN', entry)\n    }\n  }\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/document-overflow/prevent-scroll.ts",
    "content": "import type { ScrollLockStep } from './overflow-store'\n\nexport function preventScroll(): ScrollLockStep {\n  return {\n    before({ doc, d }) {\n      d.style(doc.documentElement, 'overflow', 'hidden')\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/document-overflow/use-document-overflow.ts",
    "content": "import { useStore } from '../../hooks/use-store'\nimport { useIsoMorphicEffect } from '../use-iso-morphic-effect'\nimport { overflows } from './overflow-store'\n\nexport function useDocumentOverflowLockedEffect(\n  shouldBeLocked: boolean,\n  doc: Document | null,\n  meta: (meta: Record<string, any>) => Record<string, any> = () => ({ containers: [] })\n) {\n  let store = useStore(overflows)\n  let entry = doc ? store.get(doc) : undefined\n  let locked = entry ? entry.count > 0 : false\n\n  useIsoMorphicEffect(() => {\n    if (!doc || !shouldBeLocked) {\n      return\n    }\n\n    // Prevent the document from scrolling\n    overflows.dispatch('PUSH', doc, meta)\n\n    // Allow document to scroll\n    return () => overflows.dispatch('POP', doc, meta)\n  }, [shouldBeLocked, doc])\n\n  return locked\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-active-press.tsx",
    "content": "import { useRef, useState } from 'react'\nimport { getOwnerDocument } from '../utils/owner'\nimport { useDisposables } from './use-disposables'\nimport { useEvent } from './use-event'\n\n// Only the necessary props from a DOMRect\ntype Rect = { left: number; right: number; top: number; bottom: number }\n\nfunction pointerRectFromPointerEvent(event: PointerEvent): Rect {\n  // Center of the pointer geometry\n  let offsetX = event.width / 2\n  let offsetY = event.height / 2\n\n  return {\n    top: event.clientY - offsetY,\n    right: event.clientX + offsetX,\n    bottom: event.clientY + offsetY,\n    left: event.clientX - offsetX,\n  }\n}\n\nfunction areRectsOverlapping(a: Rect | null, b: Rect | null) {\n  if (!a || !b) {\n    return false\n  }\n\n  if (a.right < b.left || a.left > b.right) {\n    return false\n  }\n\n  if (a.bottom < b.top || a.top > b.bottom) {\n    return false\n  }\n\n  return true\n}\n\nexport function useActivePress({ disabled = false }: Partial<{ disabled: boolean }> = {}) {\n  let target = useRef<HTMLElement | null>(null)\n  let [pressed, setPressed] = useState(false)\n\n  let d = useDisposables()\n\n  let reset = useEvent(() => {\n    target.current = null\n    setPressed(false)\n    d.dispose()\n  })\n\n  let handlePointerDown = useEvent((event: PointerEvent) => {\n    d.dispose() // Cancel any scheduled tasks\n\n    if (target.current !== null) return\n\n    // Keep track of the current element\n    target.current = event.currentTarget as HTMLElement\n\n    // We are definitely pressing the element now\n    setPressed(true)\n\n    // Setup global handlers to catch events on elements that are not the current element\n    {\n      let owner = getOwnerDocument(event.currentTarget as Element)!\n\n      // `pointerup` on any element means that we are no longer pressing the current element\n      d.addEventListener(owner, 'pointerup', reset, false)\n\n      // `pointerleave` isn't called consistently (if at all) on iOS Safari, so we use `pointermove` instead\n      // to determine if we are still \"pressing\". We also compare the pointer position to the target element\n      // so that we can tell if the pointer is still over the element or not.\n      d.addEventListener(\n        owner,\n        'pointermove',\n        (event: PointerEvent) => {\n          if (target.current) {\n            let pointerRect = pointerRectFromPointerEvent(event)\n            setPressed(areRectsOverlapping(pointerRect, target.current.getBoundingClientRect()))\n          }\n        },\n        false\n      )\n\n      // Whenever the browser decides to fire a `pointercancel` event, we should abort\n      d.addEventListener(owner, 'pointercancel', reset, false)\n    }\n  })\n\n  return {\n    pressed,\n    pressProps: disabled\n      ? {}\n      : {\n          onPointerDown: handlePointerDown,\n          onPointerUp: reset,\n          onClick: reset,\n        },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-by-comparator.ts",
    "content": "import { useCallback } from 'react'\n\nexport type ByComparator<T> =\n  | (NonNullable<T> extends never ? string : keyof NonNullable<T> & string)\n  | ((a: T, z: T) => boolean)\n\nfunction defaultBy<T>(a: T, z: T) {\n  if (\n    a !== null &&\n    z !== null &&\n    typeof a === 'object' &&\n    typeof z === 'object' &&\n    'id' in a &&\n    'id' in z\n  ) {\n    return a.id === z.id\n  }\n\n  return a === z\n}\n\nexport function useByComparator<T>(by: ByComparator<T> = defaultBy) {\n  return useCallback(\n    (a: T, z: T) => {\n      if (typeof by === 'string') {\n        let property = by as keyof T\n        return a?.[property] === z?.[property]\n      }\n\n      return by(a, z)\n    },\n    [by]\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-computed.ts",
    "content": "import { useState } from 'react'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\nimport { useLatestValue } from './use-latest-value'\n\nexport function useComputed<T>(cb: () => T, dependencies: React.DependencyList) {\n  let [value, setValue] = useState(cb)\n  let cbRef = useLatestValue(cb)\n  useIsoMorphicEffect(() => setValue(cbRef.current), [cbRef, setValue, ...dependencies])\n  return value\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-controllable.ts",
    "content": "import { useRef, useState } from 'react'\nimport { flushSync } from 'react-dom'\nimport { useEvent } from './use-event'\n\nexport function useControllable<T>(\n  controlledValue: T | undefined,\n  onChange?: (value: T) => void,\n  defaultValue?: T\n) {\n  let [internalValue, setInternalValue] = useState(defaultValue)\n\n  let isControlled = controlledValue !== undefined\n  let wasControlled = useRef(isControlled)\n  let didWarnOnUncontrolledToControlled = useRef(false)\n  let didWarnOnControlledToUncontrolled = useRef(false)\n\n  if (isControlled && !wasControlled.current && !didWarnOnUncontrolledToControlled.current) {\n    didWarnOnUncontrolledToControlled.current = true\n    wasControlled.current = isControlled\n    console.error(\n      'A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.'\n    )\n  } else if (!isControlled && wasControlled.current && !didWarnOnControlledToUncontrolled.current) {\n    didWarnOnControlledToUncontrolled.current = true\n    wasControlled.current = isControlled\n    console.error(\n      'A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.'\n    )\n  }\n\n  return [\n    (isControlled ? controlledValue : internalValue)!,\n    useEvent((value) => {\n      if (isControlled) {\n        return onChange?.(value)\n      } else {\n        // Ensure internal state is up to date with the value, before calling\n        // onChange. This allows you to submit forms as part of the `onChange`\n        // and gives enough time to update the form field value(s).\n        flushSync(() => setInternalValue(value))\n\n        return onChange?.(value)\n      }\n    }),\n  ] as const\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-default-value.ts",
    "content": "import { useState } from 'react'\n\n/**\n * Returns a stable value that never changes unless the component is re-mounted.\n *\n * This ensures that we can use this value in a dependency array without causing\n * unnecessary re-renders (because while the incoming `value` can change, the\n * returned `defaultValue` won't change).\n */\nexport function useDefaultValue<T>(value: T) {\n  let [defaultValue] = useState(value)\n  return defaultValue\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-disposables.ts",
    "content": "import { useEffect, useState } from 'react'\nimport { disposables } from '../utils/disposables'\n\n/**\n * The `useDisposables` hook returns a `disposables` object that is disposed\n * when the component is unmounted.\n */\nexport function useDisposables() {\n  // Using useState instead of useRef so that we can use the initializer function.\n  let [d] = useState(disposables)\n  useEffect(() => () => d.dispose(), [d])\n  return d\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-document-event.ts",
    "content": "import { useEffect } from 'react'\nimport { useLatestValue } from './use-latest-value'\n\nexport function useDocumentEvent<TType extends keyof DocumentEventMap>(\n  enabled: boolean,\n  type: TType,\n  listener: (ev: DocumentEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  let listenerRef = useLatestValue(listener)\n\n  useEffect(() => {\n    if (!enabled) return\n\n    function handler(event: DocumentEventMap[TType]) {\n      listenerRef.current(event)\n    }\n\n    document.addEventListener(type, handler, options)\n    return () => document.removeEventListener(type, handler, options)\n  }, [enabled, type, options])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-element-size.ts",
    "content": "import { useState } from 'react'\nimport { disposables } from '../utils/disposables'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nfunction computeSize(element: HTMLElement | null) {\n  if (element === null) return { width: 0, height: 0 }\n  let { width, height } = element.getBoundingClientRect()\n  return { width, height }\n}\n\nexport function useElementSize(enabled: boolean, element: HTMLElement | null, unit = false) {\n  let [size, setSize] = useState(() => computeSize(element))\n\n  useIsoMorphicEffect(() => {\n    if (!element) return\n    if (!enabled) return\n\n    let d = disposables()\n\n    // requestAnimationFrame loop to catch any visual changes such as a\n    // `transform: scale` which wouldn't trigger a ResizeObserver\n    d.requestAnimationFrame(function run() {\n      d.requestAnimationFrame(run)\n\n      setSize((current) => {\n        let newSize = computeSize(element)\n\n        if (newSize.width === current.width && newSize.height === current.height) {\n          // Return the old object to avoid re-renders\n          return current\n        }\n\n        return newSize\n      })\n    })\n\n    return () => {\n      d.dispose()\n    }\n  }, [element, enabled])\n\n  if (unit) {\n    return {\n      width: `${size.width}px`,\n      height: `${size.height}px`,\n    }\n  }\n\n  return size\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-escape.ts",
    "content": "import { Keys } from '../components/keyboard'\nimport { useEventListener } from './use-event-listener'\nimport { useIsTopLayer } from './use-is-top-layer'\n\nexport function useEscape(\n  enabled: boolean,\n  view = typeof document !== 'undefined' ? document.defaultView : null,\n  cb: (event: KeyboardEvent) => void\n) {\n  let isTopLayer = useIsTopLayer(enabled, 'escape')\n\n  useEventListener(view, 'keydown', (event) => {\n    if (!isTopLayer) return\n    if (event.defaultPrevented) return\n    if (event.key !== Keys.Escape) return\n\n    cb(event)\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-event-listener.ts",
    "content": "import { useEffect } from 'react'\nimport { useLatestValue } from './use-latest-value'\n\nexport function useEventListener<TType extends keyof WindowEventMap>(\n  element: HTMLElement | Document | Window | EventTarget | null | undefined,\n  type: TType,\n  listener: (event: WindowEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  let listenerRef = useLatestValue(listener)\n\n  useEffect(() => {\n    element = element ?? window\n\n    function handler(event: WindowEventMap[TType]) {\n      listenerRef.current(event)\n    }\n\n    element.addEventListener(type, handler as any, options)\n    return () => element!.removeEventListener(type, handler as any, options)\n  }, [element, type, options])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-event.ts",
    "content": "import React from 'react'\nimport { useLatestValue } from './use-latest-value'\n\nexport let useEvent =\n  // TODO: Add React.useEvent ?? once the useEvent hook is available\n  function useEvent<\n    F extends (...args: any[]) => any,\n    P extends any[] = Parameters<F>,\n    R = ReturnType<F>,\n  >(cb: (...args: P) => R) {\n    let cache = useLatestValue(cb)\n    return React.useCallback((...args: P) => cache.current(...args), [cache])\n  }\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-flags.ts",
    "content": "import { useCallback, useState } from 'react'\n\nexport function useFlags(initialFlags = 0) {\n  let [flags, setFlags] = useState(initialFlags)\n\n  let setFlag = useCallback((flag: number) => setFlags(flag), [])\n\n  let addFlag = useCallback((flag: number) => setFlags((flags) => flags | flag), [])\n  let hasFlag = useCallback((flag: number) => (flags & flag) === flag, [flags])\n  let removeFlag = useCallback((flag: number) => setFlags((flags) => flags & ~flag), [])\n  let toggleFlag = useCallback((flag: number) => setFlags((flags) => flags ^ flag), [])\n\n  return { flags, setFlag, addFlag, hasFlag, removeFlag, toggleFlag }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-handle-toggle.tsx",
    "content": "import { useRef, type PointerEvent as ReactPointerEvent } from 'react'\nimport { MouseButton } from '../components/mouse'\nimport { isDisabledReactIssue7711 } from '../utils/bugs'\nimport { useEvent } from './use-event'\n\nexport function useHandleToggle(cb: (event: ReactPointerEvent) => void) {\n  let pointerTypeRef = useRef<'touch' | 'mouse' | 'pen' | null>(null)\n\n  let handlePointerDown = useEvent((event: ReactPointerEvent<HTMLButtonElement>) => {\n    pointerTypeRef.current = event.pointerType\n\n    // Skip disabled elements\n    if (isDisabledReactIssue7711(event.currentTarget)) return\n\n    // We only want to handle mouse events here. Touch and pen events should be\n    // ignored to prevent accidentally blocking scrolling. They will be\n    // handled by the click listener instead.\n    if (event.pointerType !== 'mouse') return\n\n    // We are only interested in left clicks, but because this is a pointerdown\n    // event we have to check this property manually because this is also\n    // fired for right clicks.\n    if (event.button !== MouseButton.Left) return\n\n    // We use the `pointerdown` event here since it fires before the focus\n    // event, allowing us to cancel the event before focus is moved.\n    //\n    // If this is used in a button where the currently focused element is an\n    // `input` then we keep the focus in the `input` instead of moving it to\n    // the button. This preserves the cursor position and any text selection\n    // in the input.\n    event.preventDefault()\n\n    // Finally we are ready to toggle\n    cb(event)\n  })\n\n  let handleClick = useEvent((event: ReactPointerEvent<HTMLButtonElement>) => {\n    // Skip mouse events, they are already handled in the pointerdown handler above.\n    if (pointerTypeRef.current === 'mouse') return\n\n    // Skip disabled elements\n    if (isDisabledReactIssue7711(event.currentTarget)) return\n\n    // Finally we are ready to toggle\n    cb(event)\n  })\n\n  return {\n    onPointerDown: handlePointerDown,\n    onClick: handleClick,\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-id.ts",
    "content": "// Re-exporting the useId hook such that we can easily mock this hook in tests.\nexport { useId } from 'react'\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-inert-others.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React, { useRef, useState, type ReactNode } from 'react'\nimport { assertInert, assertNotInert, getByText } from '../test-utils/accessibility-assertions'\nimport { click } from '../test-utils/interactions'\nimport { useInertOthers } from './use-inert-others'\n\nbeforeEach(() => {\n  jest.restoreAllMocks()\n  jest.spyOn(global.console, 'error').mockImplementation(jest.fn())\n})\n\nit('should be possible to inert an element', async () => {\n  function Example() {\n    let ref = useRef(null)\n    let [enabled, setEnabled] = useState(true)\n    useInertOthers(enabled, { disallowed: () => [ref.current] })\n\n    return (\n      <div ref={ref} id=\"main\">\n        <button onClick={() => setEnabled((v) => !v)}>toggle</button>\n      </div>\n    )\n  }\n\n  function Before() {\n    return <div>before</div>\n  }\n\n  function After() {\n    return <div>after</div>\n  }\n\n  render(\n    <>\n      <Before />\n      <Example />\n      <After />\n    </>,\n    { container: document.body }\n  )\n\n  // Verify that `main` is inert\n  assertInert(document.getElementById('main'))\n\n  // Verify that the others are not inert\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n\n  // Restore\n  await click(getByText('toggle'))\n\n  // Verify that nothing is inert\n  assertNotInert(document.getElementById('main'))\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n})\n\nit('should not mark an element as inert when the hook is disabled', async () => {\n  function Example() {\n    let ref = useRef(null)\n    let [enabled, setEnabled] = useState(false)\n    useInertOthers(enabled, { disallowed: () => [ref.current] })\n\n    return (\n      <div ref={ref} id=\"main\">\n        <button onClick={() => setEnabled((v) => !v)}>toggle</button>\n      </div>\n    )\n  }\n\n  function Before() {\n    return <div>before</div>\n  }\n\n  function After() {\n    return <div>after</div>\n  }\n\n  render(\n    <>\n      <Before />\n      <Example />\n      <After />\n    </>,\n    { container: document.body }\n  )\n\n  assertNotInert(document.getElementById('main'))\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n})\n\nit('should mark the element as not inert anymore, once all references are gone', async () => {\n  function Example({ children }: { children: ReactNode }) {\n    let ref = useRef<HTMLDivElement | null>(null)\n\n    let [enabled, setEnabled] = useState(false)\n    useInertOthers(enabled, { disallowed: () => [ref.current?.parentElement ?? null] })\n\n    return (\n      <div ref={ref}>\n        <button onClick={() => setEnabled((v) => !v)}>{children}</button>\n      </div>\n    )\n  }\n\n  render(\n    <div id=\"parent\">\n      <Example>A</Example>\n      <Example>B</Example>\n    </div>,\n    { container: document.body }\n  )\n\n  // Parent should not be inert yet\n  assertNotInert(document.getElementById('parent'))\n\n  // Toggle A\n  await click(getByText('A'))\n\n  // Parent should be inert\n  assertInert(document.getElementById('parent'))\n\n  // Toggle B\n  await click(getByText('B'))\n\n  // Parent should still be inert\n  assertInert(document.getElementById('parent'))\n\n  // Toggle A\n  await click(getByText('A'))\n\n  // Parent should still be inert (because B is still enabled)\n  assertInert(document.getElementById('parent'))\n\n  // Toggle B\n  await click(getByText('B'))\n\n  // Parent should not be inert because both A and B are disabled\n  assertNotInert(document.getElementById('parent'))\n})\n\nit('should be possible to mark everything but allowed containers as inert', async () => {\n  function Example({ children }: { children: ReactNode }) {\n    let [enabled, setEnabled] = useState(false)\n    useInertOthers(enabled, {\n      allowed: () => [document.getElementById('a-a-b')!, document.getElementById('a-a-c')!],\n    })\n\n    return (\n      <div>\n        {children}\n        <button onClick={() => setEnabled((v) => !v)}>toggle</button>\n      </div>\n    )\n  }\n\n  render(\n    <Example>\n      <div id=\"a\">\n        <div id=\"a-a\">\n          <div id=\"a-a-a\"></div>\n          <div id=\"a-a-b\"></div>\n          <div id=\"a-a-c\"></div>\n        </div>\n        <div id=\"a-b\"></div>\n        <div id=\"a-c\"></div>\n      </div>\n    </Example>,\n    { container: document.body }\n  )\n\n  let a = document.getElementById('a')!\n  let aa = document.getElementById('a-a')!\n  let aaa = document.getElementById('a-a-a')!\n  let aab = document.getElementById('a-a-b')!\n  let aac = document.getElementById('a-a-c')!\n  let ab = document.getElementById('a-b')!\n  let ac = document.getElementById('a-c')!\n\n  // Nothing should be inert\n  for (let el of [a, aa, aaa, aab, aac, ab, ac]) assertNotInert(el)\n\n  // Toggle inert state\n  await click(getByText('toggle'))\n\n  // Every sibling of `a-a-b` and `a-a-c` should be inert, and all the\n  // siblings of the parents of `a-a-b` and `a-a-c` should be inert as well.\n  // The path to the body should not be marked as inert.\n  for (let el of [a, aa, aab, aac]) assertNotInert(el)\n  for (let el of [aaa, ab, ac]) assertInert(el)\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-inert-others.tsx",
    "content": "import { disposables } from '../utils/disposables'\nimport { getOwnerDocument } from '../utils/owner'\nimport { useIsTopLayer } from './use-is-top-layer'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nlet originals = new Map<HTMLElement, { 'aria-hidden': string | null; inert: boolean }>()\nlet counts = new Map<HTMLElement, number>()\n\nfunction markInert(element: HTMLElement) {\n  // Increase count\n  let count = counts.get(element) ?? 0\n  counts.set(element, count + 1)\n\n  // Already marked as inert, no need to do it again\n  if (count !== 0) return () => markNotInert(element)\n\n  // Keep track of previous values, so that we can restore them when we are done\n  originals.set(element, {\n    'aria-hidden': element.getAttribute('aria-hidden'),\n    inert: element.inert,\n  })\n\n  // Mark as inert\n  element.setAttribute('aria-hidden', 'true')\n  element.inert = true\n\n  return () => markNotInert(element)\n}\n\nfunction markNotInert(element: HTMLElement) {\n  // Decrease counts\n  let count = counts.get(element) ?? 1 // Should always exist\n  if (count === 1) counts.delete(element) // We are the last one, so we can delete the count\n  else counts.set(element, count - 1) // We are not the last one\n\n  // Not the last one, so we don't restore the original values (yet)\n  if (count !== 1) return\n\n  let original = originals.get(element)\n  if (!original) return // Should never happen\n\n  // Restore original values\n  if (original['aria-hidden'] === null) element.removeAttribute('aria-hidden')\n  else element.setAttribute('aria-hidden', original['aria-hidden'])\n  element.inert = original.inert\n\n  // Remove tracking of original values\n  originals.delete(element)\n}\n\n/**\n * Mark all elements on the page as inert, except for the ones that are allowed.\n *\n * We move up the tree from the allowed elements, and mark all their siblings as\n * inert. If any of the children happens to be a parent of one of the elements,\n * then that child will not be marked as inert.\n *\n * E.g.:\n *\n * ```html\n * <body>                      <!-- Stop at body -->\n *   <header></header>         <!-- Inert, sibling of parent -->\n *   <main>                    <!-- Not inert, parent of allowed element -->\n *     <div>Sidebar</div>      <!-- Inert, sibling of parent -->\n *     <div>                   <!-- Not inert, parent of allowed element -->\n *       <listbox>             <!-- Not inert, parent of allowed element -->\n *         <button></button>   <!-- Not inert, allowed element -->\n *         <options></options> <!-- Not inert, allowed element -->\n *       </listbox>\n *     </div>\n *   </main>\n *   <footer></footer>         <!-- Inert, sibling of parent -->\n * </body>\n * ```\n */\nexport function useInertOthers(\n  enabled: boolean,\n  {\n    allowed,\n    disallowed,\n  }: { allowed?: () => (HTMLElement | null)[]; disallowed?: () => (HTMLElement | null)[] } = {}\n) {\n  let isTopLayer = useIsTopLayer(enabled, 'inert-others')\n\n  useIsoMorphicEffect(() => {\n    if (!isTopLayer) return\n\n    let d = disposables()\n\n    // Mark all disallowed elements as inert\n    for (let element of disallowed?.() ?? []) {\n      if (!element) continue\n\n      d.add(markInert(element))\n    }\n\n    // Mark all siblings of allowed elements (and parents) as inert\n    let allowedElements = allowed?.() ?? []\n\n    for (let element of allowedElements) {\n      if (!element) continue\n\n      let ownerDocument = getOwnerDocument(element)\n      if (!ownerDocument) continue\n\n      let parent = element.parentElement\n      while (parent && parent !== ownerDocument.body) {\n        // Mark all siblings as inert\n        for (let node of parent.children) {\n          // If the node contains any of the elements we should not mark it as inert\n          // because it would make the elements unreachable.\n          if (allowedElements.some((el) => node.contains(el))) continue\n\n          // Mark the node as inert\n          d.add(markInert(node as HTMLElement))\n        }\n\n        // Move up the tree\n        parent = parent.parentElement\n      }\n    }\n\n    return d.dispose\n  }, [isTopLayer, allowed, disallowed])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-is-initial-render.ts",
    "content": "import { useEffect, useRef } from 'react'\n\nexport function useIsInitialRender() {\n  let initial = useRef(true)\n\n  useEffect(() => {\n    initial.current = false\n\n    return () => {\n      initial.current = true\n    }\n  }, [])\n\n  return initial.current\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-is-mounted.ts",
    "content": "import { useRef } from 'react'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nexport function useIsMounted() {\n  let mounted = useRef(false)\n\n  useIsoMorphicEffect(() => {\n    mounted.current = true\n\n    return () => {\n      mounted.current = false\n    }\n  }, [])\n\n  return mounted\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-is-top-layer.ts",
    "content": "import { useCallback, useId } from 'react'\nimport { stackMachines } from '../machines/stack-machine'\nimport { useSlice } from '../react-glue'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\n/**\n * A hook that returns whether the current node is on the top of the hierarchy,\n * aka \"top layer\". Note: this does not use the native DOM \"top-layer\" but\n * conceptually it's the same thing.\n *\n * The hierarchy is also shared across multiple components that use the same\n * scope.\n *\n * This is useful to use in components and hooks that mutate the DOM or share\n * some global state.\n *\n * A use case for this is to use this inside of a `useOutsideClick` hook where\n * only the last rendered component should handle the outside click event.\n *\n * ```ts\n * <Dialog>\n *   <Menu>\n *     <MenuButton></MenuButton> // Pressing escape on an open `Menu` should close the `Menu` and not the `Dialog`.\n *     // …\n *   </Menu>\n * </Dialog>\n * ```\n */\nexport function useIsTopLayer(enabled: boolean, scope: string | null) {\n  let id = useId()\n  let stackMachine = stackMachines.get(scope)\n\n  let [isTop, onStack] = useSlice(\n    stackMachine,\n    useCallback(\n      (state) => [\n        stackMachine.selectors.isTop(state, id),\n        stackMachine.selectors.inStack(state, id),\n      ],\n      [stackMachine, id]\n    )\n  )\n\n  // Depending on the enable state, push/pop the current `id` to/from the\n  // hierarchy.\n  useIsoMorphicEffect(() => {\n    if (!enabled) return\n    stackMachine.actions.push(id)\n    return () => stackMachine.actions.pop(id)\n  }, [stackMachine, enabled, id])\n\n  // If the hook is not enabled, we know for sure it is not going to be the\n  // top-most item.\n  if (!enabled) return false\n\n  // If the hook is enabled, and it's on the stack, we can rely on the `isTop`\n  // derived state to determine if it's the top-most item.\n  if (onStack) return isTop\n\n  // In this scenario, the hook is enabled, but we are not on the stack yet. In\n  // this case we assume that we will be the top-most item, so we return\n  // `true`. However, if that's not the case, and once we are on the stack (or\n  // other items are pushed) this hook will be re-evaluated and the `isTop`\n  // derived state will be used instead.\n  return true\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-is-touch-device.ts",
    "content": "import { useState } from 'react'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nexport function useIsTouchDevice() {\n  let [mq] = useState(() =>\n    typeof window !== 'undefined' && typeof window.matchMedia === 'function'\n      ? window.matchMedia('(pointer: coarse)')\n      : null\n  )\n  let [isTouchDevice, setIsTouchDevice] = useState(mq?.matches ?? false)\n\n  useIsoMorphicEffect(() => {\n    if (!mq) return\n\n    function handle(event: MediaQueryListEvent) {\n      setIsTouchDevice(event.matches)\n    }\n\n    mq.addEventListener('change', handle)\n    return () => mq!.removeEventListener('change', handle)\n  }, [mq])\n\n  return isTouchDevice\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-iso-morphic-effect.ts",
    "content": "import { useEffect, useLayoutEffect, type DependencyList, type EffectCallback } from 'react'\nimport { env } from '../utils/env'\n\nexport let useIsoMorphicEffect = (effect: EffectCallback, deps?: DependencyList | undefined) => {\n  if (env.isServer) {\n    useEffect(effect, deps)\n  } else {\n    useLayoutEffect(effect, deps)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-latest-value.ts",
    "content": "import { useRef } from 'react'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nexport function useLatestValue<T>(value: T) {\n  let cache = useRef(value)\n\n  useIsoMorphicEffect(() => {\n    cache.current = value\n  }, [value])\n\n  return cache\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-on-disappear.ts",
    "content": "import { useEffect, type MutableRefObject } from 'react'\nimport { disposables } from '../utils/disposables'\nimport * as DOM from '../utils/dom'\nimport { useLatestValue } from './use-latest-value'\n\n/**\n * A hook to ensure that a callback is called when the element has disappeared\n * from the screen.\n *\n * This can happen if you use Tailwind classes like: `hidden md:block`, once the\n * viewport is smaller than `md` the element will disappear.\n */\nexport function useOnDisappear(\n  enabled: boolean,\n  ref: MutableRefObject<HTMLElement | null> | HTMLElement | null,\n  cb: () => void\n) {\n  let listenerRef = useLatestValue((element: HTMLElement) => {\n    let rect = element.getBoundingClientRect()\n    if (rect.x === 0 && rect.y === 0 && rect.width === 0 && rect.height === 0) {\n      cb()\n    }\n  })\n\n  useEffect(() => {\n    if (!enabled) return\n\n    let element = ref === null ? null : DOM.isHTMLElement(ref) ? ref : ref.current\n    if (!element) return\n\n    let d = disposables()\n\n    // Try using ResizeObserver\n    if (typeof ResizeObserver !== 'undefined') {\n      let observer = new ResizeObserver(() => listenerRef.current(element!))\n      observer.observe(element)\n      d.add(() => observer.disconnect())\n    }\n\n    // Try using IntersectionObserver\n    if (typeof IntersectionObserver !== 'undefined') {\n      let observer = new IntersectionObserver(() => listenerRef.current(element!))\n      observer.observe(element)\n      d.add(() => observer.disconnect())\n    }\n\n    return () => d.dispose()\n  }, [ref, listenerRef, enabled])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-on-unmount.ts",
    "content": "import { useEffect, useRef } from 'react'\nimport { microTask } from '../utils/micro-task'\nimport { useEvent } from './use-event'\n\nexport function useOnUnmount(cb: () => void) {\n  let stableCb = useEvent(cb)\n\n  let trulyUnmounted = useRef(false)\n  useEffect(() => {\n    trulyUnmounted.current = false\n\n    return () => {\n      trulyUnmounted.current = true\n      microTask(() => {\n        if (!trulyUnmounted.current) return\n\n        stableCb()\n      })\n    }\n  }, [stableCb])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-outside-click.ts",
    "content": "import { useCallback, useRef } from 'react'\nimport * as DOM from '../utils/dom'\nimport { FocusableMode, isFocusableElement } from '../utils/focus-management'\nimport { isMobile } from '../utils/platform'\nimport { useDocumentEvent } from './use-document-event'\nimport { useLatestValue } from './use-latest-value'\nimport { useWindowEvent } from './use-window-event'\n\ntype Container = Element | null\ntype ContainerCollection = Container[] | Set<Container>\ntype ContainerInput = Container | ContainerCollection\n\n// If the user moves their finger by ${MOVE_THRESHOLD_PX} pixels or more, we'll\n// assume that they are scrolling and not clicking. This will prevent the click\n// from being triggered when the user is scrolling.\n//\n// This also allows you to \"cancel\" the click by moving your finger more than\n// the threshold in pixels in any direction.\nconst MOVE_THRESHOLD_PX = 30\n\nexport function useOutsideClick(\n  enabled: boolean,\n  containers: ContainerInput | (() => ContainerInput),\n  cb: (\n    event: MouseEvent | PointerEvent | FocusEvent | TouchEvent,\n    target: HTMLOrSVGElement & Element\n  ) => void\n) {\n  let cbRef = useLatestValue(cb)\n\n  let handleOutsideClick = useCallback(\n    function handleOutsideClick<E extends MouseEvent | PointerEvent | FocusEvent | TouchEvent>(\n      event: E,\n      resolveTarget: (event: E) => (HTMLOrSVGElement & Element) | null\n    ) {\n      // Check whether the event got prevented already. This can happen if you\n      // use the useOutsideClick hook in both a Dialog and a Menu and the inner\n      // Menu \"cancels\" the default behavior so that only the Menu closes and\n      // not the Dialog (yet)\n      if (event.defaultPrevented) return\n\n      // Resolve the new target\n      let target = resolveTarget(event)\n      if (target === null) return\n\n      // Ignore if the target doesn't exist in the DOM anymore\n      if (!target.getRootNode().contains(target)) return\n\n      // Ignore if the target was removed from the DOM by the time the handler\n      // was called\n      if (!target.isConnected) return\n\n      let _containers = (function resolve(containers): ContainerCollection {\n        if (typeof containers === 'function') {\n          return resolve(containers())\n        }\n\n        if (Array.isArray(containers)) {\n          return containers\n        }\n\n        if (containers instanceof Set) {\n          return containers\n        }\n\n        return [containers]\n      })(containers)\n\n      // Ignore if the target exists in one of the containers\n      for (let container of _containers) {\n        if (container === null) continue\n        if (container.contains(target)) {\n          return\n        }\n\n        // If the click crossed a shadow boundary, we need to check if the\n        // container is inside the tree by using `composedPath` to \"pierce\" the\n        // shadow boundary\n        if (event.composed && event.composedPath().includes(container as EventTarget)) {\n          return\n        }\n      }\n\n      // This allows us to check whether the event was defaultPrevented when you\n      // are nesting this inside a `<Dialog />` for example.\n      if (\n        // This check allows us to know whether or not we clicked on a\n        // \"focusable\" element like a button or an input. This is a backwards\n        // compatibility check so that you can open a <Menu /> and click on\n        // another <Menu /> which should close Menu A and open Menu B. We might\n        // revisit that so that you will require 2 clicks instead.\n        !isFocusableElement(target, FocusableMode.Loose) &&\n        // This could be improved, but the `Combobox.Button` adds tabIndex={-1}\n        // to make it unfocusable via the keyboard so that tabbing to the next\n        // item from the input doesn't first go to the button.\n        target.tabIndex !== -1\n      ) {\n        event.preventDefault()\n      }\n\n      return cbRef.current(event, target)\n    },\n    [cbRef, containers]\n  )\n\n  let initialClickTarget = useRef<HTMLElement | null>(null)\n\n  useDocumentEvent(\n    enabled,\n    'pointerdown',\n    (event) => {\n      if (isMobile()) return\n\n      initialClickTarget.current = (event.composedPath?.()?.[0] || event.target) as HTMLElement\n    },\n    true\n  )\n\n  useDocumentEvent(\n    enabled,\n    'pointerup',\n    (event) => {\n      if (isMobile()) return\n      if (!initialClickTarget.current) return\n\n      let target = initialClickTarget.current\n      initialClickTarget.current = null\n\n      return handleOutsideClick(event, () => target)\n    },\n\n    // We will use the `capture` phase so that layers in between with `event.stopPropagation()`\n    // don't \"cancel\" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`\n    // is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,\n    // the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.\n    true\n  )\n\n  let startPosition = useRef({ x: 0, y: 0 })\n  useDocumentEvent(\n    enabled,\n    'touchstart',\n    (event) => {\n      startPosition.current.x = event.touches[0].clientX\n      startPosition.current.y = event.touches[0].clientY\n    },\n    true\n  )\n\n  useDocumentEvent(\n    enabled,\n    'touchend',\n    (event) => {\n      // If the user moves their finger by ${MOVE_THRESHOLD_PX} pixels or more,\n      // we'll assume that they are scrolling and not clicking.\n      let endPosition = { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY }\n      if (\n        Math.abs(endPosition.x - startPosition.current.x) >= MOVE_THRESHOLD_PX ||\n        Math.abs(endPosition.y - startPosition.current.y) >= MOVE_THRESHOLD_PX\n      ) {\n        return\n      }\n\n      return handleOutsideClick(event, () => {\n        if (DOM.isHTMLorSVGElement(event.target)) {\n          return event.target\n        }\n        return null\n      })\n    },\n\n    // We will use the `capture` phase so that layers in between with `event.stopPropagation()`\n    // don't \"cancel\" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`\n    // is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,\n    // the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.\n    true\n  )\n\n  // When content inside an iframe is clicked `window` will receive a blur event\n  // This can happen when an iframe _inside_ a window is clicked\n  // Or, if headless UI is *in* the iframe, when a content in a window containing that iframe is clicked\n\n  // In this case we care only about the first case so we check to see if the active element is the iframe\n  // If so this was because of a click, focus, or other interaction with the child iframe\n  // and we can consider it an \"outside click\"\n  useWindowEvent(\n    enabled,\n    'blur',\n    (event) => {\n      return handleOutsideClick(event, () => {\n        return DOM.isHTMLIframeElement(window.document.activeElement)\n          ? window.document.activeElement\n          : null\n      })\n    },\n    true\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-owner.ts",
    "content": "import { useMemo } from 'react'\nimport { getOwnerDocument, getRootNode } from '../utils/owner'\n\nexport function useOwnerDocument(...args: Parameters<typeof getOwnerDocument>) {\n  return useMemo(() => getOwnerDocument(...args), [...args])\n}\n\nexport function useRootDocument(...args: Parameters<typeof getRootNode>) {\n  return useMemo(() => getRootNode(...args), [...args])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-quick-release.ts",
    "content": "import { useRef } from 'react'\nimport * as DOM from '../utils/dom'\nimport { useDocumentEvent } from './use-document-event'\n\nenum ActionKind {\n  Ignore,\n  Select,\n  Close,\n}\n\nexport const Action = {\n  /** Do nothing */\n  Ignore: { kind: ActionKind.Ignore } as const,\n\n  /** Select the current item */\n  Select: (target: HTMLElement) => ({ kind: ActionKind.Select, target }) as const,\n\n  /** Close the dropdown */\n  Close: { kind: ActionKind.Close } as const,\n}\n\n// If the time difference between pointerdown and pointerup is less than this,\n// it is very likely a normal click, and nothing special should happen.\n//\n// Once we reach this threshold, we assume the user is trying to select an item\n// in the dropdown, and we should close the dropdown after the click.\n//\n// Pointerdown -> drag over an item -> pointer up -> \"click\" on the item\nconst POINTER_HOLD_THRESHOLD = 200\n\n// We should at least move this amount of pixels before we consider it a quick\n// release. Otherwise it's just a normal click.\nconst POINTER_MOVEMENT_THRESHOLD = 5\n\ntype PointerEventWithTarget = Exclude<PointerEvent, 'target'> & {\n  target: HTMLElement\n}\n\nexport function useQuickRelease(\n  enabled: boolean,\n  {\n    trigger,\n    action,\n    close,\n    select,\n  }: {\n    trigger: HTMLElement | null\n    action: (\n      e: PointerEventWithTarget\n    ) =>\n      | { kind: ActionKind.Ignore }\n      | { kind: ActionKind.Select; target: HTMLElement }\n      | { kind: ActionKind.Close }\n    close: () => void\n    select: (target: HTMLElement) => void\n  }\n) {\n  // Capture the timestamp of when the `pointerdown` event happened on the\n  // trigger.\n  let triggeredAtRef = useRef<number | null>(null)\n  let startXRef = useRef<number | null>(null)\n  let startYRef = useRef<number | null>(null)\n  useDocumentEvent(enabled && trigger !== null, 'pointerdown', (e) => {\n    if (!DOM.isNode(e?.target)) return\n    if (!trigger?.contains(e.target)) return\n\n    startXRef.current = e.x\n    startYRef.current = e.y\n\n    triggeredAtRef.current = e.timeStamp\n  })\n\n  useDocumentEvent(\n    enabled && trigger !== null,\n    'pointerup',\n    (e) => {\n      let triggeredAt = triggeredAtRef.current\n      if (triggeredAt === null) return\n      triggeredAtRef.current = null\n\n      if (!DOM.isHTMLorSVGElement(e.target)) return\n\n      // Ensure we moved the pointer a bit before considering it a quick\n      // release.\n      if (\n        Math.abs(e.x - (startXRef.current ?? e.x)) < POINTER_MOVEMENT_THRESHOLD &&\n        Math.abs(e.y - (startYRef.current ?? e.y)) < POINTER_MOVEMENT_THRESHOLD\n      ) {\n        return\n      }\n\n      let result = action(e as PointerEventWithTarget)\n\n      switch (result.kind) {\n        case ActionKind.Ignore:\n          return\n\n        case ActionKind.Select: {\n          if (e.timeStamp - triggeredAt > POINTER_HOLD_THRESHOLD) {\n            select(result.target)\n            close()\n          }\n          break\n        }\n\n        case ActionKind.Close: {\n          close()\n          break\n        }\n      }\n    },\n    { capture: true }\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-refocusable-input.ts",
    "content": "import { useRef } from 'react'\nimport * as DOM from '../utils/dom'\nimport { isActiveElement } from '../utils/owner'\nimport { useEvent } from './use-event'\nimport { useEventListener } from './use-event-listener'\n\n/**\n * The `useRefocusableInput` hook exposes a function to re-focus the input element.\n *\n * This hook will also keep the cursor position into account to make sure the\n * cursor is placed at the correct position as-if we didn't loose focus at all.\n */\nexport function useRefocusableInput(input: HTMLInputElement | null) {\n  // Track the cursor position and the value of the input\n  let info = useRef({\n    value: '',\n    selectionStart: null as number | null,\n    selectionEnd: null as number | null,\n  })\n\n  useEventListener(input, 'blur', (event) => {\n    let target = event.target\n    if (!DOM.isHTMLInputElement(target)) return\n\n    info.current = {\n      value: target.value,\n      selectionStart: target.selectionStart,\n      selectionEnd: target.selectionEnd,\n    }\n  })\n\n  return useEvent(() => {\n    // If the input is already focused, we don't need to do anything\n    if (isActiveElement(input)) return\n\n    if (!DOM.isHTMLInputElement(input)) return\n    if (!input.isConnected) return\n\n    // Focus the input\n    input.focus({ preventScroll: true })\n\n    // Try to restore the cursor position\n    //\n    // If the value changed since we recorded the cursor position, then we can't\n    // restore the cursor position and we'll just leave it at the end.\n    if (input.value !== info.current.value) {\n      input.setSelectionRange(input.value.length, input.value.length)\n    }\n\n    // If the value is the same, we can restore to the previous cursor position.\n    else {\n      let { selectionStart, selectionEnd } = info.current\n      if (selectionStart !== null && selectionEnd !== null) {\n        input.setSelectionRange(selectionStart, selectionEnd)\n      }\n    }\n\n    // Reset the cursor position\n    info.current = { value: '', selectionStart: null, selectionEnd: null }\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-resolve-button-type.ts",
    "content": "import { useMemo } from 'react'\n\nexport function useResolveButtonType<TTag>(\n  props: { type?: string; as?: TTag },\n  element: HTMLElement | null\n) {\n  return useMemo(() => {\n    // A type was provided\n    if (props.type) return props.type\n\n    // Resolve the type based on the `as` prop\n    let tag = props.as ?? 'button'\n    if (typeof tag === 'string' && tag.toLowerCase() === 'button') return 'button'\n\n    // Resolve the type based on the HTML element\n    if (element?.tagName === 'BUTTON' && !element.hasAttribute('type')) return 'button'\n\n    // Could not resolve the type\n    return undefined\n  }, [props.type, props.as, element])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-resolved-tag.ts",
    "content": "import { useCallback, useState } from 'react'\nimport * as DOM from '../utils/dom'\n\n/**\n * Resolve the actual rendered tag of a DOM node. If the `tag` provided is\n * already a string we can use that as-is. This will happen when the `as` prop is\n * not used or when it's used with a string value.\n *\n * If an actual component is used, then we need to do some more work because\n * then we actually need to render the component to know what the tag name is.\n */\nexport function useResolvedTag<T extends React.ElementType>(tag: T) {\n  let tagName = typeof tag === 'string' ? tag : undefined\n  let [resolvedTag, setResolvedTag] = useState<string | undefined>(tagName)\n\n  return [\n    // The resolved tag name\n    tagName ?? resolvedTag,\n\n    // This callback should be passed to the `ref` of a component\n    useCallback(\n      (ref: any) => {\n        // Tag name is already known and it's a string, no need to re-render\n        if (tagName) return\n\n        if (DOM.isHTMLElement(ref)) {\n          // Tag name is not known yet, render the component to find out\n          setResolvedTag(ref.tagName.toLowerCase())\n        }\n      },\n      [tagName]\n    ),\n  ] as const\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-root-containers.tsx",
    "content": "import React, { createContext, useContext, useState, type MutableRefObject } from 'react'\nimport { Hidden, HiddenFeatures } from '../internal/hidden'\nimport * as DOM from '../utils/dom'\nimport { getOwnerDocument } from '../utils/owner'\nimport { useEvent } from './use-event'\n\nexport function useRootContainers({\n  defaultContainers = [],\n  portals,\n\n  // Reference to a node in the \"main\" tree, not in the portalled Dialog tree.\n  mainTreeNode,\n}: {\n  defaultContainers?: (Element | null | MutableRefObject<Element | null>)[]\n  portals?: MutableRefObject<Element[]>\n  mainTreeNode?: Element | null\n} = {}) {\n  let resolveContainers = useEvent(() => {\n    let ownerDocument = getOwnerDocument(mainTreeNode)\n    let containers: Element[] = []\n\n    // Resolve default containers\n    for (let container of defaultContainers) {\n      if (container === null) continue\n      if (DOM.isElement(container)) {\n        containers.push(container)\n      } else if ('current' in container && DOM.isElement(container.current)) {\n        containers.push(container.current)\n      }\n    }\n\n    // Resolve portal containers\n    if (portals?.current) {\n      for (let portal of portals.current) {\n        containers.push(portal)\n      }\n    }\n\n    // Resolve third party (root) containers\n    for (let container of ownerDocument?.querySelectorAll('html > *, body > *') ?? []) {\n      if (container === document.body) continue // Skip `<body>`\n      if (container === document.head) continue // Skip `<head>`\n      if (!DOM.isElement(container)) continue // Skip non-HTMLElements\n      if (container.id === 'headlessui-portal-root') continue // Skip the Headless UI portal root\n      if (mainTreeNode) {\n        if (container.contains(mainTreeNode)) continue // Skip if it is the main app\n        if (container.contains((mainTreeNode?.getRootNode() as ShadowRoot)?.host)) continue // Skip if it is the main app (and the component is inside a shadow root)\n      }\n      if (containers.some((defaultContainer) => container.contains(defaultContainer))) continue // Skip if the current container is part of a container we've already seen (e.g.: default container / portal)\n\n      containers.push(container)\n    }\n\n    return containers\n  })\n\n  return {\n    resolveContainers,\n    contains: useEvent((element: Element) =>\n      resolveContainers().some((container) => container.contains(element))\n    ),\n  }\n}\n\nlet MainTreeContext = createContext<Element | null>(null)\n\n/**\n * A provider for the main tree node.\n *\n * When a component is rendered in a `Portal`, it is no longer part of the main\n * tree. This provider helps to find the main tree node and pass it along to the\n * components that need it.\n *\n * The main tree node is used for features such as outside click behavior, where\n * we allow clicks in 3rd party containers, but not in the parent of the \"main\n * tree\".\n *\n * In case of a `Popover`, we can use the `PopoverButton` as a marker in the\n * \"main tree\", the `PopoverPanel` can't be used because it could be rendered in\n * a `Portal` (e.g. when using the `anchor` props).\n *\n * However, we can't use the `PopoverButton` when it's nested inside of another\n * `Popover`'s `PopoverPanel` component if the parent `PopoverPanel` is\n * rendered in a `Portal`.\n *\n * This is where the `MainTreeProvider` comes in. It will find the \"main tree\"\n * node and pass it on. The top-level `PopoverButton` will be used as a marker\n * in the \"main tree\" and nested `Popover` will use this button as well.\n */\nexport function MainTreeProvider({\n  children,\n  node,\n}: {\n  children: React.ReactNode\n  node?: Element | null\n}) {\n  let [mainTreeNode, setMainTreeNode] = useState<Element | null>(null)\n\n  // 1. Prefer the main tree node from context\n  // 2. Prefer the provided node\n  // 3. Create a new node at this point, and find the main tree node\n  let resolvedMainTreeNode = useMainTreeNode(node ?? mainTreeNode)\n\n  return (\n    <MainTreeContext.Provider value={resolvedMainTreeNode}>\n      {children}\n\n      {/**\n       * If no main tree node is found at this point, then we briefly render an\n       * element to find the main tree node and pass it along.\n       */}\n      {resolvedMainTreeNode === null && (\n        <Hidden\n          features={HiddenFeatures.Hidden}\n          ref={(el) => {\n            if (!el) return\n\n            // We will only render this when no `mainTreeNode` is found. This\n            // means that if we render this element and use it as the\n            // `mainTreeNode` that we will be unmounting it later.\n            //\n            // However, we can resolve the actual root container of the main\n            // tree node and use that instead.\n            for (let container of getOwnerDocument(el)?.querySelectorAll('html > *, body > *') ??\n              []) {\n              if (container === document.body) continue // Skip `<body>`\n              if (container === document.head) continue // Skip `<head>`\n              if (!DOM.isElement(container)) continue // Skip non-HTMLElements\n              if (container?.contains(el)) {\n                setMainTreeNode(container)\n                break\n              }\n            }\n          }}\n        />\n      )}\n    </MainTreeContext.Provider>\n  )\n}\n\n/**\n * Get the main tree node from context or fallback to the optionally provided node.\n */\nexport function useMainTreeNode(fallbackMainTreeNode: Element | null = null) {\n  // Prefer the main tree node from context, but fallback to the provided node.\n  return useContext(MainTreeContext) ?? fallbackMainTreeNode\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-scroll-lock.ts",
    "content": "import { useDocumentOverflowLockedEffect } from './document-overflow/use-document-overflow'\nimport { useIsTopLayer } from './use-is-top-layer'\n\nexport function useScrollLock(\n  enabled: boolean,\n  ownerDocument: Document | null,\n  resolveAllowedContainers: () => Element[] = () => [document.body]\n) {\n  let isTopLayer = useIsTopLayer(enabled, 'scroll-lock')\n\n  useDocumentOverflowLockedEffect(isTopLayer, ownerDocument, (meta) => ({\n    containers: [...(meta.containers ?? []), resolveAllowedContainers],\n  }))\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-server-handoff-complete.ts",
    "content": "import * as React from 'react'\nimport { env } from '../utils/env'\n\n/**\n * This is used to determine if we're hydrating in React 18.\n *\n * The `useServerHandoffComplete` hook doesn't work with `<Suspense>`\n * because it assumes all hydration happens at one time during page load.\n *\n * Given that the problem only exists in React 18 we can rely\n * on newer APIs to determine if hydration is happening.\n */\nfunction useIsHydratingInReact18(): boolean {\n  let isServer = typeof document === 'undefined'\n\n  // React < 18 doesn't have any way to figure this out afaik\n  if (!('useSyncExternalStore' in React)) {\n    return false\n  }\n\n  // This weird pattern makes sure bundlers don't throw at build time\n  // because `useSyncExternalStore` isn't defined in React < 18\n  const useSyncExternalStore = ((r) => r.useSyncExternalStore)(React)\n\n  // @ts-ignore\n  let result = useSyncExternalStore(\n    () => () => {},\n    () => false,\n    () => (isServer ? false : true)\n  )\n\n  return result\n}\n\n// TODO: We want to get rid of this hook eventually\nexport function useServerHandoffComplete() {\n  let isHydrating = useIsHydratingInReact18()\n  let [complete, setComplete] = React.useState(env.isHandoffComplete)\n\n  if (complete && env.isHandoffComplete === false) {\n    // This means we are in a test environment and we need to reset the handoff state\n    // This kinda breaks the rules of React but this is only used for testing purposes\n    // And should theoretically be fine\n    setComplete(false)\n  }\n\n  React.useEffect(() => {\n    if (complete === true) return\n    setComplete(true)\n  }, [complete])\n\n  // Transition from pending to complete (forcing a re-render when server rendering)\n  React.useEffect(() => env.handoff(), [])\n\n  if (isHydrating) {\n    return false\n  }\n\n  return complete\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-slot.ts",
    "content": "import { useMemo } from 'react'\n\n// The only goal of this hook is to get a stable object reference using\n// `useMemo`. This is not used to optimize expensive calculations.\nexport function useSlot<ExpectedType extends Record<string, any>>(object: ExpectedType) {\n  return useMemo(() => object, Object.values(object))\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-store.ts",
    "content": "import { useSyncExternalStore } from 'react'\nimport type { Store } from '../utils/store'\n\nexport function useStore<T>(store: Store<T, any>) {\n  return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-sync-refs.ts",
    "content": "import { useEffect, useRef } from 'react'\nimport { useEvent } from './use-event'\n\nlet Optional = Symbol()\n\nexport function optionalRef<T>(cb: (ref: T) => void, isOptional = true) {\n  return Object.assign(cb, { [Optional]: isOptional })\n}\n\nexport function useSyncRefs<TType>(\n  ...refs: (React.MutableRefObject<TType | null> | ((instance: TType) => void) | null)[]\n) {\n  let cache = useRef(refs)\n\n  useEffect(() => {\n    cache.current = refs\n  }, [refs])\n\n  let syncRefs = useEvent((value: TType) => {\n    for (let ref of cache.current) {\n      if (ref == null) continue\n      if (typeof ref === 'function') ref(value)\n      else ref.current = value\n    }\n  })\n\n  return refs.every(\n    (ref) =>\n      ref == null ||\n      // @ts-expect-error\n      ref?.[Optional]\n  )\n    ? undefined\n    : syncRefs\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-tab-direction.ts",
    "content": "import { useRef } from 'react'\nimport { useWindowEvent } from './use-window-event'\n\nexport enum Direction {\n  Forwards,\n  Backwards,\n}\n\nexport function useTabDirection() {\n  let direction = useRef(Direction.Forwards)\n  let enabled = true\n\n  useWindowEvent(\n    enabled,\n    'keydown',\n    (event) => {\n      if (event.key === 'Tab') {\n        direction.current = event.shiftKey ? Direction.Backwards : Direction.Forwards\n      }\n    },\n    true\n  )\n\n  return direction\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-text-value.ts",
    "content": "import { useRef, type MutableRefObject } from 'react'\nimport { getTextValue } from '../utils/get-text-value'\nimport { useEvent } from './use-event'\n\nexport function useTextValue(element: MutableRefObject<HTMLElement | null>) {\n  let cacheKey = useRef<string>('')\n  let cacheValue = useRef<string>('')\n\n  return useEvent(() => {\n    let el = element.current\n    if (!el) return ''\n\n    // Check for a cached version\n    let currentKey = el.innerText\n    if (cacheKey.current === currentKey) {\n      return cacheValue.current\n    }\n\n    // Calculate the value\n    let value = getTextValue(el).trim().toLowerCase()\n    cacheKey.current = currentKey\n    cacheValue.current = value\n    return value\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-tracked-pointer.ts",
    "content": "import { useRef } from 'react'\n\ntype PointerPosition = [x: number, y: number]\n\nfunction eventToPosition(evt: PointerEvent): PointerPosition {\n  return [evt.screenX, evt.screenY]\n}\n\nexport function useTrackedPointer() {\n  let lastPos = useRef<PointerPosition>([-1, -1])\n\n  return {\n    wasMoved(evt: PointerEvent) {\n      // FIXME: Remove this once we use browser testing in all the relevant places.\n      // NOTE: This is replaced with a compile-time define during the build process\n      // This hack exists to work around a few failing tests caused by our inability to \"move\" the virtual pointer in JSDOM pointer events.\n      if (process.env.TEST_BYPASS_TRACKED_POINTER) {\n        return true\n      }\n\n      let newPos = eventToPosition(evt)\n\n      if (lastPos.current[0] === newPos[0] && lastPos.current[1] === newPos[1]) {\n        return false\n      }\n\n      lastPos.current = newPos\n      return true\n    },\n\n    update(evt: PointerEvent) {\n      lastPos.current = eventToPosition(evt)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-transition.ts",
    "content": "import { useRef, useState, type MutableRefObject } from 'react'\nimport { disposables } from '../utils/disposables'\nimport { useDisposables } from './use-disposables'\nimport { useFlags } from './use-flags'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\nif (\n  typeof process !== 'undefined' &&\n  typeof globalThis !== 'undefined' &&\n  typeof Element !== 'undefined' &&\n  // Strange string concatenation is on purpose to prevent `esbuild` from\n  // replacing `process.env.NODE_ENV` with `production` in the build output,\n  // eliminating this whole branch.\n  process?.env?.['NODE' + '_' + 'ENV'] === 'test'\n) {\n  if (typeof Element?.prototype?.getAnimations === 'undefined') {\n    Element.prototype.getAnimations = function getAnimationsPolyfill() {\n      console.warn(\n        [\n          'Headless UI has polyfilled `Element.prototype.getAnimations` for your tests.',\n          'Please install a proper polyfill e.g. `jsdom-testing-mocks`, to silence these warnings.',\n          '',\n          'Example usage:',\n          '```js',\n          \"import { mockAnimationsApi } from 'jsdom-testing-mocks'\",\n          'mockAnimationsApi()',\n          '```',\n        ].join('\\n')\n      )\n\n      return []\n    }\n  }\n}\n\n/**\n * ```\n * ┌──────┐                │        ┌──────────────┐\n * │Closed│                │        │Closed        │\n * └──────┘                │        └──────────────┘\n * ┌──────┐┌──────┐┌──────┐│┌──────┐┌──────┐┌──────┐\n * │Frame ││Frame ││Frame │││Frame ││Frame ││Frame │\n * └──────┘└──────┘└──────┘│└──────┘└──────┘└──────┘\n * ┌──────────────────────┐│┌──────────────────────┐\n * │Enter                 │││Leave                 │\n * └──────────────────────┘│└──────────────────────┘\n * ┌──────────────────────┐│┌──────────────────────┐\n * │Transition            │││Transition            │\n * ├──────────────────────┘│└──────────────────────┘\n * │\n * └─ Applied when `Enter` or `Leave` is applied.\n * ```\n */\nenum TransitionState {\n  None = 0,\n\n  Closed = 1 << 0,\n\n  Enter = 1 << 1,\n  Leave = 1 << 2,\n}\n\ntype TransitionData = {\n  closed?: boolean\n  enter?: boolean\n  leave?: boolean\n  transition?: boolean\n}\n\nexport function transitionDataAttributes(data: TransitionData) {\n  let attributes: Record<string, string> = {}\n  for (let key in data) {\n    if (data[key as keyof TransitionData] === true) {\n      attributes[`data-${key}`] = ''\n    }\n  }\n  return attributes\n}\n\nexport function useTransition(\n  enabled: boolean,\n  element: HTMLElement | null,\n  show: boolean,\n  events?: {\n    start?: (show: boolean) => void\n    end?: (show: boolean) => void\n  }\n): [visible: boolean, data: TransitionData] {\n  let [visible, setVisible] = useState(show)\n\n  let { hasFlag, addFlag, removeFlag } = useFlags(\n    enabled && visible ? TransitionState.Enter | TransitionState.Closed : TransitionState.None\n  )\n  let inFlight = useRef(false)\n  let cancelledRef = useRef(false)\n\n  let d = useDisposables()\n\n  useIsoMorphicEffect(() => {\n    if (!enabled) return\n\n    if (show) {\n      setVisible(true)\n    }\n\n    if (!element) {\n      if (show) {\n        addFlag(TransitionState.Enter | TransitionState.Closed)\n      }\n      return\n    }\n\n    events?.start?.(show)\n\n    return transition(element, {\n      inFlight,\n      prepare() {\n        if (cancelledRef.current) {\n          // Cancelled a cancellation, we're back to the original state.\n          cancelledRef.current = false\n        } else {\n          // If we were already in-flight, then we want to cancel the current\n          // transition.\n          cancelledRef.current = inFlight.current\n        }\n\n        inFlight.current = true\n\n        if (cancelledRef.current) return\n\n        if (show) {\n          addFlag(TransitionState.Enter | TransitionState.Closed)\n          removeFlag(TransitionState.Leave)\n        } else {\n          addFlag(TransitionState.Leave)\n          removeFlag(TransitionState.Enter)\n        }\n      },\n      run() {\n        if (cancelledRef.current) {\n          // If we cancelled a transition, then the `show` state is going to\n          // be inverted already, but that doesn't mean we have to go to that\n          // new state.\n          //\n          // What we actually want is to revert to the \"idle\" state (the\n          // stable state where an `Enter` transitions to, and a `Leave`\n          // transitions from.)\n          //\n          // Because of this, it might look like we are swapping the flags in\n          // the following branches, but that's not the case.\n          if (show) {\n            removeFlag(TransitionState.Enter | TransitionState.Closed)\n            addFlag(TransitionState.Leave)\n          } else {\n            removeFlag(TransitionState.Leave)\n            addFlag(TransitionState.Enter | TransitionState.Closed)\n          }\n        } else {\n          if (show) {\n            removeFlag(TransitionState.Closed)\n          } else {\n            addFlag(TransitionState.Closed)\n          }\n        }\n      },\n      done() {\n        if (cancelledRef.current) {\n          if (hasPendingTransitions(element)) {\n            return\n          }\n        }\n\n        inFlight.current = false\n\n        removeFlag(TransitionState.Enter | TransitionState.Leave | TransitionState.Closed)\n\n        if (!show) {\n          setVisible(false)\n        }\n\n        events?.end?.(show)\n      },\n    })\n  }, [enabled, show, element, d])\n\n  if (!enabled) {\n    return [\n      show,\n      {\n        closed: undefined,\n        enter: undefined,\n        leave: undefined,\n        transition: undefined,\n      },\n    ] as const\n  }\n\n  return [\n    visible,\n    {\n      closed: hasFlag(TransitionState.Closed),\n      enter: hasFlag(TransitionState.Enter),\n      leave: hasFlag(TransitionState.Leave),\n      transition: hasFlag(TransitionState.Enter) || hasFlag(TransitionState.Leave),\n    },\n  ] as const\n}\n\nfunction transition(\n  node: HTMLElement,\n  {\n    prepare,\n    run,\n    done,\n    inFlight,\n  }: {\n    prepare: () => void\n    run: () => void\n    done: () => void\n    inFlight: MutableRefObject<boolean>\n  }\n) {\n  let d = disposables()\n\n  // Prepare the transitions by ensuring that all the \"before\" classes are\n  // applied and flushed to the DOM.\n  prepareTransition(node, {\n    prepare,\n    inFlight,\n  })\n\n  // This is a workaround for a bug in all major browsers.\n  //\n  // 1. When an element is just mounted\n  // 2. And you apply a transition to it (e.g.: via a class)\n  // 3. And you're using `getComputedStyle` and read any returned value\n  // 4. Then the `transition` immediately jumps to the end state\n  //\n  // This means that no transition happens at all. To fix this, we delay the\n  // actual transition by one frame.\n  d.nextFrame(() => {\n    // Initiate the transition by applying the new classes.\n    run()\n\n    // Wait for the transition, once the transition is complete we can cleanup.\n    // We wait for a frame such that the browser has time to flush the changes\n    // to the DOM.\n    d.requestAnimationFrame(() => {\n      d.add(waitForTransition(node, done))\n    })\n  })\n\n  return d.dispose\n}\n\nfunction waitForTransition(node: HTMLElement | null, done: () => void) {\n  let d = disposables()\n  if (!node) return d.dispose\n\n  let cancelled = false\n  d.add(() => {\n    cancelled = true\n  })\n\n  let transitions =\n    node.getAnimations?.().filter((animation) => animation instanceof CSSTransition) ?? []\n  // If there are no transitions, we can stop early.\n  if (transitions.length === 0) {\n    done()\n    return d.dispose\n  }\n\n  // Wait for all the transitions to complete.\n  Promise.allSettled(transitions.map((transition) => transition.finished)).then(() => {\n    if (!cancelled) {\n      done()\n    }\n  })\n\n  return d.dispose\n}\n\nfunction prepareTransition(\n  node: HTMLElement,\n  { inFlight, prepare }: { inFlight?: MutableRefObject<boolean>; prepare: () => void }\n) {\n  // If we are already transitioning, then we don't need to force cancel the\n  // current transition (by triggering a reflow).\n  if (inFlight?.current) {\n    prepare()\n    return\n  }\n\n  let previous = node.style.transition\n\n  // Force cancel current transition\n  node.style.transition = 'none'\n\n  prepare()\n\n  // Trigger a reflow, flushing the CSS changes\n  node.offsetHeight\n\n  // Reset the transition to what it was before\n  node.style.transition = previous\n}\n\nfunction hasPendingTransitions(node: HTMLElement) {\n  let animations = node.getAnimations?.() ?? []\n\n  return animations.some((animation) => {\n    return animation instanceof CSSTransition && animation.playState !== 'finished'\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-tree-walker.ts",
    "content": "import { useEffect, useRef } from 'react'\nimport { getOwnerDocument } from '../utils/owner'\nimport { useIsoMorphicEffect } from './use-iso-morphic-effect'\n\ntype AcceptNode = (\n  node: HTMLElement\n) =>\n  | typeof NodeFilter.FILTER_ACCEPT\n  | typeof NodeFilter.FILTER_SKIP\n  | typeof NodeFilter.FILTER_REJECT\n\nexport function useTreeWalker(\n  enabled: boolean,\n  {\n    container,\n    accept,\n    walk,\n  }: {\n    container: HTMLElement | null\n    accept: AcceptNode\n    walk(node: HTMLElement): void\n  }\n) {\n  let acceptRef = useRef(accept)\n  let walkRef = useRef(walk)\n\n  useEffect(() => {\n    acceptRef.current = accept\n    walkRef.current = walk\n  }, [accept, walk])\n\n  useIsoMorphicEffect(() => {\n    if (!container) return\n    if (!enabled) return\n    let ownerDocument = getOwnerDocument(container)\n    if (!ownerDocument) return\n\n    let accept = acceptRef.current\n    let walk = walkRef.current\n\n    let acceptNode = Object.assign((node: HTMLElement) => accept(node), { acceptNode: accept })\n    let walker = ownerDocument.createTreeWalker(\n      container,\n      NodeFilter.SHOW_ELEMENT,\n      acceptNode,\n      // @ts-expect-error This `false` is a simple small fix for older browsers\n      false\n    )\n\n    while (walker.nextNode()) walk(walker.currentNode as HTMLElement)\n  }, [container, enabled, acceptRef, walkRef])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-watch.ts",
    "content": "import { useEffect, useRef } from 'react'\nimport { useEvent } from './use-event'\n\nexport function useWatch<T extends any[]>(\n  cb: (newValues: [...T], oldValues: [...T]) => void | (() => void),\n  dependencies: [...T]\n) {\n  let track = useRef([] as unknown as typeof dependencies)\n  let action = useEvent(cb)\n\n  useEffect(() => {\n    let oldValues = [...track.current] as [...T]\n\n    for (let [idx, value] of dependencies.entries()) {\n      if (track.current[idx] !== value) {\n        // At least 1 item changed\n        let returnValue = action(dependencies, oldValues)\n        track.current = dependencies\n        return returnValue\n      }\n    }\n  }, [action, ...dependencies])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/hooks/use-window-event.ts",
    "content": "import { useEffect } from 'react'\nimport { useLatestValue } from './use-latest-value'\n\nexport function useWindowEvent<TType extends keyof WindowEventMap>(\n  enabled: boolean,\n  type: TType,\n  listener: (ev: WindowEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  let listenerRef = useLatestValue(listener)\n\n  useEffect(() => {\n    if (!enabled) return\n\n    function handler(event: WindowEventMap[TType]) {\n      listenerRef.current(event)\n    }\n\n    window.addEventListener(type, handler, options)\n    return () => window.removeEventListener(type, handler, options)\n  }, [enabled, type, options])\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/index.test.ts",
    "content": "import * as HeadlessUI from './index'\n\n/**\n * Looks a bit of a silly test, however this ensures that we don't accidentally expose something to\n * the outside world that we didn't want!\n */\nit('should expose the correct components', () => {\n  expect(Object.keys(HeadlessUI).sort((a, z) => a.localeCompare(z))).toEqual([\n    'Button',\n\n    'Checkbox',\n\n    'CloseButton',\n\n    'Combobox',\n    'ComboboxButton',\n    'ComboboxInput',\n    'ComboboxLabel',\n    'ComboboxOption',\n    'ComboboxOptions',\n\n    'DataInteractive',\n\n    'Description',\n\n    'Dialog',\n    'DialogBackdrop',\n    'DialogDescription',\n    'DialogPanel',\n    'DialogTitle',\n\n    'Disclosure',\n    'DisclosureButton',\n    'DisclosurePanel',\n\n    'Field',\n    'Fieldset',\n\n    'FocusTrap',\n    'FocusTrapFeatures',\n\n    'Input',\n\n    'Label',\n\n    'Legend',\n\n    'Listbox',\n    'ListboxButton',\n    'ListboxLabel',\n    'ListboxOption',\n    'ListboxOptions',\n    'ListboxSelectedOption',\n\n    'Menu',\n    'MenuButton',\n    'MenuHeading',\n    'MenuItem',\n    'MenuItems',\n    'MenuSection',\n    'MenuSeparator',\n\n    'Popover',\n    'PopoverBackdrop',\n    'PopoverButton',\n    'PopoverGroup',\n    'PopoverOverlay',\n    'PopoverPanel',\n\n    'Portal',\n\n    'Radio',\n    'RadioGroup',\n    'RadioGroupDescription',\n    'RadioGroupLabel',\n    'RadioGroupOption',\n\n    'Select',\n\n    'Switch',\n    'SwitchDescription',\n    'SwitchGroup',\n    'SwitchLabel',\n\n    'Tab',\n    'TabGroup',\n    'TabList',\n    'TabPanel',\n    'TabPanels',\n\n    'Textarea',\n\n    'Transition',\n    'TransitionChild',\n\n    'useClose',\n  ])\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/index.ts",
    "content": "export * from './components/button/button'\nexport * from './components/checkbox/checkbox'\nexport * from './components/close-button/close-button'\nexport * from './components/combobox/combobox'\nexport * from './components/data-interactive/data-interactive'\nexport { Description, type DescriptionProps } from './components/description/description'\nexport * from './components/dialog/dialog'\nexport * from './components/disclosure/disclosure'\nexport * from './components/field/field'\nexport * from './components/fieldset/fieldset'\nexport * from './components/focus-trap/focus-trap'\nexport * from './components/input/input'\nexport { Label, type LabelProps } from './components/label/label'\nexport * from './components/legend/legend'\nexport * from './components/listbox/listbox'\nexport * from './components/menu/menu'\nexport * from './components/popover/popover'\nexport { Portal } from './components/portal/portal'\nexport * from './components/radio-group/radio-group'\nexport * from './components/select/select'\nexport * from './components/switch/switch'\nexport * from './components/tabs/tabs'\nexport * from './components/textarea/textarea'\nexport { useClose } from './internal/close-provider'\n// TODO: Enable when ready\n// export * from './components/tooltip/tooltip'\nexport * from './components/transition/transition'\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/close-provider.tsx",
    "content": "'use client'\n\nimport React, { createContext, useContext } from 'react'\n\nlet CloseContext = createContext(() => {})\n\nexport function useClose() {\n  return useContext(CloseContext)\n}\n\nexport function CloseProvider({ value, children }: React.PropsWithChildren<{ value: () => void }>) {\n  return <CloseContext.Provider value={value}>{children}</CloseContext.Provider>\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/disabled.tsx",
    "content": "import React, { createContext, useContext } from 'react'\n\nlet DisabledContext = createContext<boolean | undefined>(undefined)\n\nexport function useDisabled() {\n  return useContext(DisabledContext)\n}\n\nexport function DisabledProvider({\n  value,\n  children,\n}: React.PropsWithChildren<{ value?: boolean }>) {\n  return <DisabledContext.Provider value={value}>{children}</DisabledContext.Provider>\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/floating.tsx",
    "content": "import {\n  autoUpdate,\n  flip as flipMiddleware,\n  inner as innerMiddleware,\n  offset as offsetMiddleware,\n  shift as shiftMiddleware,\n  size as sizeMiddleware,\n  useFloating,\n  useInnerOffset,\n  useInteractions,\n  type InnerProps,\n  type UseFloatingReturn,\n} from '@floating-ui/react'\nimport * as React from 'react'\nimport { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react'\nimport { useDisposables } from '../hooks/use-disposables'\nimport { useEvent } from '../hooks/use-event'\nimport { useIsoMorphicEffect } from '../hooks/use-iso-morphic-effect'\nimport * as DOM from '../utils/dom'\n\ntype Align = 'start' | 'end'\ntype Placement = 'top' | 'right' | 'bottom' | 'left'\ntype AnchorTo = `${Placement}` | `${Placement} ${Align}`\ntype AnchorToWithSelection = `${Placement | 'selection'}` | `${Placement | 'selection'} ${Align}`\n\ntype BaseAnchorProps = {\n  /**\n   * The `gap` is the space between the trigger and the panel.\n   */\n  gap: number | string // For `var()` support\n\n  /**\n   * The `offset` is the amount the panel should be nudged from its original position.\n   */\n  offset: number | string // For `var()` support\n\n  /**\n   * The `padding` is the minimum space between the panel and the viewport.\n   */\n  padding: number | string // For `var()` support\n}\n\nexport type AnchorProps =\n  | false // Disable entirely\n  | AnchorTo // String value to define the placement\n  | Partial<\n      BaseAnchorProps & {\n        /**\n         * The `to` value defines which side of the trigger the panel should be placed on and its\n         * alignment.\n         */\n        to: AnchorTo\n      }\n    >\n\nexport type AnchorPropsWithSelection =\n  | false // Disable entirely\n  | AnchorToWithSelection\n  | Partial<\n      BaseAnchorProps & {\n        /**\n         * The `to` value defines which side of the trigger the panel should be placed on and its\n         * alignment.\n         */\n        to: AnchorToWithSelection\n      }\n    >\n\nexport type InternalFloatingPanelProps = Partial<{\n  inner: {\n    listRef: InnerProps['listRef']\n    index: InnerProps['index']\n  }\n}>\n\nlet FloatingContext = createContext<{\n  styles?: UseFloatingReturn<any>['floatingStyles']\n  setReference: UseFloatingReturn<any>['refs']['setReference']\n  setFloating: UseFloatingReturn<any>['refs']['setFloating']\n  getReferenceProps: ReturnType<typeof useInteractions>['getReferenceProps']\n  getFloatingProps: ReturnType<typeof useInteractions>['getFloatingProps']\n  slot: Partial<{\n    anchor: AnchorToWithSelection\n  }>\n}>({\n  styles: undefined,\n  setReference: () => {},\n  setFloating: () => {},\n  getReferenceProps: () => ({}),\n  getFloatingProps: () => ({}),\n  slot: {},\n})\nFloatingContext.displayName = 'FloatingContext'\nlet PlacementContext = createContext<\n  ((value: Exclude<AnchorPropsWithSelection, boolean> | null) => void) | null\n>(null)\nPlacementContext.displayName = 'PlacementContext'\n\nexport function useResolvedAnchor<T extends AnchorProps | AnchorPropsWithSelection>(\n  anchor?: T\n): Exclude<T, boolean | string> | null {\n  return useMemo(() => {\n    if (!anchor) return null // Disable entirely\n    if (typeof anchor === 'string') return { to: anchor } as Exclude<T, boolean | string> // Simple string based value,\n    return anchor as Exclude<T, boolean | string> // User-provided value\n  }, [anchor])\n}\n\nexport function useFloatingReference() {\n  return useContext(FloatingContext).setReference\n}\n\nexport function useFloatingReferenceProps() {\n  return useContext(FloatingContext).getReferenceProps\n}\n\nexport function useFloatingPanelProps() {\n  let { getFloatingProps, slot } = useContext(FloatingContext)\n  return useCallback(\n    (...args: Parameters<typeof getFloatingProps>) => {\n      return Object.assign({}, getFloatingProps(...args), {\n        'data-anchor': slot.anchor,\n      })\n    },\n    [getFloatingProps, slot]\n  )\n}\n\nexport function useFloatingPanel(\n  placement: (AnchorPropsWithSelection & InternalFloatingPanelProps) | null = null\n) {\n  if (placement === false) placement = null // Disable entirely\n  if (typeof placement === 'string') placement = { to: placement } // Simple string based value\n\n  let updatePlacementConfig = useContext(PlacementContext)\n  let stablePlacement = useMemo(\n    () => placement,\n    [\n      JSON.stringify(placement, (_, v) => {\n        // When we are trying to stringify a DOM element, we want to return the\n        // `outerHTML` of the element. In all other cases, we want to return the\n        // value as-is.\n        // It's not safe enough to check whether `v` is an instanceof\n        // `HTMLElement` because some tools (like AG Grid) polyfill it to be `{}`.\n        return v?.outerHTML ?? v\n      }),\n    ]\n  )\n  useIsoMorphicEffect(() => {\n    updatePlacementConfig?.(stablePlacement ?? null)\n  }, [updatePlacementConfig, stablePlacement])\n\n  let context = useContext(FloatingContext)\n\n  return useMemo(\n    () => [context.setFloating, placement ? context.styles : {}] as const,\n    [context.setFloating, placement, context.styles]\n  )\n}\n\n// TODO: Make this a config part of the `config`. Just need to decide on a name.\nlet MINIMUM_ITEMS_VISIBLE = 4\n\nexport function FloatingProvider({\n  children,\n  enabled = true,\n}: {\n  children: React.ReactNode\n  enabled?: boolean\n}) {\n  let [config, setConfig] = useState<\n    (AnchorPropsWithSelection & InternalFloatingPanelProps) | null\n  >(null)\n  let [innerOffset, setInnerOffset] = useState(0)\n  let overflowRef = useRef(null)\n\n  let [floatingEl, setFloatingElement] = useState<HTMLElement | null>(null)\n  useFixScrollingPixel(floatingEl)\n\n  let isEnabled = enabled && config !== null && floatingEl !== null\n\n  let {\n    to: placement = 'bottom',\n    gap = 0,\n    offset = 0,\n    padding = 0,\n    inner,\n  } = useResolvedConfig(config, floatingEl)\n  let [to, align = 'center'] = placement.split(' ') as [Placement | 'selection', Align | 'center']\n\n  // Reset\n  useIsoMorphicEffect(() => {\n    if (!isEnabled) return\n    setInnerOffset(0)\n  }, [isEnabled])\n\n  let { refs, floatingStyles, context } = useFloating({\n    open: isEnabled,\n\n    placement:\n      to === 'selection'\n        ? align === 'center'\n          ? 'bottom'\n          : `bottom-${align}`\n        : align === 'center'\n          ? `${to}`\n          : `${to}-${align}`,\n\n    // This component will be used in combination with a `Portal`, which means the floating\n    // element will be rendered outside of the current DOM tree.\n    strategy: 'absolute',\n\n    // We use the panel in a `Dialog` which is making the page inert, therefore no re-positioning is\n    // needed when scrolling changes.\n    transform: false,\n\n    middleware: [\n      // - The `mainAxis` is set to `gap` which defines the gap between the panel and the\n      //   trigger/reference.\n      // - The `crossAxis` is set to `offset` which nudges the panel from its original position.\n      //\n      // When we are showing the panel on top of the selected item, we don't want a gap between the\n      // reference and the panel, therefore setting the `mainAxis` to `0`.\n      offsetMiddleware({\n        mainAxis: to === 'selection' ? 0 : gap,\n        crossAxis: offset,\n      }),\n\n      // When the panel overflows the viewport, we will try to nudge the panel to the other side to\n      // ensure it's not clipped. We use the `padding` to define the  minimum space between the\n      // panel and the viewport.\n      shiftMiddleware({ padding }),\n\n      // The `flip` middleware will swap the `placement` of the panel if there is not enough room.\n      // This is not compatible with the `inner` middleware (which is only enabled when `to` is set\n      // to \"selection\").\n      to !== 'selection' && flipMiddleware({ padding }),\n\n      // The `inner` middleware will ensure the panel is always fully visible on screen and\n      // positioned on top of the reference and moved to the currently selected item.\n      to === 'selection' && inner\n        ? innerMiddleware({\n            ...inner,\n            padding, // For overflow detection\n            overflowRef,\n            offset: innerOffset,\n            minItemsVisible: MINIMUM_ITEMS_VISIBLE,\n            referenceOverflowThreshold: padding,\n            onFallbackChange(fallback) {\n              if (!fallback) return\n              let parent = context.elements.floating\n              if (!parent) return\n              let scrollPaddingBottom =\n                parseFloat(getComputedStyle(parent!).scrollPaddingBottom) || 0\n\n              // We want at least X visible items, but if there are less than X items in the list,\n              // we want to show as many as possible.\n              let missing = Math.min(MINIMUM_ITEMS_VISIBLE, parent.childElementCount)\n\n              let elementHeight = 0\n              let elementAmountVisible = 0\n\n              for (let child of context.elements.floating?.childNodes ?? []) {\n                if (DOM.isHTMLElement(child)) {\n                  let childTop = child.offsetTop\n                  // It can be that the child is fully visible, but we also want to keep the scroll\n                  // padding into account to ensure the UI looks good. Therefore we fake that the\n                  // bottom of the child is actually `scrollPaddingBottom` amount of pixels lower.\n                  let childBottom = childTop + child.clientHeight + scrollPaddingBottom\n\n                  let parentTop = parent.scrollTop\n                  let parentBottom = parentTop + parent.clientHeight\n\n                  // Figure out if the child is fully visible in the scroll parent.\n                  if (childTop >= parentTop && childBottom <= parentBottom) {\n                    missing--\n                  } else {\n                    // Not fully visible, so we will use this child to calculate the height of\n                    // each item. We will also use this to calculate how much of the item is\n                    // already visible.\n                    elementAmountVisible = Math.max(\n                      0,\n                      Math.min(childBottom, parentBottom) - Math.max(childTop, parentTop)\n                    )\n                    elementHeight = child.clientHeight\n                    break\n                  }\n                }\n              }\n\n              // There are fewer visible items than we want, so we will try to nudge the offset\n              // to show more items.\n              if (missing >= 1) {\n                setInnerOffset((existingOffset) => {\n                  let newInnerOffset =\n                    elementHeight * missing - // `missing` amount of `elementHeight`\n                    elementAmountVisible + // The amount of the last item that is visible\n                    scrollPaddingBottom // The scroll padding to ensure the UI looks good\n\n                  // Nudged enough already, no need to continue\n                  if (existingOffset >= newInnerOffset) {\n                    return existingOffset\n                  }\n\n                  return newInnerOffset\n                })\n              }\n            },\n          })\n        : null,\n\n      // The `size` middleware will ensure the panel is never bigger than the viewport minus the\n      // provided `padding` that we want.\n      sizeMiddleware({\n        padding,\n        apply({ availableWidth, availableHeight, elements }) {\n          Object.assign(elements.floating.style, {\n            overflow: 'auto',\n            maxWidth: `${availableWidth}px`,\n            maxHeight: `min(var(--anchor-max-height, 100vh), ${availableHeight}px)`,\n          })\n        },\n      }),\n    ].filter(Boolean),\n    whileElementsMounted: autoUpdate,\n  })\n\n  // Calculate placement information to expose as data attributes\n  let [exposedTo = to, exposedAlign = align] = context.placement.split('-')\n  // If user-land code is using custom styles specifically for `bottom`, but\n  // they chose `selection`, then we want to make sure to map it to selection\n  // again otherwise styles could be wrong.\n  if (to === 'selection') exposedTo = 'selection'\n\n  let data = useMemo(\n    () => ({\n      anchor: [exposedTo, exposedAlign].filter(Boolean).join(' ') as React.ContextType<\n        typeof FloatingContext\n      >['slot']['anchor'],\n    }),\n    [exposedTo, exposedAlign]\n  )\n\n  let innerOffsetConfig = useInnerOffset(context, {\n    overflowRef,\n    onChange: setInnerOffset,\n  })\n  let { getReferenceProps, getFloatingProps } = useInteractions([innerOffsetConfig])\n\n  let setFloatingRef = useEvent((el: HTMLElement | null) => {\n    setFloatingElement(el)\n    refs.setFloating(el)\n  })\n\n  return (\n    <PlacementContext.Provider value={setConfig}>\n      <FloatingContext.Provider\n        value={{\n          setFloating: setFloatingRef,\n          setReference: refs.setReference,\n          styles: floatingStyles,\n          getReferenceProps,\n          getFloatingProps,\n          slot: data,\n        }}\n      >\n        {children}\n      </FloatingContext.Provider>\n    </PlacementContext.Provider>\n  )\n}\n\nfunction useFixScrollingPixel(element: HTMLElement | null) {\n  useIsoMorphicEffect(() => {\n    if (!element) return\n\n    let observer = new MutationObserver(() => {\n      let maxHeight = window.getComputedStyle(element).maxHeight\n\n      let maxHeightFloat = parseFloat(maxHeight)\n      if (isNaN(maxHeightFloat)) return\n\n      let maxHeightInt = parseInt(maxHeight)\n      if (isNaN(maxHeightInt)) return\n\n      if (maxHeightFloat !== maxHeightInt) {\n        element.style.maxHeight = `${Math.ceil(maxHeightFloat)}px`\n      }\n    })\n\n    observer.observe(element, {\n      attributes: true,\n      attributeFilter: ['style'],\n    })\n\n    return () => {\n      observer.disconnect()\n    }\n  }, [element])\n}\n\nfunction useResolvedConfig(\n  config: (Exclude<AnchorPropsWithSelection, boolean | string> & InternalFloatingPanelProps) | null,\n  element?: HTMLElement | null\n) {\n  let gap = useResolvePxValue(config?.gap ?? 'var(--anchor-gap, 0)', element)\n  let offset = useResolvePxValue(config?.offset ?? 'var(--anchor-offset, 0)', element)\n  let padding = useResolvePxValue(config?.padding ?? 'var(--anchor-padding, 0)', element)\n\n  return { ...config, gap, offset, padding }\n}\n\nfunction useResolvePxValue(\n  input?: string | number,\n  element?: HTMLElement | null,\n  defaultValue: number | undefined = undefined\n) {\n  let d = useDisposables()\n  let computeValue = useEvent((value?: string | number, element?: HTMLElement | null) => {\n    // Nullish\n    if (value == null) return [defaultValue, null] as const\n\n    // Number as-is\n    if (typeof value === 'number') return [value, null] as const\n\n    // String values, the interesting part\n    if (typeof value === 'string') {\n      if (!element) return [defaultValue, null] as const\n\n      let result = resolveCSSVariablePxValue(value, element)\n\n      return [\n        result,\n        (setValue: (value?: number) => void) => {\n          let variables = resolveVariables(value)\n\n          // TODO: Improve this part and make it work\n          //\n          // Observe variables themselves. Currently the browser doesn't support this, but the\n          // variables we are interested in resolve to a pixel value. Which means that we can use\n          // this variable in the `margin` of an element. Then we can observe the `margin` of the\n          // element and we will be notified when the variable changes.\n          //\n          // if (typeof ResizeObserver !== 'undefined') {\n          //   let tmpEl = document.createElement('div')\n          //   element.appendChild(tmpEl)\n          //\n          //   // Didn't use `fontSize` because a `fontSize` can't be negative.\n          //   tmpEl.style.setProperty('margin-top', '0px', 'important')\n          //\n          //   // Set the new value, if this is invalid the previous value will be used.\n          //   tmpEl.style.setProperty('margin-top', value, 'important')\n          //\n          //   let observer = new ResizeObserver(() => {\n          //     let newResult = resolveCSSVariableValue(value, element)\n          //\n          //     if (result !== newResult) {\n          //       setValue(newResult)\n          //       result = newResult\n          //     }\n          //   })\n          //   observer.observe(tmpEl)\n          //   d.add(() => observer.disconnect())\n          //   return d.dispose\n          // }\n\n          // Works as a fallback, but not very performant because we are polling the value.\n          {\n            let history = variables.map((variable) =>\n              window.getComputedStyle(element!).getPropertyValue(variable)\n            )\n\n            d.requestAnimationFrame(function check() {\n              d.nextFrame(check)\n\n              // Fast path, detect if the value of the CSS Variable has changed before completely\n              // computing the new value. Once we use `resolveCSSVariablePxValue` we will have to\n              // compute the actual px value by injecting a temporary element into the DOM.\n              //\n              // This is a lot of work, so we want to avoid it if possible.\n              let changed = false\n              for (let [idx, variable] of variables.entries()) {\n                let value = window.getComputedStyle(element!).getPropertyValue(variable)\n                if (history[idx] !== value) {\n                  history[idx] = value\n                  changed = true\n                  break\n                }\n              }\n\n              // Nothing changed, no need to perform the expensive computation.\n              if (!changed) return\n\n              let newResult = resolveCSSVariablePxValue(value, element)\n\n              if (result !== newResult) {\n                setValue(newResult)\n                result = newResult\n              }\n            })\n          }\n\n          return d.dispose\n        },\n      ] as const\n    }\n\n    return [defaultValue, null] as const\n  })\n\n  // Calculate the value immediately when the input or element changes. Later we can setup a watcher\n  // to track the value changes over time.\n  let immediateValue = useMemo(() => computeValue(input, element)[0], [input, element])\n  let [value = immediateValue, setValue] = useState<number | undefined>()\n\n  useIsoMorphicEffect(() => {\n    let [value, watcher] = computeValue(input, element)\n    setValue(value)\n\n    if (!watcher) return\n    return watcher(setValue)\n  }, [input, element])\n\n  return value\n}\n\nfunction resolveVariables(value: string): string[] {\n  let matches = /var\\((.*)\\)/.exec(value)\n  if (matches) {\n    let idx = matches[1].indexOf(',')\n    if (idx === -1) {\n      return [matches[1]]\n    }\n\n    let variable = matches[1].slice(0, idx).trim()\n    let fallback = matches[1].slice(idx + 1).trim()\n\n    if (fallback) {\n      return [variable, ...resolveVariables(fallback)]\n    }\n\n    return [variable]\n  }\n\n  return []\n}\n\nfunction resolveCSSVariablePxValue(input: string, element: HTMLElement) {\n  // Resolve the value: Instead of trying to compute the value ourselves by converting rem /\n  // vwh / ... values to pixels or by parsing out the fallback values and evaluating it\n  // (because it can contain calc expressions or other variables).\n  //\n  // We will let the browser compute all of it by creating a temporary element and setting\n  // the value as a CSS variable. Then we can read the computed value from the browser.\n  //\n  //\n  // BUG REPORT ABOUT INCORRECT VALUES, look here:\n  // ---------------------------------------------\n  //\n  // Currently this technically contains a bug because we are rendering a new element inside of the\n  // current element. Which means that if the passed in element has CSS that looks like:\n  //\n  // ```css\n  // .the-element {\n  //   --the-variable: 1rem\n  // }\n  //\n  // .the-element > * {\n  //   --the-variable: 2rem\n  // }\n  // ```\n  //\n  // Then this will result to resolved value of `2rem`, instead of `1rem`\n  let tmpEl = document.createElement('div')\n  element.appendChild(tmpEl)\n\n  // Set the value to `0px` otherwise if an invalid value is provided later the browser will read\n  // out the default value.\n  //\n  // Didn't use `fontSize` because a `fontSize` can't be negative.\n  tmpEl.style.setProperty('margin-top', '0px', 'important')\n\n  // Set the new value, if this is invalid the previous value will be used.\n  tmpEl.style.setProperty('margin-top', input, 'important')\n\n  // Reading the `margin-top` will already be in pixels (e.g.: 123px).\n  let pxValue = parseFloat(window.getComputedStyle(tmpEl).marginTop) || 0\n  element.removeChild(tmpEl)\n\n  return pxValue\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/focus-sentinel.tsx",
    "content": "import React, { useState, type FocusEvent as ReactFocusEvent } from 'react'\nimport { useIsMounted } from '../hooks/use-is-mounted'\nimport { Hidden, HiddenFeatures } from './hidden'\n\ninterface FocusSentinelProps {\n  onFocus(): boolean\n}\n\nexport function FocusSentinel({ onFocus }: FocusSentinelProps) {\n  let [enabled, setEnabled] = useState(true)\n  let mounted = useIsMounted()\n\n  if (!enabled) return null\n\n  return (\n    <Hidden\n      as=\"button\"\n      type=\"button\"\n      features={HiddenFeatures.Focusable}\n      onFocus={(event: ReactFocusEvent) => {\n        event.preventDefault()\n        let frame: ReturnType<typeof requestAnimationFrame>\n\n        let tries = 50\n        function forwardFocus() {\n          // Prevent infinite loops\n          if (tries-- <= 0) {\n            if (frame) cancelAnimationFrame(frame)\n            return\n          }\n\n          // Try to move focus to the correct element. This depends on the implementation\n          // of `onFocus` of course since it would be different for each place we use it in.\n          if (onFocus()) {\n            cancelAnimationFrame(frame)\n            if (!mounted.current) return\n\n            setEnabled(false)\n            return\n          }\n\n          // Retry\n          frame = requestAnimationFrame(forwardFocus)\n        }\n\n        frame = requestAnimationFrame(forwardFocus)\n      }}\n    />\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/form-fields.tsx",
    "content": "import React, { createContext, useContext, useEffect, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useDisposables } from '../hooks/use-disposables'\nimport { objectToFormEntries } from '../utils/form'\nimport { compact } from '../utils/render'\nimport { Hidden, HiddenFeatures } from './hidden'\n\nlet FormFieldsContext = createContext<{ target: HTMLElement | null } | null>(null)\n\nexport function FormFieldsProvider(props: React.PropsWithChildren<{}>) {\n  let [target, setTarget] = useState<HTMLElement | null>(null)\n\n  return (\n    <FormFieldsContext.Provider value={{ target }}>\n      {props.children}\n      <Hidden features={HiddenFeatures.Hidden} ref={setTarget} />\n    </FormFieldsContext.Provider>\n  )\n}\n\nexport function HoistFormFields({ children }: React.PropsWithChildren<{}>) {\n  let formFieldsContext = useContext(FormFieldsContext)\n  if (!formFieldsContext) return <>{children}</>\n\n  let { target } = formFieldsContext\n  return target\n    ? createPortal(<>{children}</>, target) //\n    : null // We know for sure that we are in a `FormFieldsContext`, but the DOM element where we want to render is not ready yet. Let's render nothing for now until that element is ready.\n}\n\nexport function FormFields({\n  data,\n  form: formId,\n  disabled,\n  onReset,\n  overrides,\n}: {\n  data: Record<string, any>\n  overrides?: Record<string, any>\n  form?: string\n  disabled?: boolean\n  onReset?: (e: Event) => void\n}) {\n  let [form, setForm] = useState<HTMLFormElement | null>(null)\n\n  let d = useDisposables()\n  useEffect(() => {\n    if (!onReset) return\n    if (!form) return\n\n    return d.addEventListener(form, 'reset', onReset)\n  }, [form, formId, onReset])\n\n  return (\n    <HoistFormFields>\n      <FormResolver setForm={setForm} formId={formId} />\n      {objectToFormEntries(data).map(([name, value]) => {\n        return (\n          <Hidden\n            features={HiddenFeatures.Hidden}\n            {...compact({\n              key: name,\n              as: 'input',\n              type: 'hidden',\n              hidden: true,\n              readOnly: true,\n              form: formId,\n              disabled,\n              name,\n              value,\n              ...overrides,\n            })}\n          />\n        )\n      })}\n    </HoistFormFields>\n  )\n}\n\nfunction FormResolver({\n  setForm,\n  formId,\n}: {\n  setForm: (form: HTMLFormElement) => void\n  formId?: string\n}) {\n  useEffect(() => {\n    if (formId) {\n      let resolvedForm = document.getElementById(formId) as HTMLFormElement\n      if (resolvedForm) setForm(resolvedForm)\n    }\n  }, [setForm, formId])\n\n  return formId ? null : (\n    <Hidden\n      features={HiddenFeatures.Hidden}\n      as=\"input\"\n      type=\"hidden\"\n      hidden\n      readOnly\n      ref={(el) => {\n        if (!el) return\n        let resolvedForm = el.closest('form')\n        if (resolvedForm) setForm(resolvedForm)\n      }}\n    />\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/frozen.tsx",
    "content": "import React, { cloneElement, isValidElement, useState } from 'react'\n\nfunction FrozenFn(\n  { children, freeze }: { children: React.ReactNode; freeze: boolean },\n  ref: React.ForwardedRef<HTMLElement>\n) {\n  let contents = useFrozenData(freeze, children)\n\n  if (isValidElement(contents)) {\n    return cloneElement(contents as React.ReactElement, { ref })\n  }\n\n  return <>{contents}</>\n}\n\nexport const Frozen = React.forwardRef(FrozenFn)\n\nexport function useFrozenData<T>(freeze: boolean, data: T) {\n  let [frozenValue, setFrozenValue] = useState(data)\n\n  // We should keep updating the frozen value, as long as we shouldn't freeze\n  // the value yet. The moment we should freeze the value we stop updating it\n  // which allows us to reference the \"previous\" (thus frozen) value.\n  if (!freeze && frozenValue !== data) {\n    setFrozenValue(data)\n  }\n\n  return freeze ? frozenValue : data\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/hidden.tsx",
    "content": "import type { ElementType, Ref } from 'react'\nimport type { Props } from '../types'\nimport { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../utils/render'\n\nlet DEFAULT_VISUALLY_HIDDEN_TAG = 'span' as const\n\nexport enum HiddenFeatures {\n  // The default, no features.\n  None = 1 << 0,\n\n  // Whether the element should be focusable or not.\n  Focusable = 1 << 1,\n\n  // Whether it should be completely hidden, even to assistive technologies.\n  Hidden = 1 << 2,\n}\n\ntype HiddenRenderPropArg = {}\ntype HiddenPropsWeControl = never\nexport type HiddenProps<TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDEN_TAG> = Props<\n  TTag,\n  HiddenRenderPropArg,\n  HiddenPropsWeControl,\n  { features?: HiddenFeatures }\n>\n\nfunction VisuallyHidden<TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDEN_TAG>(\n  props: HiddenProps<TTag>,\n  ref: Ref<HTMLElement>\n) {\n  let { features = HiddenFeatures.None, ...theirProps } = props\n  let ourProps = {\n    ref,\n    'aria-hidden':\n      (features & HiddenFeatures.Focusable) === HiddenFeatures.Focusable\n        ? true\n        : theirProps['aria-hidden'] ?? undefined,\n    hidden: (features & HiddenFeatures.Hidden) === HiddenFeatures.Hidden ? true : undefined,\n    style: {\n      position: 'fixed',\n      top: 1,\n      left: 1,\n      width: 1,\n      height: 0,\n      padding: 0,\n      margin: -1,\n      overflow: 'hidden',\n      clip: 'rect(0, 0, 0, 0)',\n      whiteSpace: 'nowrap',\n      borderWidth: '0',\n      ...((features & HiddenFeatures.Hidden) === HiddenFeatures.Hidden &&\n        !((features & HiddenFeatures.Focusable) === HiddenFeatures.Focusable) && {\n          display: 'none',\n        }),\n    },\n  }\n\n  let render = useRender()\n\n  return render({\n    ourProps,\n    theirProps,\n    slot: {},\n    defaultTag: DEFAULT_VISUALLY_HIDDEN_TAG,\n    name: 'Hidden',\n  })\n}\n\ninterface ComponentHidden extends HasDisplayName {\n  <TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDEN_TAG>(\n    props: HiddenProps<TTag> & RefProp<typeof VisuallyHidden>\n  ): React.JSX.Element\n}\n\nexport let Hidden = forwardRefWithAs(VisuallyHidden) as ComponentHidden\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/id.tsx",
    "content": "import React, { createContext, useContext } from 'react'\n\nlet IdContext = createContext<string | undefined>(undefined)\n\nexport function useProvidedId() {\n  return useContext(IdContext)\n}\n\nexport function IdProvider({ id, children }: React.PropsWithChildren<{ id: string | undefined }>) {\n  return <IdContext.Provider value={id}>{children}</IdContext.Provider>\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/open-closed.tsx",
    "content": "import React, { createContext, useContext, type ReactElement, type ReactNode } from 'react'\n\nlet Context = createContext<State | null>(null)\nContext.displayName = 'OpenClosedContext'\n\nexport enum State {\n  Open = 1 << 0,\n  Closed = 1 << 1,\n  Closing = 1 << 2,\n  Opening = 1 << 3,\n}\n\nexport function useOpenClosed() {\n  return useContext(Context)\n}\n\ninterface Props {\n  value: State\n  children: ReactNode\n}\n\nexport function OpenClosedProvider({ value, children }: Props): ReactElement {\n  return <Context.Provider value={value}>{children}</Context.Provider>\n}\n\nexport function ResetOpenClosedProvider({ children }: { children: React.ReactNode }): ReactElement {\n  return <Context.Provider value={null}>{children}</Context.Provider>\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/internal/portal-force-root.tsx",
    "content": "import React, { createContext, useContext, type ReactNode } from 'react'\n\nlet ForcePortalRootContext = createContext(false)\n\nexport function usePortalRoot() {\n  return useContext(ForcePortalRootContext)\n}\n\ninterface ForcePortalRootProps {\n  force: boolean\n  children: ReactNode\n}\n\nexport function ForcePortalRoot(props: ForcePortalRootProps) {\n  return (\n    <ForcePortalRootContext.Provider value={props.force}>\n      {props.children}\n    </ForcePortalRootContext.Provider>\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/machine.ts",
    "content": "import { DefaultMap } from './utils/default-map'\nimport { disposables } from './utils/disposables'\nimport { env } from './utils/env'\n\nexport abstract class Machine<State, Event extends { type: number | string }> {\n  #state: State = {} as State\n  #eventSubscribers = new DefaultMap<\n    Event['type'],\n    Set<(state: State, event: Extract<Event, { type: any }>) => void>\n  >(() => new Set())\n  #subscribers: Set<Subscriber<State, any>> = new Set()\n\n  disposables = disposables()\n\n  constructor(initialState: State) {\n    this.#state = initialState\n\n    if (env.isServer) {\n      // Cleanup any disposables that were registerd on the server-side\n      this.disposables.microTask(() => {\n        this.dispose()\n      })\n    }\n  }\n\n  dispose() {\n    this.disposables.dispose()\n  }\n\n  get state(): Readonly<State> {\n    return this.#state\n  }\n\n  abstract reduce(state: Readonly<State>, event: Event): Readonly<State>\n\n  subscribe<Slice>(\n    selector: (state: Readonly<State>) => Slice,\n    callback: (state: Slice) => void\n  ): () => void {\n    if (env.isServer) return () => {}\n\n    let subscriber: Subscriber<State, Slice> = {\n      selector,\n      callback,\n      current: selector(this.#state),\n    }\n    this.#subscribers.add(subscriber)\n\n    return this.disposables.add(() => {\n      this.#subscribers.delete(subscriber)\n    })\n  }\n\n  on<T extends Event['type']>(\n    type: T,\n    callback: (state: State, event: Extract<Event, { type: T }>) => void\n  ) {\n    if (env.isServer) return () => {}\n\n    this.#eventSubscribers.get(type).add(callback)\n    return this.disposables.add(() => {\n      this.#eventSubscribers.get(type).delete(callback)\n    })\n  }\n\n  send(event: Event) {\n    let newState = this.reduce(this.#state, event)\n    if (newState === this.#state) return // No change\n\n    this.#state = newState\n\n    for (let subscriber of this.#subscribers) {\n      let slice = subscriber.selector(this.#state)\n      if (shallowEqual(subscriber.current, slice)) continue\n\n      subscriber.current = slice\n      subscriber.callback(slice)\n    }\n\n    for (let callback of this.#eventSubscribers.get(event.type)) {\n      callback(this.#state, event as any)\n    }\n  }\n}\n\ninterface Subscriber<State, Slice> {\n  selector: (state: Readonly<State>) => Slice\n  callback: (state: Slice) => void\n  current: Slice\n}\n\nexport function shallowEqual(a: any, b: any): boolean {\n  // Exact same reference\n  if (Object.is(a, b)) return true\n\n  // Must be some type of object\n  if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false\n\n  // Arrays\n  if (Array.isArray(a) && Array.isArray(b)) {\n    if (a.length !== b.length) return false\n    return compareEntries(a[Symbol.iterator](), b[Symbol.iterator]())\n  }\n\n  // Map and Set\n  if ((a instanceof Map && b instanceof Map) || (a instanceof Set && b instanceof Set)) {\n    if (a.size !== b.size) return false\n    return compareEntries(a.entries(), b.entries())\n  }\n\n  // Plain objects\n  if (isPlainObject(a) && isPlainObject(b)) {\n    return compareEntries(\n      Object.entries(a)[Symbol.iterator](),\n      Object.entries(b)[Symbol.iterator]()\n    )\n  }\n\n  // TODO: Not sure how to compare other types of objects\n  return false\n}\n\nfunction compareEntries(a: IterableIterator<any>, b: IterableIterator<any>): boolean {\n  do {\n    let aResult = a.next()\n    let bResult = b.next()\n\n    if (aResult.done && bResult.done) return true\n    if (aResult.done || bResult.done) return false\n\n    if (!Object.is(aResult.value, bResult.value)) return false\n  } while (true)\n}\n\nfunction isPlainObject<T>(value: T): value is T & Record<keyof T, unknown> {\n  if (Object.prototype.toString.call(value) !== '[object Object]') {\n    return false\n  }\n\n  let prototype = Object.getPrototypeOf(value)\n  return prototype === null || Object.getPrototypeOf(prototype) === null\n}\n\nexport function batch<F extends (...args: any[]) => void, P extends any[] = Parameters<F>>(\n  setup: () => [callback: F, handle: () => void]\n) {\n  let [callback, handle] = setup()\n  let d = disposables()\n  return (...args: P) => {\n    callback(...args)\n    d.dispose()\n    d.microTask(handle)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/machines/stack-machine.ts",
    "content": "import { Machine } from '../machine'\nimport { DefaultMap } from '../utils/default-map'\nimport { match } from '../utils/match'\n\ntype Scope = string | null\ntype Id = string\n\ninterface State {\n  stack: Id[]\n}\n\nexport enum ActionTypes {\n  Push,\n  Pop,\n}\n\nexport type Actions = { type: ActionTypes.Push; id: Id } | { type: ActionTypes.Pop; id: Id }\n\nlet reducers: {\n  [P in ActionTypes]: (state: State, action: Extract<Actions, { type: P }>) => State\n} = {\n  [ActionTypes.Push](state, action) {\n    let id = action.id\n    let stack = state.stack\n    let idx = state.stack.indexOf(id)\n\n    // Already in the stack, move it to the top\n    if (idx !== -1) {\n      let copy = state.stack.slice()\n      copy.splice(idx, 1)\n      copy.push(id)\n\n      stack = copy\n      return { ...state, stack }\n    }\n\n    // Not in the stack, add it to the top\n    return { ...state, stack: [...state.stack, id] }\n  },\n  [ActionTypes.Pop](state, action) {\n    let id = action.id\n    let idx = state.stack.indexOf(id)\n    if (idx === -1) return state // Not in the stack\n\n    let copy = state.stack.slice()\n    copy.splice(idx, 1)\n\n    return { ...state, stack: copy }\n  },\n}\n\nclass StackMachine extends Machine<State, Actions> {\n  static new() {\n    return new StackMachine({ stack: [] })\n  }\n\n  reduce(state: Readonly<State>, action: Actions): State {\n    return match(action.type, reducers, state, action)\n  }\n\n  actions = {\n    push: (id: Id) => this.send({ type: ActionTypes.Push, id }),\n    pop: (id: Id) => this.send({ type: ActionTypes.Pop, id }),\n  }\n\n  selectors = {\n    isTop: (state: State, id: Id) => state.stack[state.stack.length - 1] === id,\n    inStack: (state: State, id: Id) => state.stack.includes(id),\n  }\n}\n\nexport const stackMachines = new DefaultMap<Scope, StackMachine>(() => StackMachine.new())\n"
  },
  {
    "path": "packages/@headlessui-react/src/react-glue.tsx",
    "content": "import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'\n\nimport { useEvent } from './hooks/use-event'\nimport { shallowEqual, type Machine } from './machine'\n\nexport function useSlice<M extends Machine<any, any>, Slice>(\n  machine: M,\n  selector: (state: Readonly<M extends Machine<infer State, any> ? State : never>) => Slice,\n  compare = shallowEqual\n) {\n  return useSyncExternalStoreWithSelector(\n    useEvent((onStoreChange) => machine.subscribe(identity, onStoreChange)),\n    useEvent(() => machine.state),\n    useEvent(() => machine.state),\n    useEvent(selector),\n    compare\n  )\n}\n\nfunction identity<T>(value: T) {\n  return value\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/accessibility-assertions.ts",
    "content": "import { FocusableMode, isFocusableElement } from '../utils/focus-management'\nimport { getActiveElement } from '../utils/owner'\n\nfunction assertNever(x: never): never {\n  throw new Error('Unexpected object: ' + x)\n}\n\n// ---\n\nexport function getLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-label-\"]')\n}\n\nexport function getLabels(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('label,[id^=\"headlessui-label-\"]'))\n}\n\nexport function getDescription(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-description-\"]')\n}\n\nexport function getDescriptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-description-\"]'))\n}\n\nexport function getControl(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-control-\"]')\n}\n\nexport function getInput(): HTMLElement | null {\n  return document.querySelector('input')\n}\n\nexport function getSelect(): HTMLElement | null {\n  return document.querySelector('select')\n}\n\nexport function getTextarea(): HTMLElement | null {\n  return document.querySelector('textarea')\n}\n\nexport function getCheckbox(): HTMLElement | null {\n  return document.querySelector('input[type=\"checkbox\"],[role=\"checkbox\"]')\n}\n\nexport enum CheckboxState {\n  /** The checkbox is checked. */\n  Checked,\n\n  /** The checkbox is unchecked. */\n  Unchecked,\n\n  /** The checkbox is indeterminate. */\n  Indeterminate,\n}\n\nexport function assertCheckbox(\n  options: {\n    attributes?: Record<string, string | null>\n    state: CheckboxState\n  },\n  checkbox = getCheckbox()\n) {\n  try {\n    switch (options.state) {\n      case CheckboxState.Checked:\n        if (checkbox === null) return expect(checkbox).not.toBe(null)\n\n        expect(checkbox).toHaveAttribute('aria-checked', 'true')\n        expect(checkbox).toHaveAttribute('role', 'checkbox')\n        expect(checkbox).toHaveAttribute('tabindex', '0')\n        expect(checkbox).toHaveAttribute('data-checked')\n\n        for (let attributeName in options.attributes) {\n          expect(checkbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case CheckboxState.Unchecked:\n        if (checkbox === null) return expect(checkbox).not.toBe(null)\n\n        expect(checkbox).toHaveAttribute('aria-checked', 'false')\n        expect(checkbox).toHaveAttribute('role', 'checkbox')\n        expect(checkbox).toHaveAttribute('tabindex', '0')\n\n        for (let attributeName in options.attributes) {\n          expect(checkbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case CheckboxState.Indeterminate:\n        if (checkbox === null) return expect(checkbox).not.toBe(null)\n\n        expect(checkbox).toHaveAttribute('aria-checked', 'mixed')\n        expect(checkbox).toHaveAttribute('indeterminate', 'true')\n        expect(checkbox).toHaveAttribute('role', 'checkbox')\n        expect(checkbox).toHaveAttribute('tabindex', '0')\n        expect(checkbox).toHaveAttribute('data-indeterminate')\n\n        for (let attributeName in options.attributes) {\n          expect(checkbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertCheckbox)\n    throw err\n  }\n}\n\nexport function assertLinkedWithLabel(\n  element: HTMLElement | null,\n  label: HTMLElement | HTMLElement[]\n) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    let labels = Array.isArray(label) ? label : [label]\n\n    expect(element).toHaveAttribute('aria-labelledby')\n\n    let labelledBy = new Set(element.getAttribute('aria-labelledby')?.split(' ') ?? [])\n    for (let label of labels) {\n      expect(labelledBy).toContain(label.id)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertLinkedWithLabel)\n    throw err\n  }\n}\n\nexport function assertNotLinkedWithLabel(\n  element: HTMLElement | null,\n  label: HTMLElement | HTMLElement[]\n) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    let labels = Array.isArray(label) ? label : [label]\n\n    let labelledBy = new Set(element.getAttribute('aria-labelledby')?.split(' ') ?? [])\n    for (let label of labels) {\n      expect(labelledBy).not.toContain(label.id)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotLinkedWithLabel)\n    throw err\n  }\n}\n\nexport function assertLinkedWithDescription(\n  element: HTMLElement | null,\n  description: HTMLElement | HTMLElement[]\n) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    let descriptions = Array.isArray(description) ? description : [description]\n\n    expect(element).toHaveAttribute('aria-describedby')\n\n    let describedby = new Set(element.getAttribute('aria-describedby')?.split(' ') ?? [])\n    for (let description of descriptions) {\n      expect(describedby).toContain(description.id)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertLinkedWithDescription)\n    throw err\n  }\n}\n\n// ---\n\nexport function getMenuButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-menu-button-\"]')\n}\n\nexport function getMenuButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getMenu(): HTMLElement | null {\n  return document.querySelector('[role=\"menu\"]')\n}\n\nexport function getMenus(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"menu\"]'))\n}\n\nexport function getMenuItems(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"menuitem\"]'))\n}\n\n// ---\n\nexport enum MenuState {\n  /** The menu is visible to the user. */\n  Visible,\n\n  /** The menu is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The menu is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport function assertMenuButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: MenuState\n  },\n  button = getMenuButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case MenuState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case MenuState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case MenuState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuButton)\n    throw err\n  }\n}\n\nexport function assertMenuButtonLinkedWithMenu(button = getMenuButton(), menu = getMenu()) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (menu === null) return expect(menu).not.toBe(null)\n\n    // Ensure link between button & menu is correct\n    expect(button).toHaveAttribute('aria-controls', menu.getAttribute('id'))\n    expect(menu).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuButtonLinkedWithMenu)\n    throw err\n  }\n}\n\nexport function assertMenuLinkedWithMenuItem(item: HTMLElement | null, menu = getMenu()) {\n  try {\n    if (menu === null) return expect(menu).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between menu & menu item is correct\n    expect(menu).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuLinkedWithMenuItem)\n    throw err\n  }\n}\n\nexport function assertNoActiveMenuItem(menu = getMenu()) {\n  try {\n    if (menu === null) return expect(menu).not.toBe(null)\n\n    // Ensure we don't have an active menu\n    expect(menu).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveMenuItem)\n    throw err\n  }\n}\n\nexport function assertMenu(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: MenuState\n  },\n  menu = getMenu()\n) {\n  try {\n    switch (options.state) {\n      case MenuState.InvisibleHidden:\n        if (menu === null) return expect(menu).not.toBe(null)\n\n        assertHidden(menu)\n\n        expect(menu).toHaveAttribute('aria-labelledby')\n        expect(menu).toHaveAttribute('role', 'menu')\n\n        if (options.textContent) expect(menu).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(menu).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case MenuState.Visible:\n        if (menu === null) return expect(menu).not.toBe(null)\n\n        assertVisible(menu)\n\n        expect(menu).toHaveAttribute('aria-labelledby')\n        expect(menu).toHaveAttribute('role', 'menu')\n\n        if (options.textContent) expect(menu).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(menu).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case MenuState.InvisibleUnmounted:\n        expect(menu).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenu)\n    throw err\n  }\n}\n\nexport function assertMenuItem(\n  item: HTMLElement | null,\n  options?: { tag?: string; attributes?: Record<string, string | null> }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'menuitem')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure menu button has the following attributes\n    if (options) {\n      for (let attributeName in options.attributes) {\n        expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n      }\n\n      if (options.tag) {\n        expect(item.tagName.toLowerCase()).toBe(options.tag)\n      }\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuItem)\n    throw err\n  }\n}\n\n// ---\n\nexport function getComboboxLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-combobox-label\"],[id^=\"headlessui-label\"]')\n}\n\nexport function getComboboxButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-combobox-button-\"]')\n}\n\nexport function getComboboxButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getComboboxInput(): HTMLInputElement | null {\n  return document.querySelector('[role=\"combobox\"]')\n}\n\nexport function getCombobox(): HTMLElement | null {\n  return document.querySelector('[role=\"listbox\"]')\n}\n\nexport function getComboboxInputs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"combobox\"]'))\n}\n\nexport function getComboboxes(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"listbox\"]'))\n}\n\nexport function getComboboxOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"option\"]'))\n}\n\n// ---\n\nexport enum ComboboxState {\n  /** The combobox is visible to the user. */\n  Visible,\n\n  /** The combobox is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The combobox is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport enum ComboboxMode {\n  /** The combobox is in the `single` mode. */\n  Single,\n\n  /** The combobox is in the `multiple` mode. */\n  Multiple,\n}\n\nexport function assertCombobox(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n    mode?: ComboboxMode\n  },\n  combobox = getComboboxInput(),\n  listbox = getListbox()\n) {\n  try {\n    switch (options.state) {\n      case ComboboxState.InvisibleHidden:\n        if (combobox === null) return expect(combobox).not.toBe(null)\n\n        assertHidden(combobox)\n\n        expect(combobox).toHaveAttribute('role', 'combobox')\n\n        if (options.textContent) expect(combobox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(combobox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.Visible:\n        if (combobox === null) return expect(combobox).not.toBe(null)\n\n        assertVisible(combobox)\n\n        expect(combobox).toHaveAttribute('role', 'combobox')\n\n        if (options.mode && options.mode === ComboboxMode.Multiple) {\n          expect(listbox).toHaveAttribute('aria-multiselectable', 'true')\n        }\n\n        if (options.textContent) expect(combobox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(combobox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(combobox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxInput(\n  options: {\n    attributes?: Record<string, string | null>\n    state: ComboboxState\n  },\n  input = getComboboxInput()\n) {\n  try {\n    if (input === null) return expect(input).not.toBe(null)\n\n    // Ensure combobox input has these properties\n    expect(input).toHaveAttribute('id')\n\n    switch (options.state) {\n      case ComboboxState.Visible:\n        expect(input).toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ComboboxState.InvisibleHidden:\n        expect(input).toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(input).not.toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    // Ensure combobox input has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(input).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxInput)\n    throw err\n  }\n}\n\nexport function assertComboboxList(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n  },\n  listbox = getCombobox()\n) {\n  try {\n    switch (options.state) {\n      case ComboboxState.InvisibleHidden:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertHidden(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.Visible:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertVisible(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(listbox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n  },\n  button = getComboboxButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case ComboboxState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ComboboxState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxButton)\n    throw err\n  }\n}\n\nexport function assertComboboxLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    tag?: string\n    textContent?: string\n  },\n  label = getComboboxLabel()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(label).toHaveAttribute('id')\n\n    if (options.textContent) {\n      expect(label).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(label.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxLabel)\n    throw err\n  }\n}\n\nexport function assertComboboxButtonLinkedWithCombobox(\n  button = getComboboxButton(),\n  combobox = getCombobox()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    // Ensure link between button & combobox is correct\n    expect(button).toHaveAttribute('aria-controls', combobox.getAttribute('id'))\n    expect(combobox).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxButtonLinkedWithCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxLabelLinkedWithCombobox(\n  label = getComboboxLabel(),\n  combobox = getComboboxInput()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    expect(combobox).toHaveAttribute('aria-labelledby', label.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxLabelLinkedWithCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxButtonLinkedWithComboboxLabel(\n  button = getComboboxButton(),\n  label = getComboboxLabel()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure link between button & label is correct\n    expect(button).toHaveAttribute('aria-labelledby', `${label.id} ${button.id}`)\n  } catch (err) {\n    if (err instanceof Error)\n      Error.captureStackTrace(err, assertComboboxButtonLinkedWithComboboxLabel)\n    throw err\n  }\n}\n\nexport function assertActiveComboboxOption(\n  item: HTMLElement | null,\n  combobox = getComboboxInput()\n) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between combobox & combobox item is correct\n    expect(combobox).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNotActiveComboboxOption(\n  item: HTMLElement | null,\n  combobox = getComboboxInput()\n) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between combobox & combobox item does not exist\n    expect(combobox).not.toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNoActiveComboboxOption(combobox = getComboboxInput()) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    // Ensure we don't have an active combobox\n    expect(combobox).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNoSelectedComboboxOption(items = getComboboxOptions()) {\n  try {\n    for (let item of items) expect(item).toHaveAttribute('aria-selected', 'false')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoSelectedComboboxOption)\n    throw err\n  }\n}\n\nexport function assertComboboxOption(\n  item: HTMLElement | null,\n  options?: {\n    tag?: string\n    attributes?: Record<string, string | null>\n    selected?: boolean\n  }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'option')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure combobox button has the following attributes\n    if (!options) return\n\n    for (let attributeName in options.attributes) {\n      expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n\n    if (options.tag) {\n      expect(item.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.selected != null) {\n      return expect(item).toHaveAttribute('aria-selected', options.selected ? 'true' : 'false')\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxOption)\n    throw err\n  }\n}\n\n// ---\n\nexport function getListboxLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-listbox-label\"],[id^=\"headlessui-label\"]')\n}\n\nexport function getListboxButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-listbox-button-\"]')\n}\n\nexport function getListboxButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getListbox(): HTMLElement | null {\n  return document.querySelector('[role=\"listbox\"]')\n}\n\nexport function getListboxes(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"listbox\"]'))\n}\n\nexport function getListboxOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"option\"]'))\n}\n\n// ---\n\nexport enum ListboxState {\n  /** The listbox is visible to the user. */\n  Visible,\n\n  /** The listbox is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The listbox is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport enum ListboxMode {\n  /** The listbox is in the `single` mode. */\n  Single,\n\n  /** The listbox is in the `multiple` mode. */\n  Multiple,\n}\n\nexport function assertListbox(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ListboxState\n    mode?: ListboxMode\n    orientation?: 'horizontal' | 'vertical'\n  },\n  listbox = getListbox()\n) {\n  let { orientation = 'vertical' } = options\n\n  try {\n    switch (options.state) {\n      case ListboxState.InvisibleHidden:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertHidden(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('aria-orientation', orientation)\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ListboxState.Visible:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertVisible(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('aria-orientation', orientation)\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.mode && options.mode === ListboxMode.Multiple) {\n          expect(listbox).toHaveAttribute('aria-multiselectable', 'true')\n        }\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ListboxState.InvisibleUnmounted:\n        expect(listbox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListbox)\n    throw err\n  }\n}\n\nexport function assertListboxButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ListboxState\n  },\n  button = getListboxButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case ListboxState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ListboxState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ListboxState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxButton)\n    throw err\n  }\n}\n\nexport function assertListboxLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    tag?: string\n    textContent?: string\n  },\n  label = getListboxLabel()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(label).toHaveAttribute('id')\n\n    if (options.textContent) {\n      expect(label).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(label.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxLabel)\n    throw err\n  }\n}\n\nexport function assertListboxButtonLinkedWithListbox(\n  button = getListboxButton(),\n  listbox = getListbox()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    // Ensure link between button & listbox is correct\n    expect(button).toHaveAttribute('aria-controls', listbox.getAttribute('id'))\n    expect(listbox).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxButtonLinkedWithListbox)\n    throw err\n  }\n}\n\nexport function assertListboxLabelLinkedWithListbox(\n  label = getListboxLabel(),\n  listbox = getListbox()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    expect(listbox).toHaveAttribute('aria-labelledby', label.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxLabelLinkedWithListbox)\n    throw err\n  }\n}\n\nexport function assertListboxButtonLinkedWithListboxLabel(\n  button = getListboxButton(),\n  label = getListboxLabel()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure link between button & label is correct\n    expect(button).toHaveAttribute('aria-labelledby', `${label.id} ${button.id}`)\n  } catch (err) {\n    if (err instanceof Error)\n      Error.captureStackTrace(err, assertListboxButtonLinkedWithListboxLabel)\n    throw err\n  }\n}\n\nexport function assertActiveListboxOption(item: HTMLElement | null, listbox = getListbox()) {\n  try {\n    if (listbox === null) return expect(listbox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between listbox & listbox item is correct\n    expect(listbox).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveListboxOption)\n    throw err\n  }\n}\n\nexport function assertNoActiveListboxOption(listbox = getListbox()) {\n  try {\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    // Ensure we don't have an active listbox\n    expect(listbox).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveListboxOption)\n    throw err\n  }\n}\n\nexport function assertNoSelectedListboxOption(items = getListboxOptions()) {\n  try {\n    for (let item of items) expect(item).toHaveAttribute('aria-selected', 'false')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoSelectedListboxOption)\n    throw err\n  }\n}\n\nexport function assertListboxOption(\n  item: HTMLElement | null,\n  options?: {\n    tag?: string\n    attributes?: Record<string, string | null>\n    selected?: boolean\n  }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'option')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure listbox button has the following attributes\n    if (!options) return\n\n    for (let attributeName in options.attributes) {\n      expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n\n    if (options.tag) {\n      expect(item.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.selected != null) {\n      return expect(item).toHaveAttribute('aria-selected', options.selected ? 'true' : 'false')\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxOption)\n    throw err\n  }\n}\n\n// ---\n\nexport function getSwitch(): HTMLElement | null {\n  return document.querySelector('[role=\"switch\"]')\n}\n\nexport function getSwitchLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-switch-label\"],[id^=\"headlessui-label\"]')\n}\n\n// ---\n\nexport enum SwitchState {\n  On,\n  Off,\n}\n\nexport function assertSwitch(\n  options: {\n    state: SwitchState\n    tag?: string\n    textContent?: string\n    label?: string\n    description?: string\n    attributes?: Record<string, string | null>\n  },\n  switchElement = getSwitch()\n) {\n  try {\n    if (switchElement === null) return expect(switchElement).not.toBe(null)\n\n    expect(switchElement).toHaveAttribute('role', 'switch')\n    let tabIndex = Number(switchElement.getAttribute('tabindex') ?? '0')\n    expect(tabIndex).toBeGreaterThanOrEqual(0)\n\n    if (options.textContent) {\n      expect(switchElement).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(switchElement.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.label) {\n      assertLabelValue(switchElement, options.label)\n    }\n\n    if (options.description) {\n      assertDescriptionValue(switchElement, options.description)\n    }\n\n    switch (options.state) {\n      case SwitchState.On:\n        expect(switchElement).toHaveAttribute('aria-checked', 'true')\n        break\n\n      case SwitchState.Off:\n        expect(switchElement).toHaveAttribute('aria-checked', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    // Ensure disclosure button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(switchElement).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertSwitch)\n    throw err\n  }\n}\n\n// ---\n\nexport function getDisclosureButton(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-disclosure-button-\"]')\n}\n\nexport function getDisclosurePanel(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-disclosure-panel-\"]')\n}\n\n// ---\n\nexport enum DisclosureState {\n  /** The disclosure is visible to the user. */\n  Visible,\n\n  /** The disclosure is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The disclosure is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertDisclosureButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DisclosureState\n  },\n  button = getDisclosureButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure disclosure button have these properties\n    expect(button).toHaveAttribute('id')\n\n    switch (options.state) {\n      case DisclosureState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case DisclosureState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case DisclosureState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure disclosure button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDisclosureButton)\n    throw err\n  }\n}\n\nexport function assertDisclosurePanel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DisclosureState\n  },\n  panel = getDisclosurePanel()\n) {\n  try {\n    switch (options.state) {\n      case DisclosureState.InvisibleHidden:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertHidden(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DisclosureState.Visible:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertVisible(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DisclosureState.InvisibleUnmounted:\n        expect(panel).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDisclosurePanel)\n    throw err\n  }\n}\n\n// ---\n\nexport function getPopoverButton(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-button-\"]')\n}\n\nexport function getPopoverPanel(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-panel-\"]')\n}\n\nexport function getPopoverOverlay(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-backdrop-\"]')\n}\n\n// ---\n\nexport enum PopoverState {\n  /** The popover is visible to the user. */\n  Visible,\n\n  /** The popover is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The popover is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertPopoverButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: PopoverState\n  },\n  button = getPopoverButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure popover button have these properties\n    expect(button).toHaveAttribute('id')\n\n    switch (options.state) {\n      case PopoverState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case PopoverState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case PopoverState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure popover button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertPopoverButton)\n    throw err\n  }\n}\n\nexport function assertPopoverPanel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: PopoverState\n  },\n  panel = getPopoverPanel()\n) {\n  try {\n    switch (options.state) {\n      case PopoverState.InvisibleHidden:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertHidden(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case PopoverState.Visible:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertVisible(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case PopoverState.InvisibleUnmounted:\n        expect(panel).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertPopoverPanel)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertLabelValue(element: HTMLElement | null, value: string) {\n  if (element === null) return expect(element).not.toBe(null)\n\n  if (element.hasAttribute('aria-labelledby')) {\n    let ids = element.getAttribute('aria-labelledby')!.split(' ')\n    expect(ids.map((id) => document.getElementById(id)?.textContent).join(' ')).toEqual(value)\n    return\n  }\n\n  if (element.hasAttribute('aria-label')) {\n    expect(element).toHaveAttribute('aria-label', value)\n    return\n  }\n\n  if (element.hasAttribute('id') && document.querySelectorAll(`[for=\"${element.id}\"]`).length > 0) {\n    expect(document.querySelector(`[for=\"${element.id}\"]`)).toHaveTextContent(value)\n    return\n  }\n\n  expect(element).toHaveTextContent(value)\n}\n\n// ---\n\nexport function assertDescriptionValue(element: HTMLElement | null, value: string) {\n  if (element === null) return expect(element).not.toBe(null)\n\n  let id = element.getAttribute('aria-describedby')!\n  expect(document.getElementById(id)?.textContent).toEqual(value)\n}\n\n// ---\n\nexport function getDialog(): HTMLElement | null {\n  return document.querySelector('[role=\"dialog\"],[role=\"alertdialog\"]')\n}\n\nexport function getDialogs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"dialog\"],[role=\"alertdialog\"]'))\n}\n\nexport function getDialogTitle(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-dialog-title-\"]')\n}\n\nexport function getDialogDescription(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-description-\"]')\n}\n\n// ---\n\nexport enum DialogState {\n  /** The dialog is visible to the user. */\n  Visible,\n\n  /** The dialog is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The dialog is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertDialog(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(dialog)\n\n        expect(dialog).toHaveAttribute('role', options.attributes?.['role'] ?? 'dialog')\n        expect(dialog).not.toHaveAttribute('aria-modal', 'true')\n\n        if (options.textContent) expect(dialog).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(dialog).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(dialog)\n\n        expect(dialog).toHaveAttribute('role', options.attributes?.['role'] ?? 'dialog')\n        expect(dialog).toHaveAttribute('aria-modal', 'true')\n\n        if (options.textContent) expect(dialog).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(dialog).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(dialog).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialog)\n    throw err\n  }\n}\n\nexport function assertDialogTitle(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  title = getDialogTitle(),\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (title === null) return expect(title).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(title)\n\n        expect(title).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-labelledby', title.id)\n\n        if (options.textContent) expect(title).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(title).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (title === null) return expect(title).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(title)\n\n        expect(title).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-labelledby', title.id)\n\n        if (options.textContent) expect(title).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(title).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(title).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialogTitle)\n    throw err\n  }\n}\n\nexport function assertDialogDescription(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  description = getDialogDescription(),\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (description === null) return expect(description).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(description)\n\n        expect(description).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-describedby', description.id)\n\n        if (options.textContent) expect(description).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(description).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (description === null) return expect(description).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(description)\n\n        expect(description).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-describedby', description.id)\n\n        if (options.textContent) expect(description).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(description).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(description).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialogDescription)\n    throw err\n  }\n}\n\n// ---\n\nexport function getRadioGroup(): HTMLElement | null {\n  return document.querySelector('[role=\"radiogroup\"]')\n}\n\nexport function getRadioGroupLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-label-\"]')\n}\n\nexport function getRadioGroupOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-radiogroup-option-\"]'))\n}\n\n// ---\n\nexport function assertRadioGroupLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n  },\n  label = getRadioGroupLabel(),\n  radioGroup = getRadioGroup()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (radioGroup === null) return expect(radioGroup).not.toBe(null)\n\n    expect(label).toHaveAttribute('id')\n    expect(radioGroup).toHaveAttribute('aria-labelledby', label.id)\n\n    if (options.textContent) expect(label).toHaveTextContent(options.textContent)\n\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertRadioGroupLabel)\n    throw err\n  }\n}\n\n// ---\n\nexport function getTabList(): HTMLElement | null {\n  return document.querySelector('[role=\"tablist\"]')\n}\n\nexport function getTabs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-tabs-tab-\"]'))\n}\n\nexport function getPanels(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-tabs-panel-\"]'))\n}\n\n// ---\n\nexport function assertTabs(\n  {\n    active,\n    orientation = 'horizontal',\n    tabContents = null,\n    panelContents = null,\n  }: {\n    active: number\n    orientation?: 'vertical' | 'horizontal'\n    tabContents?: string | null\n    panelContents?: string | null\n  },\n  list = getTabList(),\n  tabs = getTabs(),\n  panels = getPanels()\n) {\n  try {\n    if (list === null) return expect(list).not.toBe(null)\n\n    expect(list).toHaveAttribute('role', 'tablist')\n    expect(list).toHaveAttribute('aria-orientation', orientation)\n\n    let activeTab = Array.from(list.querySelectorAll('[id^=\"headlessui-tabs-tab-\"]'))[active]\n    let activePanel = panels.find((panel) => panel.id === activeTab.getAttribute('aria-controls'))\n\n    for (let tab of tabs) {\n      expect(tab).toHaveAttribute('id')\n      expect(tab).toHaveAttribute('role', 'tab')\n      expect(tab).toHaveAttribute('type', 'button')\n\n      if (tab === activeTab) {\n        expect(tab).toHaveAttribute('aria-selected', 'true')\n        expect(tab).toHaveAttribute('tabindex', '0')\n        if (tabContents !== null) {\n          expect(tab.textContent).toBe(tabContents)\n        }\n      } else {\n        expect(tab).toHaveAttribute('aria-selected', 'false')\n        expect(tab).toHaveAttribute('tabindex', '-1')\n      }\n\n      if (tab.hasAttribute('aria-controls')) {\n        let controlsId = tab.getAttribute('aria-controls')!\n        let panel = document.getElementById(controlsId)\n\n        expect(panel).not.toBe(null)\n        expect(panels).toContain(panel)\n        expect(panel).toHaveAttribute('aria-labelledby', tab.id)\n      }\n    }\n\n    for (let panel of panels) {\n      expect(panel).toHaveAttribute('id')\n      expect(panel).toHaveAttribute('role', 'tabpanel')\n\n      let controlledById = panel.getAttribute('aria-labelledby')!\n      let tab = document.getElementById(controlledById)\n\n      expect(tabs).toContain(tab)\n      expect(tab).toHaveAttribute('aria-controls', panel.id)\n\n      if (panel === activePanel) {\n        expect(panel).toHaveAttribute('tabindex', '0')\n        if (tabContents !== null) {\n          expect(panel.textContent).toBe(panelContents)\n        }\n      } else {\n        expect(panel).toHaveAttribute('tabindex', '-1')\n      }\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertTabs)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertActiveElement(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    try {\n      // Jest has a weird bug:\n      //   \"Cannot assign to read only property 'Symbol(impl)' of object '[object DOMImplementation]'\"\n      // when this assertion fails.\n      // Therefore we will catch it when something goes wrong, and just look at the outerHTML string.\n      expect(getActiveElement(element)).toBe(element)\n    } catch (err) {\n      expect(getActiveElement(element)?.outerHTML).toBe(element.outerHTML)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveElement)\n    throw err\n  }\n}\n\nexport function assertContainsActiveElement(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    expect(element.contains(getActiveElement(element))).toBe(true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertContainsActiveElement)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertHidden(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).toHaveAttribute('hidden')\n    expect(element).toHaveStyle({ display: 'none' })\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertHidden)\n    throw err\n  }\n}\n\nexport function assertVisible(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).not.toHaveAttribute('hidden')\n    expect(element).not.toHaveStyle({ display: 'none' })\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertVisible)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertFocusable(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(isFocusableElement(element, FocusableMode.Strict)).toBe(true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertFocusable)\n    throw err\n  }\n}\n\nexport function assertNotFocusable(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(isFocusableElement(element, FocusableMode.Strict)).toBe(false)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotFocusable)\n    throw err\n  }\n}\n\nexport function assertInert(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).toHaveAttribute('aria-hidden', 'true')\n    expect(element).toHaveProperty('inert', true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertInert)\n    throw err\n  }\n}\n\nexport function assertNotInert(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    // NOTE: We can't test that the element doesn't have `aria-hidden`, because this can still be\n    // the case even if they are not inert.\n    expect(element.inert).toBeUndefined()\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotInert)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertDisabledish(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    let actuallyDisabled = element.getAttribute('disabled')\n    if (actuallyDisabled === 'true' || actuallyDisabled === '') {\n      return\n    }\n\n    let ariaDisabled = element.getAttribute('aria-disabled')\n    if (ariaDisabled === 'true' || ariaDisabled === '') {\n      return\n    }\n\n    throw new Error(`Expected element to be disabledish, but it wasn't.`)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDisabledish)\n    throw err\n  }\n}\n\n// ---\n\nexport function getByText(text: string): HTMLElement | null {\n  let walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {\n    acceptNode(node: HTMLElement) {\n      if (node.children.length > 0) return NodeFilter.FILTER_SKIP\n      return NodeFilter.FILTER_ACCEPT\n    },\n  })\n\n  while (walker.nextNode()) {\n    if (walker.currentNode.textContent === text) return walker.currentNode as HTMLElement\n  }\n\n  return null\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/execute-timeline.ts",
    "content": "import { render } from '@testing-library/react'\nimport snapshotDiff from 'snapshot-diff'\nimport { disposables } from '../utils/disposables'\nimport { reportChanges } from './report-dom-node-changes'\n\nfunction redentSnapshot(input: string) {\n  let minSpaces = Infinity\n  let lines = input.split('\\n')\n  for (let line of lines) {\n    if (line.trim() === '---') continue\n    let spacesInLine = (line.match(/^[+-](\\s+)/g) || []).pop()!.length - 1\n    minSpaces = Math.min(minSpaces, spacesInLine)\n  }\n\n  let replacer = new RegExp(`^([+-])\\\\s{${minSpaces}}(.*)`, 'g')\n\n  return input\n    .split('\\n')\n    .map((line) =>\n      line.trim() === '---' ? line : line.replace(replacer, (_, sign, rest) => `${sign}  ${rest}`)\n    )\n    .join('\\n')\n}\n\nexport async function executeTimeline(\n  element: React.JSX.Element,\n  steps: ((tools: ReturnType<typeof render>) => (null | number)[])[]\n) {\n  let d = disposables()\n  let snapshots: { content: DocumentFragment; recordedAt: bigint }[] = []\n\n  //\n  let tools = render(element)\n\n  // Start listening for changes\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      () => {\n        // This will ensure that any DOM change to the body has been recorded.\n        snapshots.push({\n          content: tools.asFragment(),\n          recordedAt: process.hrtime.bigint(),\n        })\n      }\n    )\n  )\n\n  // We start with a `null` value because we will start with a snapshot even _before_ things start\n  // happening.\n  let timestamps: (null | number)[] = [null]\n\n  //\n  await steps.reduce(async (chain, step) => {\n    await chain\n\n    let durations = step(tools)\n\n    // Note: The following calls are just in place to ensure that **we** waited long enough for the\n    // transitions to take place. This has no impact on the actual transitions. Above where the\n    // `reportDOMNodeChanges` is used we will actually record all the changes, no matter what\n    // happens here.\n\n    timestamps.push(...durations)\n\n    let totalDuration = durations\n      .filter((duration): duration is number => duration !== null)\n      .reduce((total, current) => total + current, 0)\n\n    // Changes happen in the next frame\n    await new Promise((resolve) => d.nextFrame(resolve))\n\n    // We wait for the amount of the duration\n    await new Promise((resolve) => d.setTimeout(resolve, totalDuration))\n\n    // We wait an additional next frame so that we know that we are done\n    await new Promise((resolve) => d.nextFrame(resolve))\n  }, Promise.resolve())\n\n  if (snapshots.length <= 0) {\n    throw new Error('We could not record any changes')\n  }\n\n  let uniqueSnapshots = snapshots\n    // Only keep the snapshots that are unique. Multiple snapshots of the same\n    // content are a bit useless for us.\n    .filter((snapshot, i) => {\n      if (i === 0) return true\n      return snapshot.content !== snapshots[i - 1].content\n    })\n\n    // Add a relative time compared to the previous snapshot. We recorded everything in\n    // process.hrtime.bigint() which is in nanoseconds, we want it in milliseconds.\n    .map((snapshot, i, all) => ({\n      ...snapshot,\n      relativeToPreviousSnapshot:\n        i === 0 ? 0 : Number((snapshot.recordedAt - all[i - 1].recordedAt) / BigInt(1e6)),\n    }))\n\n    // Only keep the snapshots that are not outliers.\n    .filter((snapshot, i, all) => {\n      if (i === 0) return true\n      return snapshot.relativeToPreviousSnapshot >= all[i - 1].relativeToPreviousSnapshot\n    })\n\n  let diffed = uniqueSnapshots\n    .map((call, i) => {\n      // Skip initial render, because there is nothing to compare with\n      if (i === 0) return false\n\n      // The next bit of code is a bit ugly, but mos of the code is just cleaning up some \"noise\"\n      // that we don't need in our test output.\n      return `Render ${i}:${\n        // `This took: ${call.relativeTime}ms`\n        timestamps[i] === null\n          ? ''\n          : ` Transition took at least ${timestamps[i]}ms (${\n              isWithinFrame(call.relativeToPreviousSnapshot, timestamps[i]!)\n                ? 'yes'\n                : `no, it took ${call.relativeToPreviousSnapshot}ms`\n            })`\n      }\\n${redentSnapshot(\n        snapshotDiff(uniqueSnapshots[i - 1].content, call.content, {\n          aAnnotation: '__REMOVE_ME__',\n          bAnnotation: '__REMOVE_ME__',\n          contextLines: 0,\n        })\n          // Just to do some cleanup\n          .replace(/\\n\\n@@([^@@]*)@@/g, '') // Top level @@ signs\n          .replace(/@@([^@@]*)@@/g, '---') // In between @@ signs\n          .replace(/[-+] __REMOVE_ME__\\n/g, '')\n          .replace(/Snapshot Diff:\\n/g, '')\n      )\n        .split('\\n')\n        .map((line) => `    ${line}`)\n        .join('\\n')}`\n    })\n    .filter(Boolean)\n    .join('\\n\\n')\n\n  d.dispose()\n\n  return diffed\n}\n\nexecuteTimeline.fullTransition = (duration: number) => {\n  return [\n    /** Stage 1: Immediately add `base` and `from` classes */\n    null,\n\n    /** Stage 2: Immediately remove `from` classes and add `to` classes */\n    null,\n\n    /** Stage 3: After duration remove `to` and `base` classes */\n    duration,\n  ]\n}\n\nlet state: {\n  before: number\n  fps: number\n  handle: ReturnType<typeof requestAnimationFrame> | null\n} = {\n  before: Date.now(),\n  fps: 0,\n  handle: null,\n}\n\nstate.handle = requestAnimationFrame(function loop() {\n  let now = Date.now()\n  state.fps = Math.round(1000 / (now - state.before))\n  state.before = now\n  state.handle = requestAnimationFrame(loop)\n})\n\nafterAll(() => {\n  if (state.handle) cancelAnimationFrame(state.handle)\n})\n\nfunction isWithinFrame(actual: number, expected: number) {\n  let buffer = state.fps\n\n  let min = expected - buffer\n  let max = expected + buffer\n\n  return actual >= min && actual <= max\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/fake-pointer.ts",
    "content": "export class FakePointer {\n  private x: number = 0\n  private y: number = 0\n\n  constructor(\n    private width: number,\n    private height: number\n  ) {\n    this.width = width\n    this.height = height\n  }\n\n  get options() {\n    return {\n      screenX: this.x,\n      screenY: this.y,\n    }\n  }\n\n  randomize() {\n    this.x = Math.floor(Math.random() * this.width)\n    this.y = Math.floor(Math.random() * this.height)\n  }\n\n  advance(amount: number = 1) {\n    this.x += amount\n\n    if (this.x >= this.width) {\n      this.x %= this.width\n      this.y++\n    }\n\n    if (this.y >= this.height) {\n      this.y %= this.height\n    }\n  }\n\n  /**\n   * JSDOM does not support pointer events.\n   * Because of this when we try to set the pointer position it returns undefined so our checks fail.\n   *\n   * This runs the callback with the TEST_IGNORE_TRACKED_POINTER environment variable set to 1 so we bypass the checks.\n   */\n  bypassingTrackingChecks(callback: () => void) {\n    let original = process.env.TEST_BYPASS_TRACKED_POINTER\n    process.env.TEST_BYPASS_TRACKED_POINTER = '1'\n    callback()\n    process.env.TEST_BYPASS_TRACKED_POINTER = original\n  }\n}\n\n/**\n * A global pointer for use in pointer and mouse event checks\n */\nexport let pointer = new FakePointer(1920, 1080)\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/interactions.test.tsx",
    "content": "import { render } from '@testing-library/react'\nimport React from 'react'\nimport { Keys, shift, type } from './interactions'\n\ntype Events = 'onKeyDown' | 'onKeyUp' | 'onKeyPress' | 'onClick' | 'onBlur' | 'onFocus'\nlet events: Events[] = ['onKeyDown', 'onKeyUp', 'onKeyPress', 'onClick', 'onBlur', 'onFocus']\n\ntype Args = [\n  string | Partial<KeyboardEvent>,\n  (string | Partial<KeyboardEvent | MouseEvent>)[],\n  Set<Events>,\n]\n\nfunction key(input: string | Partial<KeyboardEvent>): Partial<KeyboardEvent> {\n  if (typeof input === 'string') return { key: input }\n  return input\n}\n\nfunction event(\n  input: string | Partial<KeyboardEvent | MouseEvent>,\n  target?: string\n): Partial<KeyboardEvent | MouseEvent> {\n  let e = typeof input === 'string' ? { type: input } : input\n\n  if (target) {\n    Object.defineProperty(e, 'target', {\n      configurable: false,\n      enumerable: true,\n      get() {\n        return document.getElementById(target!)\n      },\n    })\n  }\n\n  return e\n}\n\ndescribe('Keyboard', () => {\n  describe('type', () => {\n    it.each<Args>([\n      // Default - no cancellation\n      ['a', ['keydown', 'keypress', 'keyup'], new Set()],\n      [Keys.Space, ['keydown', 'keypress', 'keyup', 'click'], new Set()],\n      [Keys.Enter, ['keydown', 'keypress', 'click', 'keyup'], new Set()],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set(),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set(),\n      ],\n\n      // Canceling keydown\n      ['a', ['keydown', 'keyup'], new Set<Events>(['onKeyDown'])],\n      [Keys.Space, ['keydown', 'keyup'], new Set<Events>(['onKeyDown'])],\n      [Keys.Enter, ['keydown', 'keyup'], new Set<Events>(['onKeyDown'])],\n      [Keys.Tab, ['keydown', 'keyup'], new Set<Events>(['onKeyDown'])],\n      [shift(Keys.Tab), ['keydown', 'keyup'], new Set<Events>(['onKeyDown'])],\n\n      // Canceling keypress\n      ['a', ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyPress'])],\n      [Keys.Space, ['keydown', 'keypress', 'keyup', 'click'], new Set<Events>(['onKeyPress'])],\n      [Keys.Enter, ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyPress'])],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onKeyPress']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onKeyPress']),\n      ],\n\n      // Canceling keyup\n      ['a', ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyUp'])],\n      [Keys.Space, ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyUp'])],\n      [Keys.Enter, ['keydown', 'keypress', 'click', 'keyup'], new Set<Events>(['onKeyUp'])],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onKeyUp']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onKeyUp']),\n      ],\n\n      // Cancelling blur\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onBlur']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('focusout', 'trigger'),\n          event('focusin', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onBlur']),\n      ],\n    ])('should fire the correct events %#', async (input, result, prevents) => {\n      let fired: (KeyboardEvent | MouseEvent)[] = []\n\n      let state = { readyToCapture: false }\n\n      function createProps(id: string) {\n        return events.reduce(\n          (props: React.ComponentProps<'button'>, name) => {\n            props[name] = (event: any) => {\n              if (!state.readyToCapture) return\n              if (prevents.has(name)) event.preventDefault()\n              fired.push(event.nativeEvent)\n            }\n            return props\n          },\n          { id }\n        )\n      }\n\n      render(\n        <>\n          <button {...createProps('before')}>Before</button>\n          <button {...createProps('trigger')}>Trigger</button>\n          <button {...createProps('after')}>After</button>\n        </>\n      )\n\n      let trigger = document.getElementById('trigger')\n      trigger?.focus()\n      state.readyToCapture = true\n\n      await type([key(input)])\n\n      let expected = result.map((e) => event(e))\n\n      expect(fired.length).toEqual(result.length)\n\n      for (let [idx, event] of fired.entries()) {\n        for (let key in expected[idx]) {\n          let _key = key as keyof (KeyboardEvent | MouseEvent)\n          expect(event[_key]).toBe(expected[idx][_key])\n        }\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/interactions.ts",
    "content": "import { fireEvent } from '@testing-library/react'\nimport { act } from 'react'\nimport * as DOM from '../utils/dom'\nimport { pointer } from './fake-pointer'\n\nfunction nextFrame(cb: Function): void {\n  setImmediate(() => {\n    setImmediate(() => {\n      setImmediate(() => {\n        cb()\n      })\n    })\n  })\n}\n\nexport let Keys: Record<string, Partial<KeyboardEvent>> = {\n  Space: { key: ' ', keyCode: 32, charCode: 32 },\n  Enter: { key: 'Enter', keyCode: 13, charCode: 13 },\n  Escape: { key: 'Escape', keyCode: 27, charCode: 27 },\n  Backspace: { key: 'Backspace', keyCode: 8 },\n\n  ArrowLeft: { key: 'ArrowLeft', keyCode: 37 },\n  ArrowUp: { key: 'ArrowUp', keyCode: 38 },\n  ArrowRight: { key: 'ArrowRight', keyCode: 39 },\n  ArrowDown: { key: 'ArrowDown', keyCode: 40 },\n\n  Home: { key: 'Home', keyCode: 36 },\n  End: { key: 'End', keyCode: 35 },\n\n  PageUp: { key: 'PageUp', keyCode: 33 },\n  PageDown: { key: 'PageDown', keyCode: 34 },\n\n  Tab: { key: 'Tab', keyCode: 9, charCode: 9 },\n}\n\nexport function shift(event: Partial<KeyboardEvent>) {\n  return { ...event, shiftKey: true }\n}\n\nexport function word(input: string): Partial<KeyboardEvent>[] {\n  let result = input.split('').map((key) => ({ key }))\n\n  let element = document.activeElement\n\n  if (DOM.isHTMLInputElement(element) || DOM.isHTMLTextAreaElement(element)) {\n    fireEvent.change(element, {\n      target: Object.assign({}, element, { value: input }),\n    })\n  }\n\n  return result\n}\n\nlet Default = Symbol()\nlet Ignore = Symbol()\n\nlet cancellations: Record<string | typeof Default, Record<string, Set<string>>> = {\n  [Default]: {\n    keydown: new Set(['keypress']),\n    keypress: new Set([]),\n    keyup: new Set([]),\n  },\n  [Keys.Enter.key!]: {\n    keydown: new Set(['keypress', 'click']),\n    keypress: new Set(['click']),\n    keyup: new Set([]),\n  },\n  [Keys.Space.key!]: {\n    keydown: new Set(['keypress', 'click']),\n    keypress: new Set([]),\n    keyup: new Set(['click']),\n  },\n  [Keys.Tab.key!]: {\n    keydown: new Set(['keypress', 'blur', 'focus']),\n    keypress: new Set([]),\n    keyup: new Set([]),\n  },\n}\n\nlet order: Record<\n  string | typeof Default,\n  ((\n    element: Element,\n    event: Partial<KeyboardEvent | MouseEvent>\n  ) => boolean | typeof Ignore | Element)[]\n> = {\n  [Default]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function input(element, event) {\n      // TODO: This should only fire when the element's value changes\n      return fireEvent.input(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Enter.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function click(element, event) {\n      if (element instanceof HTMLButtonElement) return fireEvent.click(element, event)\n      return Ignore\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Space.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n    function click(element, event) {\n      if (element instanceof HTMLButtonElement) return fireEvent.click(element, event)\n      return Ignore\n    },\n  ],\n  [Keys.Tab.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function blurAndfocus(_element, event) {\n      return focusNext(event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Escape.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Backspace.key!]: [\n    function keydown(element, event) {\n      if (element instanceof HTMLInputElement) {\n        let ev = Object.assign({}, event, {\n          target: Object.assign({}, event.target, {\n            value: element.value.slice(0, -1),\n          }),\n        })\n\n        fireEvent.keyDown(element, ev)\n        return fireEvent.input(element, ev)\n      }\n\n      return fireEvent.keyDown(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n}\n\nexport async function type(events: Partial<KeyboardEvent>[], element = document.activeElement) {\n  jest.useFakeTimers()\n\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    for (let event of events) {\n      let skip = new Set()\n      let actions = order[event.key!] ?? order[Default as any]\n      for (let action of actions) {\n        let checks = action.name.split('And')\n        if (checks.some((check) => skip.has(check))) continue\n\n        let result = action(element, {\n          type: action.name,\n          charCode: event.key?.length === 1 ? event.key?.charCodeAt(0) : undefined,\n          ...event,\n        })\n        if (result === Ignore) continue\n        if (result instanceof Element) {\n          element = result\n        }\n\n        let cancelled = !result\n        if (cancelled) {\n          let skippablesForKey = cancellations[event.key!] ?? cancellations[Default as any]\n          let skippables = skippablesForKey?.[action.name] ?? new Set()\n\n          for (let skippable of skippables) skip.add(skippable)\n        }\n      }\n    }\n\n    // We don't want to actually wait in our tests, so let's advance\n    jest.runAllTimers()\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, type)\n    throw err\n  } finally {\n    jest.useRealTimers()\n  }\n}\n\nexport async function press(event: Partial<KeyboardEvent>, element = document.activeElement) {\n  return type([event], element)\n}\n\nexport enum MouseButton {\n  Left = 0,\n  Right = 2,\n}\n\nexport async function click(\n  element: Document | Element | Window | Node | null,\n  button = MouseButton.Left\n) {\n  return act(() => rawClick(element, button))\n}\n\nexport async function rawClick(\n  element: Document | Element | Window | Node | null,\n  button = MouseButton.Left\n) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    if (element instanceof HTMLButtonElement && element.disabled) return\n\n    let options = { button }\n\n    if (button === MouseButton.Left) {\n      // Cancel in pointerDown cancels mouseDown, mouseUp\n      let cancelled = !fireEvent.pointerDown(element, options)\n      if (!cancelled) {\n        cancelled = !fireEvent.mouseDown(element, options)\n      }\n\n      // Ensure to trigger a `focus` event if the element is focusable, or within a focusable element\n      if (!cancelled) {\n        let next: HTMLElement | null = element as HTMLElement | null\n        while (next !== null) {\n          if (next.matches(focusableSelector)) {\n            act(() => {\n              // act scopes are called immediately. `next` should keep its type refinements.\n              next!.focus()\n            })\n            break\n          }\n          next = next.parentElement\n        }\n      }\n\n      fireEvent.pointerUp(element, options)\n      if (!cancelled) {\n        fireEvent.mouseUp(element, options)\n      }\n      fireEvent.click(element, options)\n    } else if (button === MouseButton.Right) {\n      // Cancel in pointerDown cancels mouseDown, mouseUp\n      let cancelled = !fireEvent.pointerDown(element, options)\n      if (!cancelled) {\n        fireEvent.mouseDown(element, options)\n      }\n\n      // Only in Firefox:\n      fireEvent.pointerUp(element, options)\n      if (!cancelled) {\n        fireEvent.mouseUp(element, options)\n      }\n    }\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, click)\n    throw err\n  }\n}\n\nexport async function focus(element: Document | Element | Window | Node | null) {\n  await act(async () => {\n    try {\n      if (element === null) return expect(element).not.toBe(null)\n      if (element instanceof HTMLElement) {\n        element.focus()\n      } else {\n        fireEvent.focus(element)\n      }\n\n      await new Promise(nextFrame)\n    } catch (err) {\n      if (err instanceof Error) Error.captureStackTrace(err, focus)\n      throw err\n    }\n  })\n}\n\nexport async function blur(element: Document | Element | Window | Node | null) {\n  await act(async () => {\n    try {\n      if (element === null) return expect(element).not.toBe(null)\n\n      if (element instanceof HTMLElement) {\n        element.blur()\n      } else {\n        fireEvent.blur(element)\n      }\n\n      await new Promise(nextFrame)\n    } catch (err) {\n      if (err instanceof Error) Error.captureStackTrace(err, blur)\n      throw err\n    }\n  })\n}\n\nexport async function mouseEnter(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    fireEvent.pointerOver(element)\n    fireEvent.pointerEnter(element)\n    fireEvent.mouseOver(element)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseEnter)\n    throw err\n  }\n}\n\nexport async function mouseMove(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    pointer.advance()\n\n    pointer.bypassingTrackingChecks(() => {\n      fireEvent.pointerMove(element)\n    })\n\n    fireEvent.mouseMove(element, pointer.options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseMove)\n    throw err\n  }\n}\n\nexport async function mouseLeave(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    pointer.advance()\n\n    pointer.bypassingTrackingChecks(() => {\n      fireEvent.pointerOut(element)\n      fireEvent.pointerLeave(element)\n    })\n\n    fireEvent.mouseOut(element, pointer.options)\n    fireEvent.mouseLeave(element, pointer.options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseLeave)\n    throw err\n  }\n}\n\nexport async function mouseDrag(\n  startingElement: Document | Element | Window | Node | null,\n  endingElement: Document | Element | Window | Node | null\n) {\n  let button = MouseButton.Left\n\n  try {\n    if (startingElement === null) return expect(startingElement).not.toBe(null)\n    if (endingElement === null) return expect(endingElement).not.toBe(null)\n    if (startingElement instanceof HTMLButtonElement && startingElement.disabled) return\n\n    let options = { button }\n\n    // Cancel in pointerDown cancels mouseDown, mouseUp\n    let cancelled = !fireEvent.pointerDown(startingElement, options)\n\n    if (!cancelled) {\n      cancelled = !fireEvent.mouseDown(startingElement, options)\n    }\n\n    // Ensure to trigger a `focus` event if the element is focusable, or within a focusable element\n    if (!cancelled) {\n      let next: HTMLElement | null = startingElement as HTMLElement | null\n      while (next !== null) {\n        if (next.matches(focusableSelector)) {\n          act(() => {\n            // act scopes are called immediately. `next` should keep its type refinements.\n            next!.focus()\n          })\n          break\n        }\n        next = next.parentElement\n      }\n    }\n\n    fireEvent.pointerMove(startingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseMove(startingElement, options)\n    }\n\n    fireEvent.pointerOut(startingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseOut(startingElement, options)\n    }\n\n    // crosses over to the ending element\n\n    fireEvent.pointerOver(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseOver(endingElement, options)\n    }\n\n    fireEvent.pointerMove(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseMove(endingElement, options)\n    }\n\n    fireEvent.pointerUp(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseUp(endingElement, options)\n    }\n\n    fireEvent.click(endingElement, options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, click)\n    throw err\n  }\n}\n\n// ---\n\nfunction focusNext(event: Partial<KeyboardEvent>) {\n  let direction = event.shiftKey ? -1 : +1\n  let focusableElements = getFocusableElements()\n  let total = focusableElements.length\n\n  function innerFocusNext(offset = 0): Element {\n    let currentIdx = focusableElements.indexOf(document.activeElement as HTMLElement)\n    let next = focusableElements[(currentIdx + total + direction + offset) % total] as HTMLElement\n\n    if (next) {\n      act(() => {\n        next?.focus({ preventScroll: true })\n      })\n    }\n\n    if (next !== document.activeElement) return innerFocusNext(offset + direction)\n    return next\n  }\n\n  return innerFocusNext()\n}\n\n// Credit:\n//  - https://stackoverflow.com/a/30753870\nlet focusableSelector = [\n  '[contentEditable=true]',\n  '[tabindex]',\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'iframe',\n  'input:not([disabled])',\n  'select:not([disabled])',\n  // TODO: Re-enable once we bump JSDOM\n  // 'details:not(:has(> summary))',\n  'details>summary',\n  'textarea:not([disabled])',\n]\n  .map(\n    process.env.NODE_ENV === 'test'\n      ? // TODO: Remove this once JSDOM fixes the issue where an element that is\n        // \"hidden\" can be the document.activeElement, because this is not possible\n        // in real browsers.\n        (selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`\n      : (selector) => `${selector}:not([tabindex='-1'])`\n  )\n  .join(',')\n\nfunction getFocusableElements(container = document.body) {\n  if (!container) return []\n  return Array.from(container.querySelectorAll(focusableSelector))\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/report-dom-node-changes.ts",
    "content": "import { disposables } from '../utils/disposables'\n\nexport function reportChanges<TType>(key: () => TType, onChange: (value: TType) => void) {\n  let d = disposables()\n\n  let previous: TType\n\n  function track() {\n    let next = key()\n    if (previous !== next) {\n      previous = next\n      onChange(next)\n    }\n    d.requestAnimationFrame(track)\n  }\n\n  track()\n\n  return d.dispose\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/scenarios.tsx",
    "content": "import { render, screen } from '@testing-library/react'\nimport React from 'react'\nimport { Description, Field, Fieldset, Label } from '..'\nimport {\n  assertActiveElement,\n  assertDisabledish,\n  assertLinkedWithDescription,\n  assertLinkedWithLabel,\n  getControl,\n  getDescriptions,\n  getLabel,\n  getLabels,\n} from './accessibility-assertions'\nimport { click } from './interactions'\nimport { suppressConsoleLogs } from './suppress-console-logs'\n\nexport function commonControlScenarios(Control: React.ComponentType<any>) {\n  describe('Rendering composition', () => {\n    describe('Inside `Field`', () => {\n      it('should mark the control as disabled, if the `Field` is disabled', () => {\n        render(\n          <Field disabled>\n            <Control />\n          </Field>\n        )\n\n        assertDisabledish(getControl())\n      })\n\n      it('should link a control and a `Label` when inside a `Field`', () => {\n        render(\n          <Field>\n            <Label>My Label</Label>\n            <Control />\n          </Field>\n        )\n\n        assertLinkedWithLabel(getControl(), getLabels())\n      })\n\n      it('should link a control and multiple `Label` components when inside a `Field`', () => {\n        render(\n          <Field>\n            <Label>My Label #1</Label>\n            <Label>My Label #2</Label>\n            <Control />\n          </Field>\n        )\n\n        assertLinkedWithLabel(getControl(), getLabels())\n      })\n\n      it('should link a control and a `Description` when inside a `Field`', () => {\n        render(\n          <Field>\n            <Control />\n            <Description>My Description</Description>\n          </Field>\n        )\n\n        assertLinkedWithDescription(getControl(), getDescriptions())\n      })\n\n      it('should link a control and multiple `Description` components when inside a `Field`', () => {\n        render(\n          <Field>\n            <Control />\n            <Description>My Description #1</Description>\n            <Description>My Description #2</Description>\n            <Description>My Description #3</Description>\n          </Field>\n        )\n\n        assertLinkedWithDescription(getControl(), getDescriptions())\n      })\n\n      it('should link a control with a `Label` and a `Description` when inside a `Field`', () => {\n        render(\n          <Field>\n            <Label>My Label</Label>\n            <Control />\n            <Description>My Description</Description>\n          </Field>\n        )\n\n        assertLinkedWithDescription(getControl(), getDescriptions())\n        assertLinkedWithLabel(getControl(), getLabels())\n      })\n    })\n  })\n\n  describe('Mouse interactions', () => {\n    describe('Inside `Field`', () => {\n      it('should be possible to click a `Label`, and focus the control when in a `Field`', async () => {\n        render(\n          <Field>\n            <Label>My Label</Label>\n            <Control />\n          </Field>\n        )\n\n        assertActiveElement(document.body)\n        await click(getLabel())\n        assertActiveElement(getControl())\n      })\n\n      it('should not be possible to click a `Label`, if the `Label` has the `passive` prop', async () => {\n        render(\n          <Field>\n            <Label passive>My Label</Label>\n            <Control />\n          </Field>\n        )\n\n        assertActiveElement(document.body)\n        await click(getLabel())\n        assertActiveElement(document.body)\n      })\n\n      it('should not be possible to click a `Label` and focus the control, if the control is disabled', async () => {\n        render(\n          <Field>\n            <Label>My Label</Label>\n            <Control disabled />\n          </Field>\n        )\n\n        assertActiveElement(document.body)\n        await click(getLabel())\n        assertActiveElement(document.body)\n      })\n\n      it('should not be possible to click a `Label` and focus the control, if the `Field` is disabled', async () => {\n        render(\n          <Field disabled>\n            <Label>My Label</Label>\n            <Control />\n          </Field>\n        )\n\n        assertActiveElement(document.body)\n        await click(getLabel())\n        assertActiveElement(document.body)\n      })\n    })\n\n    describe('Inside `Fieldset`', () => {\n      it('should not be possible to click a `Label` and focus the control, if the `Fieldset` is disabled', async () => {\n        render(\n          <Fieldset disabled>\n            <Field>\n              <Label>My Label</Label>\n              <Control />\n            </Field>\n          </Fieldset>\n        )\n\n        assertActiveElement(document.body)\n        await click(getLabel())\n        assertActiveElement(document.body)\n      })\n    })\n  })\n}\n\nexport function commonFormScenarios(\n  Control: React.ComponentType<any>,\n  {\n    performUserInteraction,\n  }: { performUserInteraction: (control: HTMLElement | null) => PromiseLike<void> }\n) {\n  describe('Form compatibility', () => {\n    it('should render native (hidden) form elements for the control', () => {\n      render(\n        <form>\n          <Control name=\"foo\" />\n        </form>\n      )\n\n      expect(document.querySelector('[name=foo]')).toBeInTheDocument()\n    })\n\n    it('should submit the form with all the data', async () => {\n      let formDataMock = jest.fn()\n\n      render(\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            formDataMock(new FormData(e.target as HTMLFormElement))\n          }}\n        >\n          <Control name=\"foo\" />\n          <button>Submit</button>\n        </form>\n      )\n\n      // Submit form\n      await click(screen.getByText('Submit'))\n\n      // Ensure the form was submitted with the `foo` input present\n      expect(formDataMock.mock.calls[0][0].has('foo')).toBe(true)\n    })\n\n    it('should not submit the data if the control is disabled', async () => {\n      let submits = jest.fn()\n\n      function Example() {\n        return (\n          <form\n            onSubmit={(event) => {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget).entries()])\n            }}\n          >\n            <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n            <Control name=\"bar\" disabled />\n            <button>Submit</button>\n          </form>\n        )\n      }\n\n      render(<Example />)\n\n      // Submit the form\n      await click(screen.getByText('Submit'))\n\n      // Verify that the form has been submitted\n      expect(submits).toHaveBeenLastCalledWith([\n        ['foo', 'bar'], // The only available field\n      ])\n    })\n\n    it(\n      'should reset the control when the form is reset',\n      suppressConsoleLogs(async () => {\n        let formDataMock = jest.fn()\n\n        render(\n          <form\n            onSubmit={(e) => {\n              e.preventDefault()\n              formDataMock(new FormData(e.target as HTMLFormElement))\n            }}\n          >\n            <Field>\n              <Label>The Label</Label>\n              <Control name=\"foo\" />\n            </Field>\n\n            <button>Submit</button>\n            <button type=\"reset\">Reset</button>\n          </form>\n        )\n\n        // Submit the form to get the initial state of the form\n        await click(screen.getByText('Submit'))\n        let formState = Object.fromEntries(formDataMock.mock.calls[0][0])\n\n        // Make changes to the control\n        await performUserInteraction(getControl())\n\n        // Submit form\n        await click(screen.getByText('Submit'))\n\n        // Ensure the form was, and the values are different\n        let newFormState = Object.fromEntries(formDataMock.mock.calls[1][0])\n        expect(newFormState).not.toEqual(formState)\n\n        // Reset the form\n        await click(screen.getByText('Reset'))\n\n        // Ensure the form was reset\n        await click(screen.getByText('Submit'))\n\n        // Ensure the form state looks like the initial state\n        let resetFormState = Object.fromEntries(formDataMock.mock.calls[2][0])\n        expect(resetFormState).toEqual(formState)\n      })\n    )\n  })\n}\n\nexport function commonRenderingScenarios(\n  Control: React.ComponentType<any>,\n  { getElement }: { getElement: () => HTMLElement | null }\n) {\n  describe('Rendering', () => {\n    it('should render a control', async () => {\n      render(<Control />)\n\n      expect(getElement()).toBeInTheDocument()\n    })\n\n    it('should have an `id` attached', () => {\n      render(<Control />)\n\n      expect(getElement()).toHaveAttribute('id')\n    })\n\n    it('should be possible to override the `id`', () => {\n      render(<Control id=\"foo\" />)\n\n      expect(getElement()).toHaveAttribute('id', 'foo')\n    })\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/snapshot.ts",
    "content": "import { useLayoutEffect, useRef, type MutableRefObject } from 'react'\n\nexport function createSnapshot() {\n  let snapshots: HTMLElement[] = []\n\n  return {\n    get latest() {\n      return snapshots.at(-1)\n    },\n\n    get firstChild() {\n      return this.latest?.firstChild ?? null\n    },\n\n    use() {\n      let ref = useRef<HTMLElement>(null)\n      useLayoutEffect(() => this.take(ref), [])\n      return ref\n    },\n\n    take(ref: MutableRefObject<HTMLElement | null>) {\n      snapshots.push(ref.current!.parentElement!.cloneNode(true) as any)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/ssr.tsx",
    "content": "import {\n  cleanup,\n  render,\n  screen,\n  type RenderOptions,\n  type RenderResult,\n} from '@testing-library/react'\nimport React, { type ReactElement } from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { env } from '../utils/env'\n\ntype ServerRenderOptions = Omit<RenderOptions, 'queries'> & {\n  strict?: boolean\n}\n\ninterface ServerRenderResult {\n  type: 'ssr' | 'hydrate'\n  contents: string\n  result: RenderResult\n  hydrate: () => Promise<ServerRenderResult>\n}\n\nexport async function renderSSR(\n  ui: ReactElement,\n  options: ServerRenderOptions = {}\n): Promise<ServerRenderResult> {\n  let container = document.createElement('div')\n  document.body.appendChild(container)\n  options = { ...options, container }\n\n  if (options.strict) {\n    options = {\n      ...options,\n      wrapper({ children }) {\n        return <React.StrictMode>{children}</React.StrictMode>\n      },\n    }\n  }\n\n  env.set('server')\n  let contents = renderToString(ui)\n  let result = render(<div dangerouslySetInnerHTML={{ __html: contents }} />, options)\n\n  async function hydrate(): Promise<ServerRenderResult> {\n    cleanup()\n\n    container.remove()\n\n    container = document.createElement('div')\n    container.innerHTML = contents\n    document.body.appendChild(container)\n\n    env.set('client')\n\n    let newResult = render(ui, {\n      ...options,\n      container,\n      hydrate: true,\n    })\n\n    return {\n      type: 'hydrate',\n      contents: container.innerHTML,\n      result: newResult,\n      hydrate,\n    }\n  }\n\n  return {\n    type: 'ssr',\n    contents,\n    result,\n    hydrate,\n  }\n}\n\nexport async function renderHydrate(el: ReactElement, options: ServerRenderOptions = {}) {\n  return renderSSR(el, options).then((r) => r.hydrate())\n}\n\nexport { screen }\n"
  },
  {
    "path": "packages/@headlessui-react/src/test-utils/suppress-console-logs.ts",
    "content": "type FunctionPropertyNames<T> = {\n  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T] &\n  string\n\nexport function suppressConsoleLogs<T extends unknown[]>(\n  cb: (...args: T) => unknown,\n  type: FunctionPropertyNames<typeof globalThis.console> = 'error'\n) {\n  return (...args: T) => {\n    let spy = jest.spyOn(globalThis.console, type).mockImplementation(jest.fn())\n\n    return new Promise<unknown>((resolve, reject) => {\n      Promise.resolve(cb(...args)).then(resolve, reject)\n    }).finally(() => spy.mockRestore())\n  }\n}\n\nexport function mockingConsoleLogs<T extends unknown[]>(\n  cb: (spy: jest.SpyInstance, ...args: T) => unknown,\n  type: FunctionPropertyNames<typeof globalThis.console> = 'error'\n) {\n  return (...args: T) => {\n    let spy = jest.spyOn(globalThis.console, type).mockImplementation(jest.fn())\n\n    return new Promise<unknown>((resolve, reject) => {\n      Promise.resolve(cb(spy, ...args)).then(resolve, reject)\n    }).finally(() => spy.mockRestore())\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/types.ts",
    "content": "import type { JSXElementConstructor, ReactElement, ReactNode } from 'react'\n\nexport type ReactTag = keyof React.JSX.IntrinsicElements | JSXElementConstructor<any>\n\nexport type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never\n\nexport type PropsOf<TTag extends ReactTag> = TTag extends React.ElementType\n  ? Omit<React.ComponentProps<TTag>, 'ref'>\n  : never\n\ntype PropsWeControl = 'as' | 'children' | 'refName' | 'className'\n\n// Resolve the props of the component, but ensure to omit certain props that we control\ntype CleanProps<TTag extends ReactTag, TOmittableProps extends PropertyKey = never> = Omit<\n  PropsOf<TTag>,\n  TOmittableProps | PropsWeControl\n>\n\n// Add certain props that we control\ntype OurProps<TTag extends ReactTag, TSlot> = {\n  as?: TTag\n  children?: ReactNode | ((bag: TSlot) => ReactElement)\n  refName?: string\n}\n\ntype HasProperty<T extends object, K extends PropertyKey> = T extends never\n  ? never\n  : K extends keyof T\n    ? true\n    : never\n\n// Conditionally override the `className`, to also allow for a function\n// if and only if the PropsOf<TTag> already defines `className`.\n// This will allow us to have a TS error on as={Fragment}\ntype ClassNameOverride<TTag extends ReactTag, TSlot = {}> =\n  // Order is important here, because `never extends true` is `true`...\n  true extends HasProperty<PropsOf<TTag>, 'className'>\n    ? { className?: PropsOf<TTag>['className'] | ((bag: TSlot) => string) }\n    : {}\n\n// Provide clean TypeScript props, which exposes some of our custom APIs.\nexport type Props<\n  TTag extends ReactTag,\n  TSlot = {},\n  TOmittableProps extends PropertyKey = never,\n  Overrides = {},\n> = CleanProps<TTag, TOmittableProps | keyof Overrides> &\n  OurProps<TTag, TSlot> &\n  ClassNameOverride<TTag, TSlot> &\n  Overrides\n\nexport type EnsureArray<T> = T extends any[] ? T : Expand<T>[]\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/__snapshots__/render.test.tsx.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Default functionality should be possible to add a ref with a different name 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div\n    potato=\\\\\"[object Object]\\\\\"\n  >\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Default functionality should be possible to change the underlying DOM node using the \\`as\\` prop 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <button />\n</div>\"\n`;\n\nexports[`Default functionality should be possible to change the underlying DOM node using the \\`as\\` prop and still have a function as children 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <button>\n    <span>\n      Contents\n    </span>\n  </button>\n</div>\"\n`;\n\nexports[`Default functionality should be possible to merge class fns when rendering 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper-with-class\\\\\"\n>\n  <div\n    data-testid=\\\\\"wrapper\\\\\"\n  >\n    <div\n      class=\\\\\"test-inner test-outer\\\\\"\n    />\n  </div>\n</div>\"\n`;\n\nexports[`Default functionality should be possible to merge classes when rendering 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper-with-class\\\\\"\n>\n  <div\n    class=\\\\\"test-inner test-outer\\\\\"\n  />\n</div>\"\n`;\n\nexports[`Default functionality should be possible to passthrough props to a dummy component 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div\n    a=\\\\\"1\\\\\"\n    b=\\\\\"2\\\\\"\n    c=\\\\\"3\\\\\"\n  />\n</div>\"\n`;\n\nexports[`Default functionality should be possible to render a dummy component 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div />\n</div>\"\n`;\n\nexports[`Default functionality should be possible to render a dummy component with some children as a callback 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    <span>\n      Contents\n    </span>\n  </div>\n</div>\"\n`;\n\nexports[`Default functionality should be possible to render the children only when the \\`as\\` prop is set to Fragment 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  Contents\n</div>\"\n`;\n\nexports[`Default functionality should forward all the props to the first child when using an as={Fragment} 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <span\n    a=\\\\\"1\\\\\"\n    b=\\\\\"1\\\\\"\n  >\n    Contents\n  </span>\n</div>\"\n`;\n\nexports[`Default functionality should forward boolean values from \\`slot\\` as data attributes 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div\n    data-a=\\\\\"\\\\\"\n    data-c=\\\\\"\\\\\"\n    data-headlessui-state=\\\\\"a c\\\\\"\n  >\n    <span>\n      Contents\n    </span>\n  </div>\n</div>\"\n`;\n\nexports[`Default functionality should not error when we are rendering a Fragment with multiple children when we don't passthrough additional props 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <span>\n    Contents A\n  </span>\n  <span>\n    Contents B\n  </span>\n</div>\"\n`;\n\nexports[`Default functionality should prefer user provided data attributes over the ones we set automatically 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <span\n    data-accept=\\\\\"always\\\\\"\n    data-headlessui-state=\\\\\"accept\\\\\"\n  >\n    Contents\n  </span>\n</div>\"\n`;\n\nexports[`Features.RenderStrategy Hidden render strategy should be possible to render an \\`unmount={false}\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div\n    hidden=\\\\\"\\\\\"\n    style=\\\\\"display: none;\\\\\"\n  >\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.RenderStrategy Hidden render strategy should be possible to render an \\`unmount={false}\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.RenderStrategy Unmount render strategy should be possible to render an \\`unmount\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n/>\"\n`;\n\nexports[`Features.RenderStrategy Unmount render strategy should be possible to render an \\`unmount\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy Hidden render strategy should be possible to render an \\`unmount={false}\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div\n    hidden=\\\\\"\\\\\"\n    style=\\\\\"display: none;\\\\\"\n  >\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy Hidden render strategy should be possible to render an \\`unmount={false}\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy Unmount render strategy should be possible to render an \\`unmount\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n/>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy Unmount render strategy should be possible to render an \\`unmount\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy should be possible to render a \\`static\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static | Features.RenderStrategy should be possible to render a \\`static\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static should be possible to render a \\`static\\` dummy component (show = false) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n\nexports[`Features.Static should be possible to render a \\`static\\` dummy component (show = true) 1`] = `\n\"<div\n  data-testid=\\\\\"wrapper\\\\\"\n>\n  <div>\n    Contents\n  </div>\n</div>\"\n`;\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/active-element-history.ts",
    "content": "import { onDocumentReady } from './document-ready'\nimport * as DOM from './dom'\nimport { focusableSelector } from './focus-management'\n\nexport let history: (HTMLOrSVGElement & Element)[] = []\nonDocumentReady(() => {\n  function handle(e: Event) {\n    if (!DOM.isHTMLorSVGElement(e.target)) return\n    if (e.target === document.body) return\n    if (history[0] === e.target) return\n\n    let focusableElement = e.target\n\n    // Figure out the closest focusable element, this is needed in a situation\n    // where you click on a non-focusable element inside a focusable element.\n    //\n    // E.g.:\n    //\n    // ```html\n    // <button>\n    //   <span>Click me</span>\n    // </button>\n    // ```\n    focusableElement = focusableElement.closest(focusableSelector) as HTMLOrSVGElement & Element\n\n    history.unshift(focusableElement ?? e.target)\n\n    // Filter out DOM Nodes that don't exist anymore\n    history = history.filter((x) => x != null && x.isConnected)\n    history.splice(10) // Only keep the 10 most recent items\n  }\n\n  window.addEventListener('click', handle, { capture: true })\n  window.addEventListener('mousedown', handle, { capture: true })\n  window.addEventListener('focus', handle, { capture: true })\n\n  document.body.addEventListener('click', handle, { capture: true })\n  document.body.addEventListener('mousedown', handle, { capture: true })\n  document.body.addEventListener('focus', handle, { capture: true })\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/bugs.ts",
    "content": "import * as DOM from './dom'\n\n// See: https://github.com/facebook/react/issues/7711\n// See: https://github.com/facebook/react/pull/20612\n// See: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled (2.)\nexport function isDisabledReactIssue7711(element: Element): boolean {\n  let parent = element.parentElement\n  let legend = null\n\n  while (parent && !DOM.isHTMLFieldSetElement(parent)) {\n    if (DOM.isHTMLLegendElement(parent)) legend = parent\n    parent = parent.parentElement\n  }\n\n  let isParentDisabled = parent?.getAttribute('disabled') === '' ?? false\n  if (isParentDisabled && isFirstLegend(legend)) return false\n\n  return isParentDisabled\n}\n\nfunction isFirstLegend(element: HTMLLegendElement | null): boolean {\n  if (!element) return false\n\n  let previous = element.previousElementSibling\n\n  while (previous !== null) {\n    if (DOM.isHTMLLegendElement(previous)) return false\n    previous = previous.previousElementSibling\n  }\n\n  return true\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/calculate-active-index.ts",
    "content": "function assertNever(x: never): never {\n  throw new Error('Unexpected object: ' + x)\n}\n\nexport enum Focus {\n  /** Focus the first non-disabled item. */\n  First,\n\n  /** Focus the previous non-disabled item. */\n  Previous,\n\n  /** Focus the next non-disabled item. */\n  Next,\n\n  /** Focus the last non-disabled item. */\n  Last,\n\n  /** Focus a specific item based on the `id` of the item. */\n  Specific,\n\n  /** Focus no items at all. */\n  Nothing,\n}\n\nexport function calculateActiveIndex<TItem>(\n  action: { focus: Focus.Specific; id: string } | { focus: Exclude<Focus, Focus.Specific> },\n  resolvers: {\n    resolveItems(): TItem[]\n    resolveActiveIndex(): number | null\n    resolveId(item: TItem, index: number, items: TItem[]): string\n    resolveDisabled(item: TItem, index: number, items: TItem[]): boolean\n  }\n) {\n  let items = resolvers.resolveItems()\n  if (items.length <= 0) return null\n\n  let currentActiveIndex = resolvers.resolveActiveIndex()\n  let activeIndex = currentActiveIndex ?? -1\n\n  switch (action.focus) {\n    case Focus.First: {\n      for (let i = 0; i < items.length; ++i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Previous: {\n      // If nothing is active, focus the last relevant item\n      if (activeIndex === -1) activeIndex = items.length\n\n      for (let i = activeIndex - 1; i >= 0; --i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Next: {\n      for (let i = activeIndex + 1; i < items.length; ++i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Last: {\n      for (let i = items.length - 1; i >= 0; --i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Specific: {\n      for (let i = 0; i < items.length; ++i) {\n        if (resolvers.resolveId(items[i], i, items) === action.id) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Nothing:\n      return null\n\n    default:\n      assertNever(action)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/class-names.ts",
    "content": "export function classNames(...classes: (false | null | undefined | string)[]): string {\n  return Array.from(\n    new Set(\n      classes.flatMap((value) => {\n        if (typeof value === 'string') {\n          return value.split(' ')\n        }\n\n        return []\n      })\n    )\n  )\n    .filter(Boolean)\n    .join(' ')\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/default-map.ts",
    "content": "export class DefaultMap<T = string, V = any> extends Map<T, V> {\n  constructor(private factory: (key: T) => V) {\n    super()\n  }\n\n  get(key: T): V {\n    let value = super.get(key)\n\n    if (value === undefined) {\n      value = this.factory(key)\n      this.set(key, value)\n    }\n\n    return value\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/disposables.ts",
    "content": "import { microTask } from './micro-task'\n\nexport type Disposables = ReturnType<typeof disposables>\n\n/**\n * Disposables are a way to manage event handlers and functions like\n * `setTimeout` and `requestAnimationFrame` that need to be cleaned up when they\n * are no longer needed.\n *\n *\n * When you register a disposable function, it is added to a collection of\n * disposables. Each disposable in the collection provides a `dispose` clean up\n * function that can be called when it's no longer needed. There is also a\n * `dispose` function on the collection itself that can be used to clean up all\n * pending disposables in that collection.\n */\nexport function disposables() {\n  let _disposables: Function[] = []\n\n  let api = {\n    addEventListener<TEventName extends keyof WindowEventMap>(\n      element: HTMLElement | Window | Document,\n      name: TEventName,\n      listener: (event: WindowEventMap[TEventName]) => any,\n      options?: boolean | AddEventListenerOptions\n    ) {\n      element.addEventListener(name, listener as any, options)\n      return api.add(() => element.removeEventListener(name, listener as any, options))\n    },\n\n    requestAnimationFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      let raf = requestAnimationFrame(...args)\n      return api.add(() => cancelAnimationFrame(raf))\n    },\n\n    nextFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      return api.requestAnimationFrame(() => {\n        return api.requestAnimationFrame(...args)\n      })\n    },\n\n    setTimeout(...args: Parameters<typeof setTimeout>) {\n      let timer = setTimeout(...args)\n      return api.add(() => clearTimeout(timer))\n    },\n\n    microTask(...args: Parameters<typeof microTask>) {\n      let task = { current: true }\n      microTask(() => {\n        if (task.current) {\n          args[0]()\n        }\n      })\n      return api.add(() => {\n        task.current = false\n      })\n    },\n\n    style(node: ElementCSSInlineStyle, property: string, value: string) {\n      let previous = node.style.getPropertyValue(property)\n      Object.assign(node.style, { [property]: value })\n      return this.add(() => {\n        Object.assign(node.style, { [property]: previous })\n      })\n    },\n\n    group(cb: (d: typeof this) => void) {\n      let d = disposables()\n      cb(d)\n      return this.add(() => d.dispose())\n    },\n\n    add(cb: () => void) {\n      // Ensure we don't add the same callback twice\n      if (!_disposables.includes(cb)) {\n        _disposables.push(cb)\n      }\n\n      return () => {\n        let idx = _disposables.indexOf(cb)\n        if (idx >= 0) {\n          for (let dispose of _disposables.splice(idx, 1)) {\n            dispose()\n          }\n        }\n      }\n    },\n\n    dispose() {\n      for (let dispose of _disposables.splice(0)) {\n        dispose()\n      }\n    },\n  }\n\n  return api\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/document-ready.ts",
    "content": "export function onDocumentReady(cb: () => void) {\n  function check() {\n    if (document.readyState === 'loading') return\n    cb()\n    document.removeEventListener('DOMContentLoaded', check)\n  }\n\n  if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n    document.addEventListener('DOMContentLoaded', check)\n    check()\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/dom.ts",
    "content": "// This file contains a bunch of utilities to verify that an element is of a\n// specific type.\n//\n// Normally you can use `element instanceof HTMLElement`, but if you are in\n// different JS Context (e.g.: inside an iframe) then the `HTMLElement` will be\n// a different class and the check will fail.\n//\n// Instead, we will check for certain properties to determine if the element\n// is of a specific type.\n\nexport function isNode(element: unknown): element is Node {\n  if (typeof element !== 'object') return false\n  if (element === null) return false\n  return 'nodeType' in element\n}\n\nexport function isElement(element: unknown): element is Element {\n  return isNode(element) && 'tagName' in element\n}\n\nexport function isHTMLElement(element: unknown): element is HTMLElement {\n  return isElement(element) && 'accessKey' in element\n}\n\n// HTMLOrSVGElement doesn't inherit from HTMLElement or from Element. But this\n// is the type that contains the `tabIndex` property.\n//\n// Once we know that this is an `HTMLOrSVGElement` we also know that it is an\n// `Element` (that contains more information)\nexport function isHTMLorSVGElement(element: unknown): element is HTMLOrSVGElement & Element {\n  return isElement(element) && 'tabIndex' in element\n}\n\nexport function hasInlineStyle(element: unknown): element is ElementCSSInlineStyle {\n  return isElement(element) && 'style' in element\n}\n\nexport function isHTMLIframeElement(element: unknown): element is HTMLIFrameElement {\n  return isHTMLElement(element) && element.nodeName === 'IFRAME'\n}\n\nexport function isHTMLInputElement(element: unknown): element is HTMLInputElement {\n  return isHTMLElement(element) && element.nodeName === 'INPUT'\n}\n\nexport function isHTMLTextAreaElement(element: unknown): element is HTMLTextAreaElement {\n  return isHTMLElement(element) && element.nodeName === 'TEXTAREA'\n}\n\nexport function isHTMLLabelElement(element: unknown): element is HTMLLabelElement {\n  return isHTMLElement(element) && element.nodeName === 'LABEL'\n}\n\nexport function isHTMLFieldSetElement(element: unknown): element is HTMLFieldSetElement {\n  return isHTMLElement(element) && element.nodeName === 'FIELDSET'\n}\n\nexport function isHTMLLegendElement(element: unknown): element is HTMLLegendElement {\n  return isHTMLElement(element) && element.nodeName === 'LEGEND'\n}\n\n// https://html.spec.whatwg.org/#interactive-content-2\n// - a (if the href attribute is present)\n// - audio (if the controls attribute is present)\n// - button\n// - details\n// - embed\n// - iframe\n// - img (if the usemap attribute is present)\n// - input (if the type attribute is not in the Hidden state)\n// - label\n// - select\n// - textarea\n// - video (if the controls attribute is present)\nexport function isInteractiveElement(element: unknown): element is Element {\n  if (!isElement(element)) return false\n\n  return element.matches(\n    'a[href],audio[controls],button,details,embed,iframe,img[usemap],input:not([type=\"hidden\"]),label,select,textarea,video[controls]'\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/element-movement.ts",
    "content": "import { disposables } from './disposables'\n\nexport const ElementPositionState = {\n  Idle: { kind: 'Idle' as const },\n  Tracked: (position: string) => ({ kind: 'Tracked' as const, position }),\n  Moved: { kind: 'Moved' as const },\n}\n\ntype ResolvedStates<T extends Record<string, any>> = {\n  [K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : T[K]\n}[keyof T]\n\nexport type ElementPositionState = ResolvedStates<typeof ElementPositionState>\n\nexport function computeVisualPosition(element: HTMLElement): string {\n  let rect = element.getBoundingClientRect()\n  return `${rect.x},${rect.y}`\n}\n\nexport function detectMovement(\n  target: HTMLElement,\n  state: ResolvedStates<typeof ElementPositionState>,\n  onMove: () => void\n) {\n  let d = disposables()\n\n  if (state.kind === 'Tracked') {\n    let { position } = state\n\n    function check() {\n      if (position !== computeVisualPosition(target)) {\n        d.dispose()\n        onMove()\n      }\n    }\n\n    let observer = new ResizeObserver(check)\n    observer.observe(target)\n    d.add(() => observer.disconnect())\n\n    d.addEventListener(window, 'scroll', check, { passive: true })\n    d.addEventListener(window, 'resize', check)\n  }\n\n  return () => d.dispose()\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/env.ts",
    "content": "type RenderEnv = 'client' | 'server'\ntype HandoffState = 'pending' | 'complete'\n\nclass Env {\n  current: RenderEnv = this.detect()\n  handoffState: HandoffState = 'pending'\n  currentId = 0\n\n  set(env: RenderEnv): void {\n    if (this.current === env) return\n\n    this.handoffState = 'pending'\n    this.currentId = 0\n    this.current = env\n  }\n\n  reset(): void {\n    this.set(this.detect())\n  }\n\n  nextId() {\n    return ++this.currentId\n  }\n\n  get isServer(): boolean {\n    return this.current === 'server'\n  }\n\n  get isClient(): boolean {\n    return this.current === 'client'\n  }\n\n  private detect(): RenderEnv {\n    if (typeof window === 'undefined' || typeof document === 'undefined') {\n      return 'server'\n    }\n\n    return 'client'\n  }\n\n  handoff(): void {\n    if (this.handoffState === 'pending') {\n      this.handoffState = 'complete'\n    }\n  }\n\n  get isHandoffComplete(): boolean {\n    return this.handoffState === 'complete'\n  }\n}\n\nexport let env = new Env()\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/focus-management.ts",
    "content": "import type { MutableRefObject } from 'react'\nimport { disposables } from './disposables'\nimport * as DOM from './dom'\nimport { match } from './match'\nimport { getActiveElement, getOwnerDocument, getRootNode } from './owner'\n\n// Credit:\n//  - https://stackoverflow.com/a/30753870\nexport let focusableSelector = [\n  '[contentEditable=true]',\n  '[tabindex]',\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'iframe',\n  'input:not([disabled])',\n  'select:not([disabled])',\n  // TODO: Re-enable once we bump JSDOM\n  // 'details:not(:has(> summary))',\n  'details>summary',\n  'textarea:not([disabled])',\n]\n  .map(\n    process.env.NODE_ENV === 'test'\n      ? // TODO: Remove this once JSDOM fixes the issue where an element that is\n        // \"hidden\" can be the document.activeElement, because this is not possible\n        // in real browsers.\n        (selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`\n      : (selector) => `${selector}:not([tabindex='-1'])`\n  )\n  .join(',')\n\nlet autoFocusableSelector = [\n  // In a perfect world this was just `autofocus`, but React doesn't pass `autofocus` to the DOM...\n  '[data-autofocus]',\n]\n  .map(\n    process.env.NODE_ENV === 'test'\n      ? // TODO: Remove this once JSDOM fixes the issue where an element that is\n        // \"hidden\" can be the document.activeElement, because this is not possible\n        // in real browsers.\n        (selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`\n      : (selector) => `${selector}:not([tabindex='-1'])`\n  )\n  .join(',')\n\nexport enum Focus {\n  /** Focus the first non-disabled element */\n  First = 1 << 0,\n\n  /** Focus the previous non-disabled element */\n  Previous = 1 << 1,\n\n  /** Focus the next non-disabled element */\n  Next = 1 << 2,\n\n  /** Focus the last non-disabled element */\n  Last = 1 << 3,\n\n  /** Wrap tab around */\n  WrapAround = 1 << 4,\n\n  /** Prevent scrolling the focusable elements into view */\n  NoScroll = 1 << 5,\n\n  /** Focus the first focusable element with the `data-autofocus` attribute. */\n  AutoFocus = 1 << 6,\n}\n\nexport enum FocusResult {\n  /** Something went wrong while trying to focus. */\n  Error,\n\n  /** When `Focus.WrapAround` is enabled, going from position `N` to `N+1` where `N` is the last index in the array, then we overflow. */\n  Overflow,\n\n  /** Focus was successful. */\n  Success,\n\n  /** When `Focus.WrapAround` is enabled, going from position `N` to `N-1` where `N` is the first index in the array, then we underflow. */\n  Underflow,\n}\n\nenum Direction {\n  Previous = -1,\n  Next = 1,\n}\n\ninterface QuerySelectorAll {\n  querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>\n}\n\nexport function getFocusableElements(container: QuerySelectorAll | null = document.body) {\n  if (container == null) return []\n  return Array.from(container.querySelectorAll<HTMLElement>(focusableSelector)).sort(\n    // We want to move `tabIndex={0}` to the end of the list, this is what the browser does as well.\n    (a, z) =>\n      Math.sign((a.tabIndex || Number.MAX_SAFE_INTEGER) - (z.tabIndex || Number.MAX_SAFE_INTEGER))\n  )\n}\n\nexport function getAutoFocusableElements(container: HTMLElement | null = document.body) {\n  if (container == null) return []\n  return Array.from(container.querySelectorAll<HTMLElement>(autoFocusableSelector)).sort(\n    // We want to move `tabIndex={0}` to the end of the list, this is what the browser does as well.\n    (a, z) =>\n      Math.sign((a.tabIndex || Number.MAX_SAFE_INTEGER) - (z.tabIndex || Number.MAX_SAFE_INTEGER))\n  )\n}\n\nexport enum FocusableMode {\n  /** The element itself must be focusable. */\n  Strict,\n\n  /** The element should be inside of a focusable element. */\n  Loose,\n}\n\nexport function isFocusableElement(\n  element: HTMLOrSVGElement & Element,\n  mode: FocusableMode = FocusableMode.Strict\n) {\n  if (element === getOwnerDocument(element)?.body) return false\n\n  return match(mode, {\n    [FocusableMode.Strict]() {\n      return element.matches(focusableSelector)\n    },\n    [FocusableMode.Loose]() {\n      let next: Element | null = element\n\n      while (next !== null) {\n        if (next.matches(focusableSelector)) return true\n        next = next.parentElement\n      }\n\n      return false\n    },\n  })\n}\n\nexport function restoreFocusIfNecessary(element: HTMLElement | null) {\n  disposables().nextFrame(() => {\n    let activeElement = getActiveElement(element)\n\n    if (\n      activeElement &&\n      DOM.isHTMLorSVGElement(activeElement) &&\n      !isFocusableElement(activeElement, FocusableMode.Strict)\n    ) {\n      focusElement(element)\n    }\n  })\n}\n\n// The method of triggering an action, this is used to determine how we should\n// restore focus after an action has been performed.\nenum ActivationMethod {\n  /* If the action was triggered by a keyboard event. */\n  Keyboard = 0,\n\n  /* If the action was triggered by a mouse / pointer / ... event.*/\n  Mouse = 1,\n}\n\n// We want to be able to set and remove the `data-headlessui-mouse` attribute on the `html` element.\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n  document.addEventListener(\n    'keydown',\n    (event) => {\n      if (event.metaKey || event.altKey || event.ctrlKey) {\n        return\n      }\n\n      document.documentElement.dataset.headlessuiFocusVisible = ''\n    },\n    true\n  )\n\n  document.addEventListener(\n    'click',\n    (event) => {\n      // Event originated from an actual mouse click\n      if (event.detail === ActivationMethod.Mouse) {\n        delete document.documentElement.dataset.headlessuiFocusVisible\n      }\n\n      // Event originated from a keyboard event that triggered the `click` event\n      else if (event.detail === ActivationMethod.Keyboard) {\n        document.documentElement.dataset.headlessuiFocusVisible = ''\n      }\n    },\n    true\n  )\n}\n\nexport function focusElement(element: HTMLOrSVGElement | null) {\n  element?.focus({ preventScroll: true })\n}\n\n// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select\nlet selectableSelector = ['textarea', 'input'].join(',')\nfunction isSelectableElement(\n  element: Element | null\n): element is HTMLInputElement | HTMLTextAreaElement {\n  return element?.matches?.(selectableSelector) ?? false\n}\n\nexport function sortByDomNode<T>(\n  nodes: T[],\n  resolveKey: (item: T) => HTMLElement | null = (i) => i as HTMLElement | null\n): T[] {\n  return nodes.slice().sort((aItem, zItem) => {\n    let a = resolveKey(aItem)\n    let z = resolveKey(zItem)\n\n    if (a === null || z === null) return 0\n\n    let position = a.compareDocumentPosition(z)\n\n    if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1\n    if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1\n    return 0\n  })\n}\n\nexport function focusFrom(\n  current: HTMLElement | null,\n  focus: Focus,\n  container = current === null ? document.body : getRootNode(current)\n) {\n  return focusIn(getFocusableElements(container), focus, { relativeTo: current })\n}\n\nexport function focusIn(\n  container: HTMLElement | HTMLElement[],\n  focus: Focus,\n  {\n    sorted = true,\n    relativeTo = null,\n    skipElements = [],\n  }: Partial<{\n    sorted: boolean\n    relativeTo: HTMLElement | null\n    skipElements: (HTMLElement | MutableRefObject<HTMLElement | null>)[]\n  }> = {}\n) {\n  let root = Array.isArray(container)\n    ? container.length > 0\n      ? getRootNode(container[0])\n      : document\n    : getRootNode(container)\n\n  let elements = Array.isArray(container)\n    ? sorted\n      ? sortByDomNode(container)\n      : container\n    : focus & Focus.AutoFocus\n      ? getAutoFocusableElements(container)\n      : getFocusableElements(container)\n\n  if (skipElements.length > 0 && elements.length > 1) {\n    elements = elements.filter(\n      (element) =>\n        !skipElements.some(\n          (skipElement) =>\n            skipElement != null && 'current' in skipElement\n              ? skipElement?.current === element // Handle MutableRefObject\n              : skipElement === element // Handle HTMLElement directly\n        )\n    )\n  }\n\n  relativeTo = relativeTo ?? (root?.activeElement as HTMLElement)\n\n  let direction = (() => {\n    if (focus & (Focus.First | Focus.Next)) return Direction.Next\n    if (focus & (Focus.Previous | Focus.Last)) return Direction.Previous\n\n    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')\n  })()\n\n  let startIndex = (() => {\n    if (focus & Focus.First) return 0\n    if (focus & Focus.Previous) return Math.max(0, elements.indexOf(relativeTo)) - 1\n    if (focus & Focus.Next) return Math.max(0, elements.indexOf(relativeTo)) + 1\n    if (focus & Focus.Last) return elements.length - 1\n\n    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')\n  })()\n\n  let focusOptions = focus & Focus.NoScroll ? { preventScroll: true } : {}\n\n  let offset = 0\n  let total = elements.length\n  let next = undefined\n  do {\n    // Guard against infinite loops\n    if (offset >= total || offset + total <= 0) return FocusResult.Error\n\n    let nextIdx = startIndex + offset\n\n    if (focus & Focus.WrapAround) {\n      nextIdx = (nextIdx + total) % total\n    } else {\n      if (nextIdx < 0) return FocusResult.Underflow\n      if (nextIdx >= total) return FocusResult.Overflow\n    }\n\n    next = elements[nextIdx]\n\n    // Try the focus the next element, might not work if it is \"hidden\" to the user.\n    next?.focus(focusOptions)\n\n    // Try the next one in line\n    offset += direction\n  } while (next !== getActiveElement(next))\n\n  // By default if you <Tab> to a text input or a textarea, the browser will\n  // select all the text once the focus is inside these DOM Nodes. However,\n  // since we are manually moving focus this behavior is not happening. This\n  // code will make sure that the text gets selected as-if you did it manually.\n  // Note: We only do this when going forward / backward. Not for the\n  // Focus.First or Focus.Last actions. This is similar to the `autoFocus`\n  // behavior on an input where the input will get focus but won't be\n  // selected.\n  if (focus & (Focus.Next | Focus.Previous) && isSelectableElement(next)) {\n    next.select()\n  }\n\n  return FocusResult.Success\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/form.test.ts",
    "content": "import { objectToFormEntries } from './form'\n\nit.each([\n  [{ a: 'b' }, [['a', 'b']]],\n  [\n    [1, 2, 3],\n    [\n      ['0', '1'],\n      ['1', '2'],\n      ['2', '3'],\n    ],\n  ],\n  [\n    { id: 1, admin: true, name: { first: 'Jane', last: 'Doe', nickname: { preferred: 'JDoe' } } },\n    [\n      ['id', '1'],\n      ['admin', '1'],\n      ['name[first]', 'Jane'],\n      ['name[last]', 'Doe'],\n      ['name[nickname][preferred]', 'JDoe'],\n    ],\n  ],\n])('should encode an input of %j to an form data output', (input, output) => {\n  expect(objectToFormEntries(input)).toEqual(output)\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/form.ts",
    "content": "import { isValidElement } from 'react'\n\ntype Entries = [string, string][]\n\nexport function objectToFormEntries(\n  source: Record<string, any> = {},\n  parentKey: string | null = null,\n  entries: Entries = []\n): Entries {\n  for (let [key, value] of Object.entries(source)) {\n    append(entries, composeKey(parentKey, key), value)\n  }\n\n  return entries\n}\n\nfunction composeKey(parent: string | null, key: string): string {\n  return parent ? parent + '[' + key + ']' : key\n}\n\nfunction append(entries: Entries, key: string, value: any): void {\n  if (Array.isArray(value)) {\n    for (let [subkey, subvalue] of value.entries()) {\n      append(entries, composeKey(key, subkey.toString()), subvalue)\n    }\n  } else if (value instanceof Date) {\n    entries.push([key, value.toISOString()])\n  } else if (typeof value === 'boolean') {\n    entries.push([key, value ? '1' : '0'])\n  } else if (typeof value === 'string') {\n    entries.push([key, value])\n  } else if (typeof value === 'number') {\n    entries.push([key, `${value}`])\n  } else if (value === null || value === undefined) {\n    entries.push([key, ''])\n  } else if (isPlainObject(value) && !isValidElement(value)) {\n    objectToFormEntries(value, key, entries)\n  }\n}\n\nexport function attemptSubmit(elementInForm: HTMLElement) {\n  let form = (elementInForm as any)?.form ?? elementInForm.closest('form')\n  if (!form) return\n\n  for (let element of form.elements) {\n    if (element === elementInForm) continue\n\n    if (\n      (element.tagName === 'INPUT' && element.type === 'submit') ||\n      (element.tagName === 'BUTTON' && element.type === 'submit') ||\n      (element.nodeName === 'INPUT' && element.type === 'image')\n    ) {\n      // If you press `enter` in a normal input[type='text'] field, then the form will submit by\n      // searching for the a submit element and \"click\" it. We could also use the\n      // `form.requestSubmit()` function, but this has a downside where an `event.preventDefault()`\n      // inside a `click` listener on the submit button won't stop the form from submitting.\n      element.click()\n      return\n    }\n  }\n\n  // If we get here, then there is no submit button in the form. We can use the\n  // `form.requestSubmit()` function to submit the form instead. We cannot use `form.submit()`\n  // because then the `submit` event won't be fired and `onSubmit` listeners won't be fired.\n  form.requestSubmit?.()\n}\n\nfunction isPlainObject<T>(value: T): value is T & Record<keyof T, unknown> {\n  if (Object.prototype.toString.call(value) !== '[object Object]') {\n    return false\n  }\n\n  let prototype = Object.getPrototypeOf(value)\n  return prototype === null || Object.getPrototypeOf(prototype) === null\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/get-text-value.test.ts",
    "content": "import { getTextValue } from './get-text-value'\n\nlet html = String.raw\n\nit('should be possible to get the text value from an element', () => {\n  let element = document.createElement('div')\n  element.innerText = 'Hello World'\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should strip out emojis when receiving the text from the element', () => {\n  let element = document.createElement('div')\n  element.innerText = '🇨🇦 Canada'\n  expect(getTextValue(element)).toEqual('Canada')\n})\n\nit('should strip out hidden elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span hidden>Hello</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should strip out aria-hidden elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span aria-hidden>Hello</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should strip out role=\"img\" elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span role=\"img\">°</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should be possible to get the text value from the aria-label', () => {\n  let element = document.createElement('div')\n  element.setAttribute('aria-label', 'Hello World')\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should be possible to get the text value from the aria-label (even if there is content)', () => {\n  let element = document.createElement('div')\n  element.setAttribute('aria-label', 'Hello World')\n  element.innerHTML = 'Hello Universe'\n  element.innerText = 'Hello Universe'\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using `aria-label`)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar\">Contents of foo</div>\n      <div id=\"bar\" aria-label=\"Actual value of bar\">Contents of bar</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Actual value of bar')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using its contents)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar\">Contents of foo</div>\n      <div id=\"bar\">Contents of bar</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Contents of bar')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using `aria-label`, multiple)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar baz\">Contents of foo</div>\n      <div id=\"bar\" aria-label=\"Actual value of bar\">Contents of bar</div>\n      <div id=\"baz\" aria-label=\"Actual value of baz\">Contents of baz</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual(\n    'Actual value of bar, Actual value of baz'\n  )\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using its contents, multiple)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar baz\">Contents of foo</div>\n      <div id=\"bar\">Contents of bar</div>\n      <div id=\"baz\">Contents of baz</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Contents of bar, Contents of baz')\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/get-text-value.ts",
    "content": "import * as DOM from './dom'\n\nlet emojiRegex =\n  /([\\u2700-\\u27BF]|[\\uE000-\\uF8FF]|\\uD83C[\\uDC00-\\uDFFF]|\\uD83D[\\uDC00-\\uDFFF]|[\\u2011-\\u26FF]|\\uD83E[\\uDD10-\\uDDFF])/g\n\nfunction getTextContents(element: HTMLElement): string {\n  // Using innerText instead of textContent because:\n  //\n  // > textContent gets the content of all elements, including <script> and <style> elements. In\n  // > contrast, innerText only shows \"human-readable\" elements.\n  // >\n  // > — https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext\n  let currentInnerText = element.innerText ?? ''\n\n  // Remove all the elements that shouldn't be there.\n  //\n  // [hidden]       — The user doesn't see it\n  // [aria-hidden]  — The screen reader doesn't see it\n  // [role=\"img\"]   — Even if it is text, it is used as an image\n  //\n  // This is probably the slowest part, but if you want complete control over the text value, then\n  // it is better to set an `aria-label` instead.\n  let copy = element.cloneNode(true)\n  if (!DOM.isHTMLElement(copy)) {\n    return currentInnerText\n  }\n\n  let dropped = false\n  // Drop the elements that shouldn't be there.\n  for (let child of copy.querySelectorAll('[hidden],[aria-hidden],[role=\"img\"]')) {\n    child.remove()\n    dropped = true\n  }\n\n  // Now that the elements are removed, we can get the innerText such that we can strip the emojis.\n  let value = dropped ? copy.innerText ?? '' : currentInnerText\n\n  // Check if it contains some emojis or not, if so, we need to remove them\n  // because ideally we work with simple text values.\n  //\n  // Ideally we can use the much simpler RegEx: /\\p{Extended_Pictographic}/u\n  // but we can't rely on this yet, so we use the more complex one.\n  if (emojiRegex.test(value)) {\n    value = value.replace(emojiRegex, '')\n  }\n\n  return value\n}\n\nexport function getTextValue(element: HTMLElement): string {\n  // Try to use the `aria-label` first\n  let label = element.getAttribute('aria-label')\n  if (typeof label === 'string') return label.trim()\n\n  // Try to use the `aria-labelledby` second\n  let labelledby = element.getAttribute('aria-labelledby')\n  if (labelledby) {\n    // aria-labelledby can be a space-separated list of IDs, so we need to split them up and\n    // combine them into a single string.\n    let labels = labelledby\n      .split(' ')\n      .map((labelledby) => {\n        let labelEl = document.getElementById(labelledby)\n        if (labelEl) {\n          let label = labelEl.getAttribute('aria-label')\n          // Try to use the `aria-label` first (of the referenced element)\n          if (typeof label === 'string') return label.trim()\n\n          // This time, the `aria-labelledby` isn't used anymore (in Safari), so we just have to\n          // look at the contents itself.\n          return getTextContents(labelEl).trim()\n        }\n\n        return null\n      })\n      .filter(Boolean)\n\n    if (labels.length > 0) return labels.join(', ')\n  }\n\n  // Try to use the text contents of the element itself\n  return getTextContents(element).trim()\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/match.ts",
    "content": "export function match<TValue extends string | number = string, TReturnValue = unknown>(\n  value: TValue,\n  lookup: Record<TValue, TReturnValue | ((...args: any[]) => TReturnValue)>,\n  ...args: any[]\n): TReturnValue {\n  if (value in lookup) {\n    let returnValue = lookup[value]\n    return typeof returnValue === 'function' ? returnValue(...args) : returnValue\n  }\n\n  let error = new Error(\n    `Tried to handle \"${value}\" but there is no handler defined. Only defined handlers are: ${Object.keys(\n      lookup\n    )\n      .map((key) => `\"${key}\"`)\n      .join(', ')}.`\n  )\n  if (Error.captureStackTrace) Error.captureStackTrace(error, match)\n  throw error\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/micro-task.ts",
    "content": "// Polyfill\nexport function microTask(cb: () => void) {\n  if (typeof queueMicrotask === 'function') {\n    queueMicrotask(cb)\n  } else {\n    Promise.resolve()\n      .then(cb)\n      .catch((e) =>\n        setTimeout(() => {\n          throw e\n        })\n      )\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/once.ts",
    "content": "export function once<T>(cb: (...args: T[]) => void) {\n  let state = { called: false }\n\n  return (...args: T[]) => {\n    if (state.called) return\n    state.called = true\n    return cb(...args)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/owner.ts",
    "content": "import { env } from './env'\n\nexport function getOwnerDocument<T extends Element>(\n  element: T | null | undefined\n): Document | null {\n  if (env.isServer) return null\n  if (element == null) return document\n\n  return element?.ownerDocument ?? document\n}\n\nexport function getRootNode<T extends Element>(\n  element: T | null | undefined\n): Document | ShadowRoot | null {\n  if (env.isServer) return null\n  if (element == null) return document\n\n  // @ts-expect-error `getRootNode`'s return type is typed as `Node`, but we want to type it a bit better\n  return element?.getRootNode?.() ?? document\n}\n\nexport function getActiveElement(element: Element | null | undefined): Element | null {\n  return getRootNode(element)?.activeElement ?? null\n}\n\nexport function isActiveElement(element: Element | null | undefined): boolean {\n  return getActiveElement(element) === element\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/platform.ts",
    "content": "// This file contains functions to detect the platform the app is running on. They aren't perfect,\n// and we are making assumptions here. But it's the best we can do for now.\n\nexport function isIOS() {\n  // TODO: This is not a great way to detect iOS, but it's the best I can do for now.\n  // - `window.platform` is deprecated\n  // - `window.userAgentData.platform` is still experimental (https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/platform)\n  // - `window.userAgent` also doesn't contain the required information\n  return (\n    // Check if it is an iPhone\n    /iPhone/gi.test(window.navigator.platform) ||\n    // Check if it is an iPad. iPad reports itself as \"MacIntel\", but we can check if it is a touch\n    // screen. Let's hope that Apple doesn't release a touch screen Mac (or maybe this would then\n    // work as expected 🤔).\n    (/Mac/gi.test(window.navigator.platform) && window.navigator.maxTouchPoints > 0)\n  )\n}\n\nexport function isAndroid() {\n  return /Android/gi.test(window.navigator.userAgent)\n}\n\nexport function isMobile() {\n  return isIOS() || isAndroid()\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/render.test.tsx",
    "content": "import { getByTestId, prettyDOM, render as testRender } from '@testing-library/react'\nimport React, { Fragment, createRef, type ElementType, type Ref } from 'react'\nimport { suppressConsoleLogs } from '../test-utils/suppress-console-logs'\nimport type { Expand, Props } from '../types'\nimport { RenderFeatures, useRender, type PropsForFeatures } from './render'\n\nfunction contents(id = 'wrapper') {\n  return prettyDOM(getByTestId(document.body, id), undefined, {\n    highlight: false,\n  })\n}\n\ndescribe('Default functionality', () => {\n  let slot = {}\n  function Dummy<TTag extends ElementType = 'div'>(\n    props: Props<TTag> & Partial<{ a: any; b: any; c: any }>\n  ) {\n    let render = useRender()\n    return (\n      <div data-testid=\"wrapper\">\n        {render({\n          ourProps: {},\n          theirProps: props,\n          slot,\n          defaultTag: 'div',\n          name: 'Dummy',\n        })}\n      </div>\n    )\n  }\n\n  function DummyWithClassName<TTag extends ElementType = 'div'>(\n    props: Props<TTag> & Partial<{ className: string | (() => string) }>\n  ) {\n    let render = useRender()\n    return (\n      <div data-testid=\"wrapper-with-class\">\n        {render({\n          ourProps: {},\n          theirProps: props,\n          slot,\n          defaultTag: 'div',\n          name: 'Dummy',\n        })}\n      </div>\n    )\n  }\n\n  it('should be possible to render a dummy component', () => {\n    testRender(<Dummy />)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to merge classes when rendering', () => {\n    testRender(\n      <DummyWithClassName as={Fragment} className=\"test-outer\">\n        <div className=\"test-inner\"></div>\n      </DummyWithClassName>\n    )\n\n    expect(contents('wrapper-with-class')).toMatchSnapshot()\n  })\n\n  it('should be possible to merge class fns when rendering', () => {\n    testRender(\n      <DummyWithClassName as={Fragment} className=\"test-outer\">\n        <Dummy className={() => 'test-inner'}></Dummy>\n      </DummyWithClassName>\n    )\n\n    expect(contents('wrapper-with-class')).toMatchSnapshot()\n  })\n\n  it('should be possible to render a dummy component with some children as a callback', () => {\n    expect.assertions(2)\n\n    testRender(\n      <Dummy>\n        {(data) => {\n          expect(data).toBe(slot)\n\n          return <span>Contents</span>\n        }}\n      </Dummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to add a ref with a different name', () => {\n    let ref = createRef<HTMLDivElement>()\n\n    function MyComponent<T extends ElementType = 'div'>({\n      innerRef,\n      ...props\n    }: Props<T, {}, never, { innerRef?: Ref<HTMLDivElement> }>) {\n      return <div ref={innerRef} {...props} />\n    }\n\n    function OtherDummy<TTag extends ElementType = 'div'>(props: Props<TTag>) {\n      let render = useRender()\n      return (\n        <div data-testid=\"wrapper\">\n          {render({\n            ourProps: { ref },\n            theirProps: props,\n            slot,\n            defaultTag: 'div',\n            name: 'OtherDummy',\n          })}\n        </div>\n      )\n    }\n\n    testRender(\n      <OtherDummy as={MyComponent} refName=\"potato\">\n        Contents\n      </OtherDummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to passthrough props to a dummy component', () => {\n    testRender(<Dummy a={1} b={2} c={3} />)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to change the underlying DOM node using the `as` prop', () => {\n    testRender(<Dummy as=\"button\" />)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to change the underlying DOM node using the `as` prop and still have a function as children', () => {\n    testRender(<Dummy as=\"button\">{() => <span>Contents</span>}</Dummy>)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to render the children only when the `as` prop is set to Fragment', () => {\n    testRender(<Dummy as={Fragment}>Contents</Dummy>)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should forward all the props to the first child when using an as={Fragment}', () => {\n    testRender(\n      <Dummy as={Fragment} a={1} b={1}>\n        {() => <span>Contents</span>}\n      </Dummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should forward boolean values from `slot` as data attributes', () => {\n    function Dummy<TTag extends ElementType = 'div'>(\n      props: Props<TTag> & Partial<{ a: any; b: any; c: any }>\n    ) {\n      let render = useRender()\n      return (\n        <div data-testid=\"wrapper\">\n          {render({\n            ourProps: {},\n            theirProps: props,\n            slot: { a: true, b: false, c: true },\n            defaultTag: 'div',\n            name: 'Dummy',\n          })}\n        </div>\n      )\n    }\n\n    testRender(<Dummy>{() => <span>Contents</span>}</Dummy>)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should prefer user provided data attributes over the ones we set automatically', () => {\n    function Dummy<TTag extends ElementType = 'div'>(\n      props: Props<TTag> & Partial<{ a: any; b: any; c: any }>\n    ) {\n      let render = useRender()\n      return (\n        <div data-testid=\"wrapper\">\n          {render({\n            ourProps: {},\n            theirProps: props,\n            slot: { accept: true },\n            defaultTag: 'div',\n            name: 'Dummy',\n          })}\n        </div>\n      )\n    }\n\n    testRender(<Dummy as={Fragment}>{() => <span data-accept=\"always\">Contents</span>}</Dummy>)\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it(\n    'should error when we are rendering a Fragment with multiple children',\n    suppressConsoleLogs(() => {\n      expect.assertions(1)\n\n      return expect(() => {\n        testRender(\n          // @ts-expect-error className cannot be applied to a Fragment\n          <Dummy as={Fragment} className=\"p-12\">\n            <span>Contents A</span>\n            <span>Contents B</span>\n          </Dummy>\n        )\n      }).toThrow(\n        new Error(\n          [\n            'Passing props on \"Fragment\"!',\n            '',\n            'The current component <Dummy /> is rendering a \"Fragment\".',\n            'However we need to passthrough the following props:',\n            '  - className',\n            '',\n            'You can apply a few solutions:',\n            '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"Fragment\".',\n            '  - Render a single element as the child so that we can forward the props onto that element.',\n          ].join('\\n')\n        )\n      )\n    })\n  )\n\n  it(\"should not error when we are rendering a Fragment with multiple children when we don't passthrough additional props\", () => {\n    testRender(\n      <Dummy as={Fragment}>\n        <span>Contents A</span>\n        <span>Contents B</span>\n      </Dummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it(\n    'should error when we are applying props to a Fragment when we do not have a dedicated element',\n    suppressConsoleLogs(() => {\n      expect.assertions(1)\n\n      return expect(() => {\n        testRender(\n          // @ts-expect-error className cannot be applied to a Fragment\n          <Dummy as={Fragment} className=\"p-12\">\n            Contents\n          </Dummy>\n        )\n      }).toThrow(\n        new Error(\n          [\n            'Passing props on \"Fragment\"!',\n            '',\n            'The current component <Dummy /> is rendering a \"Fragment\".',\n            'However we need to passthrough the following props:',\n            '  - className',\n            '',\n            'You can apply a few solutions:',\n            '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"Fragment\".',\n            '  - Render a single element as the child so that we can forward the props onto that element.',\n          ].join('\\n')\n        )\n      )\n    })\n  )\n})\n\n// ---\n\nfunction testStaticFeature(Dummy: (props: any) => React.JSX.Element) {\n  it('should be possible to render a `static` dummy component (show = true)', () => {\n    testRender(\n      <Dummy show={true} static>\n        Contents\n      </Dummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n\n  it('should be possible to render a `static` dummy component (show = false)', () => {\n    testRender(\n      <Dummy show={false} static>\n        Contents\n      </Dummy>\n    )\n\n    expect(contents()).toMatchSnapshot()\n  })\n}\n\n// With the `static` keyword, the user is always in control. When we internally decide to show the\n// component or hide it then it won't have any effect. This is useful for when you want to wrap your\n// component in a Transition for example so that the Transition component can control the\n// showing/hiding based on the `show` prop AND the state of the transition.\ndescribe('Features.Static', () => {\n  let slot = {}\n  let EnabledFeatures = RenderFeatures.Static\n  function Dummy<TTag extends ElementType = 'div'>(\n    props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }\n  ) {\n    let { show, ...rest } = props\n    let render = useRender()\n    return (\n      <div data-testid=\"wrapper\">\n        {render({\n          ourProps: {},\n          theirProps: rest,\n          slot,\n          defaultTag: 'div',\n          features: EnabledFeatures,\n          visible: show,\n          name: 'Dummy',\n        })}\n      </div>\n    )\n  }\n\n  testStaticFeature(Dummy)\n})\n\n// ---\n\nfunction testRenderStrategyFeature(Dummy: (props: any) => React.JSX.Element) {\n  describe('Unmount render strategy', () => {\n    it('should be possible to render an `unmount` dummy component (show = true)', () => {\n      testRender(\n        <Dummy show={true} unmount>\n          Contents\n        </Dummy>\n      )\n\n      expect(contents()).toMatchSnapshot()\n    })\n\n    it('should be possible to render an `unmount` dummy component (show = false)', () => {\n      testRender(\n        <Dummy show={false} unmount>\n          Contents\n        </Dummy>\n      )\n\n      // No contents, because we unmounted!\n      expect(contents()).toMatchSnapshot()\n    })\n  })\n\n  describe('Hidden render strategy', () => {\n    it('should be possible to render an `unmount={false}` dummy component (show = true)', () => {\n      testRender(\n        <Dummy show={true} unmount={false}>\n          Contents\n        </Dummy>\n      )\n\n      expect(contents()).toMatchSnapshot()\n    })\n\n    it('should be possible to render an `unmount={false}` dummy component (show = false)', () => {\n      testRender(\n        <Dummy show={false} unmount={false}>\n          Contents\n        </Dummy>\n      )\n\n      // We do have contents, but it is marked as hidden!\n      expect(contents()).toMatchSnapshot()\n    })\n  })\n}\n\ndescribe('Features.RenderStrategy', () => {\n  let slot = {}\n  let EnabledFeatures = RenderFeatures.RenderStrategy\n  function Dummy<TTag extends ElementType = 'div'>(\n    props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }\n  ) {\n    let { show, ...rest } = props\n    let render = useRender()\n    return (\n      <div data-testid=\"wrapper\">\n        {render({\n          ourProps: {},\n          theirProps: rest,\n          slot,\n          defaultTag: 'div',\n          features: EnabledFeatures,\n          visible: show,\n          name: 'Dummy',\n        })}\n      </div>\n    )\n  }\n\n  testRenderStrategyFeature(Dummy)\n})\n\n// ---\n\n// This should enable the `static` and `unmount` features. However they can't be used together!\ndescribe('Features.Static | Features.RenderStrategy', () => {\n  let slot = {}\n  let EnabledFeatures = RenderFeatures.Static | RenderFeatures.RenderStrategy\n  function Dummy<TTag extends ElementType = 'div'>(\n    props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }\n  ) {\n    let { show, ...rest } = props\n    let render = useRender()\n    return (\n      <div data-testid=\"wrapper\">\n        {render({\n          ourProps: {},\n          theirProps: rest,\n          slot,\n          defaultTag: 'div',\n          features: EnabledFeatures,\n          visible: show,\n          name: 'Dummy',\n        })}\n      </div>\n    )\n  }\n\n  // To avoid duplication, and to make sure that the features tested in isolation can also be\n  // re-used when they are combined.\n  testStaticFeature(Dummy)\n  testRenderStrategyFeature(Dummy)\n})\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/render.ts",
    "content": "import React, {\n  Fragment,\n  cloneElement,\n  createElement,\n  forwardRef,\n  isValidElement,\n  useCallback,\n  useRef,\n  type ElementType,\n  type MutableRefObject,\n  type ReactElement,\n  type Ref,\n} from 'react'\nimport type { Expand, Props } from '../types'\nimport { classNames } from './class-names'\nimport { match } from './match'\n\nexport enum RenderFeatures {\n  /** No features at all */\n  None = 0,\n\n  /**\n   * When used, this will allow us to use one of the render strategies.\n   *\n   * **The render strategies are:**\n   *    - **Unmount**   _(Will unmount the component.)_\n   *    - **Hidden**    _(Will hide the component using the [hidden] attribute.)_\n   */\n  RenderStrategy = 1,\n\n  /**\n   * When used, this will allow the user of our component to be in control. This can be used when\n   * you want to transition based on some state.\n   */\n  Static = 2,\n}\n\nexport enum RenderStrategy {\n  Unmount,\n  Hidden,\n}\n\ntype UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any\n  ? R\n  : never\n\ntype PropsForFeature<\n  TPassedInFeatures extends RenderFeatures,\n  TForFeature extends RenderFeatures,\n  TProps,\n> = TPassedInFeatures extends TForFeature ? TProps : {}\n\nexport type PropsForFeatures<T extends RenderFeatures> = Expand<\n  UnionToIntersection<\n    | PropsForFeature<T, RenderFeatures.Static, { static?: boolean }>\n    | PropsForFeature<T, RenderFeatures.RenderStrategy, { unmount?: boolean }>\n  >\n>\n\nexport function useRender() {\n  let mergeRefs = useMergeRefsFn()\n\n  return useCallback(\n    (args: Parameters<typeof render>[0]) => render({ mergeRefs, ...args }),\n    [mergeRefs]\n  ) as typeof render\n}\n\nfunction render<TFeature extends RenderFeatures, TTag extends ElementType, TSlot>({\n  ourProps,\n  theirProps,\n  slot,\n  defaultTag,\n  features,\n  visible = true,\n  name,\n  mergeRefs,\n}: {\n  ourProps: Expand<Props<TTag, TSlot, any> & PropsForFeatures<TFeature>> & {\n    ref?: Ref<HTMLElement | ElementType>\n  }\n  theirProps: Expand<Props<TTag, TSlot, any>>\n  slot?: TSlot\n  defaultTag: ElementType\n  features?: TFeature\n  visible?: boolean\n  name: string\n  mergeRefs?: ReturnType<typeof useMergeRefsFn>\n}): ReturnType<typeof _render> | null {\n  mergeRefs = mergeRefs ?? defaultMergeRefs\n\n  let props = mergePropsAdvanced(theirProps, ourProps)\n\n  // Visible always render\n  if (visible) return _render(props, slot, defaultTag, name, mergeRefs)\n\n  let featureFlags = features ?? RenderFeatures.None\n\n  if (featureFlags & RenderFeatures.Static) {\n    let { static: isStatic = false, ...rest } = props as PropsForFeatures<RenderFeatures.Static>\n\n    // When the `static` prop is passed as `true`, then the user is in control, thus we don't care about anything else\n    if (isStatic) return _render(rest, slot, defaultTag, name, mergeRefs)\n  }\n\n  if (featureFlags & RenderFeatures.RenderStrategy) {\n    let { unmount = true, ...rest } = props as PropsForFeatures<RenderFeatures.RenderStrategy>\n    let strategy = unmount ? RenderStrategy.Unmount : RenderStrategy.Hidden\n\n    return match(strategy, {\n      [RenderStrategy.Unmount]() {\n        return null\n      },\n      [RenderStrategy.Hidden]() {\n        return _render(\n          { ...rest, ...{ hidden: true, style: { display: 'none' } } },\n          slot,\n          defaultTag,\n          name,\n          mergeRefs!\n        )\n      },\n    })\n  }\n\n  // No features enabled, just render\n  return _render(props, slot, defaultTag, name, mergeRefs)\n}\n\nfunction _render<TTag extends ElementType, TSlot>(\n  props: Props<TTag, TSlot> & { ref?: unknown },\n  slot: TSlot = {} as TSlot,\n  tag: ElementType,\n  name: string,\n  mergeRefs: ReturnType<typeof useMergeRefsFn>\n) {\n  let {\n    as: Component = tag,\n    children,\n    refName = 'ref',\n    ...rest\n  } = omit(props, ['unmount', 'static'])\n\n  // This allows us to use `<HeadlessUIComponent as={MyComponent} refName=\"innerRef\" />`\n  let refRelatedProps = props.ref !== undefined ? { [refName]: props.ref } : {}\n\n  let resolvedChildren = (typeof children === 'function' ? children(slot) : children) as\n    | ReactElement\n    | ReactElement[]\n\n  // Allow for className to be a function with the slot as the contents\n  if ('className' in rest && rest.className && typeof rest.className === 'function') {\n    rest.className = rest.className(slot)\n  }\n\n  // Drop `aria-labelledby` if it only references the current element. If the `aria-labelledby`\n  // references itself but also another element then we can keep it.\n  if (rest['aria-labelledby'] && rest['aria-labelledby'] === rest.id) {\n    rest['aria-labelledby'] = undefined\n  }\n\n  let dataAttributes: Record<string, string> = {}\n  if (slot) {\n    let exposeState = false\n    let states = []\n    for (let [k, v] of Object.entries(slot)) {\n      if (typeof v === 'boolean') {\n        exposeState = true\n      }\n\n      if (v === true) {\n        states.push(k.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`))\n      }\n    }\n\n    if (exposeState) {\n      dataAttributes['data-headlessui-state'] = states.join(' ')\n      for (let state of states) {\n        dataAttributes[`data-${state}`] = ''\n      }\n    }\n  }\n\n  if (isFragment(Component)) {\n    if (Object.keys(compact(rest)).length > 0 || Object.keys(compact(dataAttributes)).length > 0) {\n      if (\n        !isValidElement(resolvedChildren) ||\n        (Array.isArray(resolvedChildren) && resolvedChildren.length > 1) ||\n        isFragmentInstance(resolvedChildren)\n      ) {\n        if (Object.keys(compact(rest)).length > 0) {\n          throw new Error(\n            [\n              'Passing props on \"Fragment\"!',\n              '',\n              `The current component <${name} /> is rendering a \"Fragment\".`,\n              `However we need to passthrough the following props:`,\n              Object.keys(compact(rest))\n                .concat(Object.keys(compact(dataAttributes)))\n                .map((line) => `  - ${line}`)\n                .join('\\n'),\n              '',\n              'You can apply a few solutions:',\n              [\n                'Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"Fragment\".',\n                'Render a single element as the child so that we can forward the props onto that element.',\n              ]\n                .map((line) => `  - ${line}`)\n                .join('\\n'),\n            ].join('\\n')\n          )\n        }\n      } else {\n        // Merge class name prop in SSR\n        // @ts-ignore We know that the props may not have className. It'll be undefined then which is fine.\n        let childProps = resolvedChildren.props as { className: string | (() => string) } | null\n\n        let childPropsClassName = childProps?.className\n        let newClassName =\n          typeof childPropsClassName === 'function'\n            ? (...args: any[]) =>\n                classNames(\n                  (childPropsClassName as Function)(...args),\n                  (rest as { className?: string }).className\n                )\n            : classNames(childPropsClassName, (rest as { className?: string }).className)\n\n        let classNameProps = newClassName ? { className: newClassName } : {}\n\n        // Merge props from the existing element with the incoming props\n        let mergedProps = mergePropsAdvanced(\n          resolvedChildren.props as any,\n          // Filter out undefined values so that they don't override the existing values\n          compact(omit(rest, ['ref']))\n        )\n\n        // Make sure that `data-*` that already exist in the `mergedProps` are\n        // skipped.\n        //\n        // Typically we want to keep the props we set in each component because\n        // they are required to make the component work correctly. However, in\n        // case of `data-*` attributes, these are attributes that help the end\n        // user.\n        //\n        // This means that since the props are not required for the component to\n        // work, that we can safely prefer the `data-*` attributes from the\n        // component that the end user provided.\n        for (let key in dataAttributes) {\n          if (key in mergedProps) {\n            delete dataAttributes[key]\n          }\n        }\n\n        return cloneElement(\n          resolvedChildren,\n          Object.assign(\n            {},\n            mergedProps,\n            dataAttributes,\n            refRelatedProps,\n            { ref: mergeRefs(getElementRef(resolvedChildren), refRelatedProps.ref) },\n            classNameProps\n          )\n        )\n      }\n    }\n  }\n\n  return createElement(\n    Component,\n    Object.assign(\n      {},\n      omit(rest, ['ref']),\n      !isFragment(Component) && refRelatedProps,\n      !isFragment(Component) && dataAttributes\n    ),\n    resolvedChildren\n  )\n}\n\n/**\n * This is a singleton hook. **You can ONLY call the returned\n * function *once* to produce expected results.** If you need\n * to call `mergeRefs()` multiple times you need to create a\n * separate function for each invocation. This happens as we\n * store the list of `refs` to update and always return the\n * same function that refers to that list of refs.\n *\n * You shouldn't normally read refs during render but this\n * should actually be okay because React itself is calling\n * the `function` that updates these refs and can only do\n * so once the ref that contains the list is updated.\n */\nfunction useMergeRefsFn() {\n  type MaybeRef<T> = MutableRefObject<T> | ((value: T) => void) | null | undefined\n  let currentRefs = useRef<MaybeRef<any>[]>([])\n  let mergedRef = useCallback((value: any) => {\n    for (let ref of currentRefs.current) {\n      if (ref == null) continue\n      if (typeof ref === 'function') ref(value)\n      else ref.current = value\n    }\n  }, [])\n\n  return (...refs: any[]) => {\n    if (refs.every((ref) => ref == null)) {\n      return undefined\n    }\n\n    currentRefs.current = refs\n    return mergedRef\n  }\n}\n\n// This does not produce a stable function to use as a ref\n// But we only use it in the case of as={Fragment}\n// And it should really only re-render if setting the ref causes the parent to re-render unconditionally\n// which then causes the child to re-render resulting in a render loop\n// TODO: Add tests for this somehow\nfunction defaultMergeRefs(...refs: any[]) {\n  return refs.every((ref) => ref == null)\n    ? undefined\n    : (value: any) => {\n        for (let ref of refs) {\n          if (ref == null) continue\n          if (typeof ref === 'function') ref(value)\n          else ref.current = value\n        }\n      }\n}\n\n// A more complex example fo the `mergeProps` function, this one also cancels subsequent event\n// listeners if the event has already been `preventDefault`ed.\nfunction mergePropsAdvanced(...listOfProps: Props<any, any>[]) {\n  if (listOfProps.length === 0) return {}\n  if (listOfProps.length === 1) return listOfProps[0]\n\n  let target: Props<any, any> = {}\n\n  let eventHandlers: Record<\n    string,\n    ((event: { defaultPrevented: boolean }, ...args: any[]) => void | undefined)[]\n  > = {}\n\n  for (let props of listOfProps) {\n    for (let prop in props) {\n      // Collect event handlers\n      if (prop.startsWith('on') && typeof props[prop] === 'function') {\n        eventHandlers[prop] ??= []\n        eventHandlers[prop].push(props[prop])\n      } else {\n        // Override incoming prop\n        target[prop] = props[prop]\n      }\n    }\n  }\n\n  // Ensure event listeners are not called if `disabled` or `aria-disabled` is true\n  if (target.disabled || target['aria-disabled']) {\n    for (let eventName in eventHandlers) {\n      // Prevent default events for `onClick`, `onMouseDown`, `onKeyDown`, etc.\n      if (/^(on(?:Click|Pointer|Mouse|Key)(?:Down|Up|Press)?)$/.test(eventName)) {\n        eventHandlers[eventName] = [(e: any) => e?.preventDefault?.()]\n      }\n    }\n  }\n\n  // Merge event handlers\n  for (let eventName in eventHandlers) {\n    Object.assign(target, {\n      [eventName](event: { nativeEvent?: Event; defaultPrevented: boolean }, ...args: any[]) {\n        let handlers = eventHandlers[eventName]\n\n        for (let handler of handlers) {\n          if (\n            (event instanceof Event || event?.nativeEvent instanceof Event) &&\n            event.defaultPrevented\n          ) {\n            return\n          }\n\n          handler(event, ...args)\n        }\n      },\n    })\n  }\n\n  return target\n}\n\nexport type HasDisplayName = {\n  displayName: string\n}\n\nexport type RefProp<T extends Function> = T extends (props: any, ref: Ref<infer RefType>) => any\n  ? { ref?: Ref<RefType> }\n  : never\n\n// TODO: add proper return type, but this is not exposed as public API so it's fine for now\nexport function mergeProps<T extends Props<any, any>[]>(...listOfProps: T) {\n  if (listOfProps.length === 0) return {}\n  if (listOfProps.length === 1) return listOfProps[0]\n\n  let target: Props<any, any> = {}\n\n  let eventHandlers: Record<string, ((...args: any[]) => void | undefined)[]> = {}\n\n  for (let props of listOfProps) {\n    for (let prop in props) {\n      // Merge event listeners\n      if (prop.startsWith('on') && typeof props[prop] === 'function') {\n        eventHandlers[prop] ??= []\n        eventHandlers[prop].push(props[prop])\n      } else {\n        // Override incoming prop\n        target[prop] = props[prop]\n      }\n    }\n  }\n\n  // Merge event handlers\n  for (let eventName in eventHandlers) {\n    Object.assign(target, {\n      [eventName](...args: any[]) {\n        let handlers = eventHandlers[eventName]\n\n        for (let handler of handlers) {\n          handler?.(...args)\n        }\n      },\n    })\n  }\n\n  return target\n}\n\n/**\n * This is a hack, but basically we want to keep the full 'API' of the component, but we do want to\n * wrap it in a forwardRef so that we _can_ passthrough the ref\n */\nexport function forwardRefWithAs<T extends { name: string; displayName?: string }>(\n  component: T\n): T & { displayName: string } {\n  return Object.assign(forwardRef(component as any) as any, {\n    displayName: component.displayName ?? component.name,\n  })\n}\n\nexport function compact<T extends Record<any, any>>(object: T) {\n  let clone = Object.assign({}, object)\n  for (let key in clone) {\n    if (clone[key] === undefined) delete clone[key]\n  }\n  return clone\n}\n\nfunction omit<T extends Record<any, any>>(object: T, keysToOmit: string[] = []) {\n  let clone = Object.assign({}, object) as T\n  for (let key of keysToOmit) {\n    if (key in clone) delete clone[key]\n  }\n  return clone\n}\n\nfunction getElementRef(element: React.ReactElement) {\n  // @ts-expect-error\n  return React.version.split('.')[0] >= '19' ? element.props.ref : element.ref\n}\n\nexport function isFragment(element: any): element is typeof Fragment {\n  return element === Fragment || element === Symbol.for('react.fragment')\n}\n\nexport function isFragmentInstance(element: React.ReactElement): boolean {\n  return isFragment(element.type)\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/stable-collection.tsx",
    "content": "import * as React from 'react'\n\ntype CollectionKey = string | symbol\ntype CollectionItem = [number, () => void]\ntype CollectionRef = React.MutableRefObject<ReturnType<typeof createCollection>>\nconst StableCollectionContext = React.createContext<CollectionRef | null>(null)\n\nfunction createCollection() {\n  return {\n    /** @type {Map<string, Map<string, number>>} */\n    groups: new Map(),\n\n    get(group: string, key: CollectionKey): CollectionItem {\n      let list = this.groups.get(group)\n      if (!list) {\n        list = new Map()\n        this.groups.set(group, list)\n      }\n\n      let renders = list.get(key) ?? 0\n      // FIXME: This is a side-effect during render. `release` is only called in\n      // an effect cleanup so we may never release if we had to render multiple\n      // times before commit e.g. when a sibling suspends.\n      list.set(key, renders + 1)\n\n      let index = Array.from(list.keys()).indexOf(key)\n      function release() {\n        let renders = list.get(key)\n        if (renders > 1) {\n          list.set(key, renders - 1)\n        } else {\n          list.delete(key)\n        }\n      }\n\n      return [index, release]\n    },\n  }\n}\n\nexport function StableCollection({ children }: { children: React.ReactNode | React.ReactNode[] }) {\n  let collection = React.useRef(createCollection())\n\n  return (\n    <StableCollectionContext.Provider value={collection}>\n      {children}\n    </StableCollectionContext.Provider>\n  )\n}\n\nexport function useStableCollectionIndex(group: string) {\n  let collection = React.useContext(StableCollectionContext)\n  if (!collection) throw new Error('You must wrap your component in a <StableCollection>')\n\n  let key = React.useId()\n  let [idx, cleanupIdx] = collection.current.get(group, key)\n  React.useEffect(() => cleanupIdx, [])\n  return idx\n}\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/start-transition.ts",
    "content": "import React from 'react'\n\nexport let startTransition =\n  // Prefer React's `startTransition` if it's available.\n  React.startTransition ??\n  function startTransition(cb: () => void) {\n    cb()\n  }\n"
  },
  {
    "path": "packages/@headlessui-react/src/utils/store.ts",
    "content": "type ChangeFn = () => void\ntype UnsubscribeFn = () => void\ntype ActionFn<T> = (this: T, ...args: any[]) => T | void\ntype StoreActions<Key extends string, T> = Record<Key, ActionFn<T>>\n\nexport interface Store<T, ActionKey extends string> {\n  getSnapshot(): T\n  subscribe(onChange: ChangeFn): UnsubscribeFn\n  dispatch(action: ActionKey, ...args: any[]): void\n}\n\nexport function createStore<T, ActionKey extends string>(\n  initial: () => T,\n  actions: StoreActions<ActionKey, T>\n): Store<T, ActionKey> {\n  let state: T = initial()\n\n  let listeners = new Set<ChangeFn>()\n\n  return {\n    getSnapshot() {\n      return state\n    },\n\n    subscribe(onChange) {\n      listeners.add(onChange)\n\n      return () => listeners.delete(onChange)\n    },\n\n    dispatch(key: ActionKey, ...args: any[]) {\n      let newState = actions[key].call(state, ...args)\n      if (newState) {\n        state = newState\n        listeners.forEach((listener) => listener())\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-react/tsconfig.json",
    "content": "{\n  \"include\": [\"src\", \"types\"],\n  \"compilerOptions\": {\n    \"module\": \"esnext\",\n    \"lib\": [\"dom\", \"esnext\", \"dom.iterable\"],\n    \"importHelpers\": true,\n    \"declaration\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"downlevelIteration\": true,\n    \"moduleResolution\": \"node\",\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"target\": \"ESNext\",\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true\n  },\n  \"exclude\": [\"node_modules\", \"**/*.test.tsx?\"]\n}\n"
  },
  {
    "path": "packages/@headlessui-react/types/jest.d.ts",
    "content": "export {}\n\ndeclare global {\n  namespace jest {\n    interface Matchers<R> {\n      toBeWithinRenderFrame(actual: number): R\n    }\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n- Nothing yet!\n\n## [0.2.2] - 2025-02-06\n\n- Support installing with Tailwind CSS v4 ([#3634](https://github.com/tailwindlabs/headlessui/pull/3634))\n\n## [0.2.1] - 2024-05-29\n\n### Fixed\n\n- Improve CJS types ([#2880](https://github.com/tailwindlabs/headlessui/pull/2880))\n- Improve ESM types ([#3051](https://github.com/tailwindlabs/headlessui/pull/3051))\n\n## [0.2.0] - 2023-07-27\n\n### Added\n\n- Add `ui-focus-visible` variant to Tailwind plugin ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347))\n- Add `ui-not-focus-visible` variant to Tailwind plugin ([#2618](https://github.com/tailwindlabs/headlessui/pull/2618))\n\n## [0.1.3] - 2023-04-12\n\n### Fixed\n\n- Add ESM and `types` paths to `package.json` for Tailwind CSS plugin ([#2382](https://github.com/tailwindlabs/headlessui/pull/2382))\n\n## [0.1.2] - 2022-12-06\n\n### Fixed\n\n- Move `tailwindcss` to `devDependencies` ([#2040](https://github.com/tailwindlabs/headlessui/pull/2040))\n\n## [0.1.1] - 2022-09-06\n\n### Fixed\n\n- Fix bracket order of `not` variants ([#1621](https://github.com/tailwindlabs/headlessui/pull/1621))\n\n## [0.1.0] - 2022-05-24\n\n### Added\n\n- Add `@headlessui/tailwindcss` plugin ([#1487](https://github.com/tailwindlabs/headlessui/pull/1487))\n\n[unreleased]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.2.2...HEAD\n[0.2.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.2.1...@headlessui/tailwindcss@v0.2.2\n[0.2.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.2.0...@headlessui/tailwindcss@v0.2.1\n[0.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.1.3...@headlessui/tailwindcss@v0.2.0\n[0.1.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.1.2...@headlessui/tailwindcss@v0.1.3\n[0.1.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.1.1...@headlessui/tailwindcss@v0.1.2\n[0.1.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/tailwindcss@v0.1.0...@headlessui/tailwindcss@v0.1.1\n[0.1.0]: https://github.com/tailwindlabs/headlessui/releases/tag/@headlessui/tailwindcss@v0.1.0\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Tailwind Labs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "packages/@headlessui-tailwindcss/README.md",
    "content": "<h3 align=\"center\">\n  @headlessui/tailwindcss\n</h3>\n\n<p align=\"center\">\n  A complementary Tailwind CSS plugin for Headless UI\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@headlessui/tailwindcss\"><img src=\"https://img.shields.io/npm/dt/@headlessui/tailwindcss.svg\" alt=\"Total Downloads\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/releases\"><img src=\"https://img.shields.io/npm/v/@headlessui/tailwindcss.svg\" alt=\"Latest Release\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/blob/main/LICENSE\"><img src=\"https://img.shields.io/npm/l/@headlessui/tailwindcss.svg\" alt=\"License\"></a>\n</p>\n\n## Installation\n\n```sh\nnpm install @headlessui/tailwindcss\n```\n\n```js\n// tailwind.config.js\nmodule.exports = {\n  content: [],\n  theme: {\n    extend: {},\n  },\n  plugins: [\n    require('@headlessui/tailwindcss')\n\n    // Or with a custom prefix:\n    require('@headlessui/tailwindcss')({ prefix: 'ui' })\n  ],\n}\n```\n\n## Documentation\n\nUse Tailwind CSS utilities for styling the components based on their state. You can use the\nfollowing variants:\n\n| Variant            | Inverse variant        |\n| ------------------ | ---------------------- |\n| `ui-open`          | `ui-not-open`          |\n| `ui-checked`       | `ui-not-checked`       |\n| `ui-selected`      | `ui-not-selected`      |\n| `ui-active`        | `ui-not-active`        |\n| `ui-disabled`      | `ui-not-disabled`      |\n| `ui-focus-visible` | `ui-not-focus-visible` |\n\nExample:\n\n```js\nimport { Menu } from '@headlessui/react'\n\nfunction MyDropdown() {\n  return (\n    <Menu>\n      <Menu.Button>More</Menu.Button>\n      <Menu.Items>\n        <Menu.Item>\n          <a\n            className=\"ui-active:bg-blue-500 ui-active:text-white ui-not-active:bg-white ui-not-active:text-black\"\n            href=\"/account-settings\"\n          >\n            Account settings\n          </a>\n        </Menu.Item>\n        {/* ... */}\n      </Menu.Items>\n    </Menu>\n  )\n}\n```\n\n## Community\n\nFor help, discussion about best practices, or feature ideas:\n\n[Discuss Headless UI on GitHub](https://github.com/tailwindlabs/headlessui/discussions)\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/build/index.cjs",
    "content": "'use strict'\n\nlet plugin =\n  process.env.NODE_ENV === 'production'\n    ? require('./headlessui.prod.cjs')\n    : require('./headlessui.dev.cjs')\n\nmodule.exports = (plugin.__esModule ? plugin : { default: plugin }).default\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/jest.config.cjs",
    "content": "let create = require('../../jest/create-jest-config.cjs')\nmodule.exports = create(__dirname, {\n  displayName: ' CSS ',\n})\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/package.json",
    "content": "{\n  \"name\": \"@headlessui/tailwindcss\",\n  \"version\": \"0.2.2\",\n  \"description\": \"A complementary Tailwind CSS plugin\",\n  \"main\": \"dist/index.cjs\",\n  \"types\": \"dist/index.d.ts\",\n  \"module\": \"dist/headlessui.esm.js\",\n  \"type\": \"module\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"README.md\",\n    \"dist\"\n  ],\n  \"exports\": {\n    \"types\": {\n      \"import\": \"./dist/index.d.ts\",\n      \"require\": \"./dist/index.d.cts\"\n    },\n    \"import\": \"./dist/headlessui.esm.js\",\n    \"require\": \"./dist/index.cjs\"\n  },\n  \"sideEffects\": false,\n  \"engines\": {\n    \"node\": \">=10\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/tailwindlabs/headlessui.git\",\n    \"directory\": \"packages/@headlessui-tailwindcss\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"prepublishOnly\": \"npm run build\",\n    \"build\": \"../../scripts/build.sh --external:tailwindcss && node ./scripts/fix-types.cjs\",\n    \"watch\": \"../../scripts/watch.sh --external:tailwindcss\",\n    \"test\": \"../../scripts/test.sh\",\n    \"lint\": \"../../scripts/lint.sh\",\n    \"lint-types\": \"npm run attw -P --workspaces --if-present\",\n    \"clean\": \"rimraf ./dist\"\n  },\n  \"peerDependencies\": {\n    \"tailwindcss\": \"^3.0 || ^4.0\"\n  },\n  \"devDependencies\": {\n    \"tailwindcss\": \"^3.2.7\"\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/scripts/fix-types.cjs",
    "content": "let fs = require('fs/promises')\nlet path = require('path')\n\n/**\n * Everything in the Headless UI codebase is written in TypeScript, however the\n * `@headlessui/tailwindcss` plugin has to compile to a CommonJs file. This works great, however\n * the types that were generated by tsc use `export default _default` instead of `export =\n * _default` even if we use `module: CommonJs` in the `tsconfig.json`\n *\n * Don't want to spend too much time on this problem, so doing this little hack to change the\n * exported type. This allows us to use the `@headlessui/tailwindcss` plugin and have types in a\n * CommonJs environment.\n **/\n\nlet esmTypes = path.resolve(__dirname, '..', 'dist', 'index.d.ts')\nlet cjsTypes = path.resolve(__dirname, '..', 'dist', 'index.d.cts')\n\nasync function run() {\n  let contents = await fs.readFile(esmTypes, 'utf8')\n  contents = contents.replace('export default', 'export =')\n  await fs.writeFile(cjsTypes, contents, 'utf8')\n}\n\nrun()\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/src/__snapshots__/index.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`custom prefix should generate css for an exposed state 1`] = `\n\".hui-open\\\\\\\\:underline[data-headlessui-state~=\\\\\"open\\\\\"] {\n    text-decoration-line: underline\n}\n:where([data-headlessui-state~=\\\\\"open\\\\\"]) .hui-open\\\\\\\\:underline {\n    text-decoration-line: underline\n}\"\n`;\n\nexports[`custom prefix should generate the inverse \"not\" css for an exposed state 1`] = `\n\".hui-not-open\\\\\\\\:underline[data-headlessui-state]:not([data-headlessui-state~=\\\\\"open\\\\\"]) {\n    text-decoration-line: underline\n}\n:where([data-headlessui-state]:not([data-headlessui-state~=\\\\\"open\\\\\"])) .hui-not-open\\\\\\\\:underline:not([data-headlessui-state]) {\n    text-decoration-line: underline\n}\"\n`;\n\nexports[`should generate css for an exposed state 1`] = `\n\".ui-open\\\\\\\\:underline[data-headlessui-state~=\\\\\"open\\\\\"] {\n    text-decoration-line: underline\n}\n:where([data-headlessui-state~=\\\\\"open\\\\\"]) .ui-open\\\\\\\\:underline {\n    text-decoration-line: underline\n}\"\n`;\n\nexports[`should generate the inverse \"not\" css for an exposed state 1`] = `\n\".ui-not-open\\\\\\\\:underline[data-headlessui-state]:not([data-headlessui-state~=\\\\\"open\\\\\"]) {\n    text-decoration-line: underline\n}\n:where([data-headlessui-state]:not([data-headlessui-state~=\\\\\"open\\\\\"])) .ui-not-open\\\\\\\\:underline:not([data-headlessui-state]) {\n    text-decoration-line: underline\n}\"\n`;\n\nexports[`should generate the ui-focus-visible variant 1`] = `\n\":where([data-headlessui-focus-visible]) .ui-focus-visible\\\\\\\\:underline:focus {\n    text-decoration-line: underline\n}\"\n`;\n\nexports[`should generate the ui-not-focus-visible variant 1`] = `\n\".ui-not-focus-visible\\\\\\\\:underline:focus:where(:not([data-headlessui-focus-visible] .ui-not-focus-visible\\\\\\\\:underline)) {\n    text-decoration-line: underline\n}\"\n`;\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/src/index.test.ts",
    "content": "import path from 'path'\nimport postcss from 'postcss'\nimport tailwind from 'tailwindcss'\nimport hui from './index'\n\nlet html = String.raw\n\nfunction run(input: string, config: any, plugin = tailwind) {\n  let { currentTestName } = expect.getState()\n\n  // @ts-ignore\n  return postcss(plugin(config)).process(input, {\n    from: `${path.resolve(__filename)}?test=${currentTestName}`,\n  })\n}\n\nit('should generate css for an exposed state', async () => {\n  let config = {\n    content: [{ raw: html`<div class=\"ui-open:underline\"></div>` }],\n    plugins: [hui],\n  }\n\n  return run('@tailwind utilities', config).then((result) => {\n    expect(result.css).toMatchSnapshot()\n  })\n})\n\nit('should generate the inverse \"not\" css for an exposed state', async () => {\n  let config = {\n    content: [{ raw: html`<div class=\"ui-not-open:underline\"></div>` }],\n    plugins: [hui],\n  }\n\n  return run('@tailwind utilities', config).then((result) => {\n    expect(result.css).toMatchSnapshot()\n  })\n})\n\nit('should generate the ui-focus-visible variant', async () => {\n  let config = {\n    content: [{ raw: html`<div class=\"ui-focus-visible:underline\"></div>` }],\n    plugins: [hui],\n  }\n\n  return run('@tailwind utilities', config).then((result) => {\n    expect(result.css).toMatchSnapshot()\n  })\n})\n\nit('should generate the ui-not-focus-visible variant', async () => {\n  let config = {\n    content: [{ raw: html`<div class=\"ui-not-focus-visible:underline\"></div>` }],\n    plugins: [hui],\n  }\n\n  return run('@tailwind utilities', config).then((result) => {\n    expect(result.css).toMatchSnapshot()\n  })\n})\n\ndescribe('custom prefix', () => {\n  it('should generate css for an exposed state', async () => {\n    let config = {\n      content: [{ raw: html`<div class=\"hui-open:underline\"></div>` }],\n      plugins: [hui({ prefix: 'hui' })],\n    }\n\n    return run('@tailwind utilities', config).then((result) => {\n      expect(result.css).toMatchSnapshot()\n    })\n  })\n\n  it('should generate the inverse \"not\" css for an exposed state', async () => {\n    let config = {\n      content: [{ raw: html`<div class=\"hui-not-open:underline\"></div>` }],\n      plugins: [hui({ prefix: 'hui' })],\n    }\n\n    return run('@tailwind utilities', config).then((result) => {\n      expect(result.css).toMatchSnapshot()\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/src/index.ts",
    "content": "import plugin from 'tailwindcss/plugin.js'\n\ninterface Options {\n  /**\n   * The prefix used for the variants. This defaults to `ui`.\n   *\n   * Usage example:\n   * ```html\n   *  <div class=\"ui-open:underline\"></div>\n   *  ```\n   **/\n  prefix?: string\n}\n\nexport default plugin.withOptions<Options>(({ prefix = 'ui' } = {}) => {\n  return ({ addVariant }) => {\n    for (let state of ['open', 'checked', 'selected', 'active', 'disabled']) {\n      // TODO: Once `:has()` is properly supported, then we can switch to this version:\n      // addVariant(`${prefix}-${state}`, [\n      //   `&[data-headlessui-state~=\"${state}\"]`,\n      //   `:where([data-headlessui-state~=\"${state}\"]):not(:has([data-headlessui-state])) &`,\n      // ])\n\n      // But for now, this will do:\n      addVariant(`${prefix}-${state}`, [\n        `&[data-headlessui-state~=\"${state}\"]`,\n        `:where([data-headlessui-state~=\"${state}\"]) &`,\n      ])\n\n      addVariant(`${prefix}-not-${state}`, [\n        `&[data-headlessui-state]:not([data-headlessui-state~=\"${state}\"])`,\n        `:where([data-headlessui-state]:not([data-headlessui-state~=\"${state}\"])) &:not([data-headlessui-state])`,\n      ])\n    }\n\n    addVariant(`${prefix}-focus-visible`, ':where([data-headlessui-focus-visible]) &:focus')\n    addVariant(\n      `${prefix}-not-focus-visible`,\n      '&:focus:where(:not([data-headlessui-focus-visible] &))'\n    )\n  }\n})\n"
  },
  {
    "path": "packages/@headlessui-tailwindcss/tsconfig.json",
    "content": "{\n  \"include\": [\"src\"],\n  \"compilerOptions\": {\n    \"module\": \"ESNext\",\n    \"lib\": [],\n    \"importHelpers\": true,\n    \"declaration\": true,\n    \"sourceMap\": true,\n    \"rootDir\": \"./src\",\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"downlevelIteration\": true,\n    \"moduleResolution\": \"node\",\n    \"baseUrl\": \"./\",\n    \"paths\": {\n      \"*\": [\"src/*\", \"node_modules/*\"]\n    },\n    \"jsx\": \"preserve\",\n    \"esModuleInterop\": true,\n    \"target\": \"ESNext\",\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": false\n  },\n  \"exclude\": [\"node_modules\", \"**/*.test.tsx?\"]\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n### Added\n\n- Add `immediate` prop to `Combobox` for immediately opening the Combobox when the `input` receives focus ([#2686](https://github.com/tailwindlabs/headlessui/pull/2686))\n- Add `virtual` prop to `Combobox` component ([#2779](https://github.com/tailwindlabs/headlessui/pull/2779))\n\n### Fixed\n\n- Cancel outside click behavior on touch devices when scrolling ([#3266](https://github.com/tailwindlabs/headlessui/pull/3266))\n- Fix restoring focus to correct element when closing `Dialog` component ([#3365](https://github.com/tailwindlabs/headlessui/pull/3365))\n- Cleanup `process` in Combobox component when using virtualization ([#3495](https://github.com/tailwindlabs/headlessui/pull/3495))\n- Ensure outside click properly works when clicking SVG elements ([#3777](https://github.com/tailwindlabs/headlessui/pull/3777))\n- Support `<summary>` as a focusable element inside `<details>` ([#3389](https://github.com/tailwindlabs/headlessui/pull/3389))\n\n## [1.7.22] - 2024-05-08\n\n### Fixed\n\n- Don’t cancel `touchmove` on `input` elements inside a dialog ([#3166](https://github.com/tailwindlabs/headlessui/pull/3166))\n- Ensure clicking a `ComboboxOption` after filtering the options, correctly triggers a change ([#3180](https://github.com/tailwindlabs/headlessui/pull/3180))\n\n## [1.7.21] - 2024-04-26\n\n### Fixed\n\n- Prevent closing the `Combobox` component when clicking inside the scrollbar area ([#3104](https://github.com/tailwindlabs/headlessui/pull/3104))\n- Don’t unmount portal targets used by other portals ([#3144](https://github.com/tailwindlabs/headlessui/pull/3144))\n\n## [1.7.20] - 2024-04-15\n\n### Fixed\n\n- Expose `disabled` state on `Tab` component ([#2918](https://github.com/tailwindlabs/headlessui/pull/2918))\n- Prevent default behaviour when clicking outside of a `DialogPanel` ([#2919](https://github.com/tailwindlabs/headlessui/pull/2919))\n- Don’t override explicit `disabled` prop for components inside `MenuItem` ([#2929](https://github.com/tailwindlabs/headlessui/pull/2929))\n- Add `hidden` attribute to internal `Hidden` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))\n- Allow setting custom `tabIndex` on the `Switch` component ([#2966](https://github.com/tailwindlabs/headlessui/pull/2966))\n- Forward `disabled` state to hidden inputs in form-like components ([#3004](https://github.com/tailwindlabs/headlessui/pull/3004))\n- Respect `selectedIndex` for controlled `Tab` components ([#3037](https://github.com/tailwindlabs/headlessui/pull/3037))\n\n## [1.7.19] - 2024-02-07\n\n- Fix Combobox `activeOption` render prop ([#2973](https://github.com/tailwindlabs/headlessui/pull/2973))\n\n## [1.7.18] - 2024-02-02\n\n### Added\n\n- Allow users customize ID generation ([#2959](https://github.com/tailwindlabs/headlessui/pull/2959))\n\n## [1.7.17] - 2024-01-08\n\n### Fixed\n\n- Don't call `Dialog`'s `onClose` twice on mobile devices ([#2690](https://github.com/tailwindlabs/headlessui/pull/2690))\n- Fix Portal SSR hydration mismatches ([#2700](https://github.com/tailwindlabs/headlessui/pull/2700))\n- Ensure hidden `TabPanel` components are hidden from the accessibility tree ([#2708](https://github.com/tailwindlabs/headlessui/pull/2708))\n- Add support for `role=\"alertdialog\"` to `Dialog` component ([#2709](https://github.com/tailwindlabs/headlessui/pull/2709))\n- Ensure blurring the `ComboboxInput` component closes the `Combobox` ([#2712](https://github.com/tailwindlabs/headlessui/pull/2712))\n- Allow `button` to be in nested components in `PopoverButton` ([#2715](https://github.com/tailwindlabs/headlessui/pull/2715))\n- Don't overwrite user-defined template refs when rendering ([#2720](https://github.com/tailwindlabs/headlessui/pull/2720))\n- Fix missing `data-headlessui-state` attribute when `as=\"template\"` ([#2787](https://github.com/tailwindlabs/headlessui/pull/2787))\n- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))\n- Fix outside click detection when component is mounted in the Shadow DOM ([6846231](https://github.com/tailwindlabs/headlessui/commit/684623131b99d9e75dfc1c1f6d27244c334a95d9))\n- Fix CJS types ([#2880](https://github.com/tailwindlabs/headlessui/pull/2880))\n- Fix error when transition classes contain new lines ([#2871](https://github.com/tailwindlabs/headlessui/pull/2871))\n\n## [1.7.16] - 2023-08-17\n\n### Fixed\n\n- Fix form elements for uncontrolled `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))\n- Use correct value when resetting `<Listbox multiple>` and `<Combobox multiple>` ([#2626](https://github.com/tailwindlabs/headlessui/pull/2626))\n- Render `MainTreeNode` in `PopoverGroup` component only ([#2634](https://github.com/tailwindlabs/headlessui/pull/2634))\n- Disable smooth scrolling when opening/closing `Dialog` components on iOS ([#2635](https://github.com/tailwindlabs/headlessui/pull/2635))\n- Don't assume `Tab` components are available when setting the next index ([#2642](https://github.com/tailwindlabs/headlessui/pull/2642))\n- Improve SSR of the `Disclosure` component ([#2645](https://github.com/tailwindlabs/headlessui/pull/2645))\n- Fix incorrectly focused `ComboboxInput` component on page load ([#2654](https://github.com/tailwindlabs/headlessui/pull/2654))\n- Improve resetting values when using the `nullable` prop on the `Combobox` component ([#2660](https://github.com/tailwindlabs/headlessui/pull/2660))\n- Prevent scrolling when focusing a tab ([#2674](https://github.com/tailwindlabs/headlessui/pull/2674))\n\n## [1.7.15] - 2023-07-27\n\n### Fixed\n\n- Ensure the caret is in a consistent position when syncing the `Combobox.Input` value ([#2568](https://github.com/tailwindlabs/headlessui/pull/2568))\n- Improve \"outside click\" behaviour in combination with 3rd party libraries ([#2572](https://github.com/tailwindlabs/headlessui/pull/2572))\n- Improve performance of `Combobox` component ([#2574](https://github.com/tailwindlabs/headlessui/pull/2574))\n- Ensure IME works on Android devices ([#2580](https://github.com/tailwindlabs/headlessui/pull/2580))\n- Calculate `aria-expanded` purely based on the open/closed state ([#2610](https://github.com/tailwindlabs/headlessui/pull/2610))\n- Submit form on `Enter` even if no submit-like button was found ([#2613](https://github.com/tailwindlabs/headlessui/pull/2613))\n\n## [1.7.14] - 2023-06-01\n\n### Fixed\n\n- Fix memory leak in `Popover` component ([#2430](https://github.com/tailwindlabs/headlessui/pull/2430))\n- Ensure `FocusTrap` is only active when the given `enabled` value is `true` ([#2456](https://github.com/tailwindlabs/headlessui/pull/2456))\n- Ensure the exposed `activeIndex` is up to date for the `Combobox` component ([#2463](https://github.com/tailwindlabs/headlessui/pull/2463))\n- Improve control over `Menu` and `Listbox` options while searching ([#2471](https://github.com/tailwindlabs/headlessui/pull/2471))\n- Consider clicks inside iframes to be \"outside\" ([#2485](https://github.com/tailwindlabs/headlessui/pull/2485))\n- Ensure moving focus within a `Portal` component, does not close the `Popover` component ([#2492](https://github.com/tailwindlabs/headlessui/pull/2492))\n\n### Changed\n\n- Move `types` condition to the front ([#2469](https://github.com/tailwindlabs/headlessui/pull/2469))\n\n## [1.7.13] - 2023-04-12\n\n### Fixed\n\n- Fix focus styles showing up when using the mouse ([#2347](https://github.com/tailwindlabs/headlessui/pull/2347))\n- Disable `ComboboxInput` when its `Combobox` is disabled ([#2375](https://github.com/tailwindlabs/headlessui/pull/2375))\n- Add `FocusTrap` event listeners once document has loaded ([#2389](https://github.com/tailwindlabs/headlessui/pull/2389))\n- Don't scroll-lock `Dialog` when wrapping transition isn't showing ([#2422](https://github.com/tailwindlabs/headlessui/pull/2422))\n- Ensure DOM `ref` is properly handled in the `RadioGroup` component ([#2424](https://github.com/tailwindlabs/headlessui/pull/2424))\n- Correctly handle IME composition in `Combobox.Input` ([#2426](https://github.com/tailwindlabs/headlessui/pull/2426))\n\n### Added\n\n- Add `form` prop to form-like components such as `RadioGroup`, `Switch`, `Listbox`, and `Combobox` ([#2356](https://github.com/tailwindlabs/headlessui/pull/2356))\n\n## [1.7.12] - 2023-03-03\n\n### Fixed\n\n- Enable native label behavior for `Switch` where possible ([#2265](https://github.com/tailwindlabs/headlessui/pull/2265))\n- Allow root containers from the `Dialog` component in the `FocusTrap` component ([#2322](https://github.com/tailwindlabs/headlessui/pull/2322))\n- Cleanup internal TypeScript types ([#2329](https://github.com/tailwindlabs/headlessui/pull/2329))\n- Fix restore focus to buttons in Safari, when `Dialog` component closes ([#2326](https://github.com/tailwindlabs/headlessui/pull/2326))\n- Ensure hooks in the `FocusTrap` component only apply when mounted ([#2331](https://github.com/tailwindlabs/headlessui/pull/2331))\n\n## [1.7.11] - 2023-02-24\n\n### Fixed\n\n- Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290))\n- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293))\n- Fix `change` event incorrectly getting called on `blur` ([#2296](https://github.com/tailwindlabs/headlessui/pull/2296))\n- Fix `Dialog` cleanup when the `Dialog` becomes hidden ([#2303](https://github.com/tailwindlabs/headlessui/pull/2303))\n\n## [1.7.10] - 2023-02-15\n\n### Fixed\n\n- Don’t fire `afterLeave` event more than once for a given transition ([#2267](https://github.com/tailwindlabs/headlessui/pull/2267))\n- Move `aria-multiselectable` to `[role=listbox]` in the `Combobox` component ([#2271](https://github.com/tailwindlabs/headlessui/pull/2271))\n- Re-focus `Combobox.Input` when a `Combobox.Option` is selected ([#2272](https://github.com/tailwindlabs/headlessui/pull/2272))\n- Ensure we reset the `activeOptionIndex` if the active option is unmounted ([#2274](https://github.com/tailwindlabs/headlessui/pull/2274))\n- Start cleanup phase of the `Dialog` component when going into the `Closing` state ([#2264](https://github.com/tailwindlabs/headlessui/pull/2264))\n\n## [1.7.9] - 2023-02-03\n\n### Fixed\n\n- Don't break overflow when multiple dialogs are open at the same time ([#2215](https://github.com/tailwindlabs/headlessui/pull/2215))\n\n## [1.7.8] - 2023-01-27\n\n### Changed\n\n- Adjust SSR detection mechanism ([#2102](https://github.com/tailwindlabs/headlessui/pull/2102))\n\n### Fixed\n\n- Ensure `disabled=\"false\"` is not incorrectly passed to the underlying DOM Node ([#2138](https://github.com/tailwindlabs/headlessui/pull/2138))\n- Fix arrow key handling in `Tab` (after DOM order changes) ([#2145](https://github.com/tailwindlabs/headlessui/pull/2145))\n- Fix `Tab` key with non focusable elements in `Popover.Panel` ([#2147](https://github.com/tailwindlabs/headlessui/pull/2147))\n- Don’t overwrite classes during SSR when rendering fragments ([#2173](https://github.com/tailwindlabs/headlessui/pull/2173))\n- Improve `Combobox` accessibility ([#2153](https://github.com/tailwindlabs/headlessui/pull/2153))\n- Fix crash when reading `headlessuiFocusGuard` of `relatedTarget` in the `FocusTrap` component ([#2203](https://github.com/tailwindlabs/headlessui/pull/2203))\n- Fix `FocusTrap` in `Dialog` when there is only 1 focusable element ([#2172](https://github.com/tailwindlabs/headlessui/pull/2172))\n- Improve `Tabs` wrapping around when controlling the component and overflowing the `selectedIndex` ([#2213](https://github.com/tailwindlabs/headlessui/pull/2213))\n- Fix `shadow-root` bug closing `Dialog` containers ([#2217](https://github.com/tailwindlabs/headlessui/pull/2217))\n\n### Added\n\n- Allow setting `tabIndex` on the `Tab.Panel` ([#2214](https://github.com/tailwindlabs/headlessui/pull/2214))\n\n## [1.7.7] - 2022-12-16\n\n### Fixed\n\n- Improve scroll restoration after `Dialog` closes ([b20e48dd](https://github.com/tailwindlabs/headlessui/commit/b20e48dde3c37867f3900be3d475f9ac2058b587))\n\n## [1.7.6] - 2022-12-15\n\n### Fixed\n\n- Fix regression where `displayValue` crashes ([#2087](https://github.com/tailwindlabs/headlessui/pull/2087))\n- Fix `displayValue` syncing when `Combobox.Input` is unmounted and re-mounted in different trees ([#2090](https://github.com/tailwindlabs/headlessui/pull/2090))\n- Fix FocusTrap escape due to strange tabindex values ([#2093](https://github.com/tailwindlabs/headlessui/pull/2093))\n- Improve scroll locking on iOS ([#2100](https://github.com/tailwindlabs/headlessui/pull/2100), [28234b0e](https://github.com/tailwindlabs/headlessui/commit/28234b0e37633c0e2ec62ac8bd12320207a5d02b))\n\n## [1.7.5] - 2022-12-08\n\n### Fixed\n\n- Reset form-like components when the parent `form` resets ([#2004](https://github.com/tailwindlabs/headlessui/pull/2004))\n- Ensure Popover doesn't crash when `focus` is going to `window` ([#2019](https://github.com/tailwindlabs/headlessui/pull/2019))\n- Ensure `shift+home` and `shift+end` works as expected in the `ComboboxInput` component ([#2024](https://github.com/tailwindlabs/headlessui/pull/2024))\n- Improve syncing of the `ComboboxInput` value ([#2042](https://github.com/tailwindlabs/headlessui/pull/2042))\n- Fix crash when using `multiple` mode without `value` prop (uncontrolled) for `Listbox` and `Combobox` components ([#2058](https://github.com/tailwindlabs/headlessui/pull/2058))\n- Allow passing in your own `id` prop ([#2060](https://github.com/tailwindlabs/headlessui/pull/2060))\n- Add `null` as a valid type for Listbox and Combobox in Vue ([#2064](https://github.com/tailwindlabs/headlessui/pull/2064), [#2067](https://github.com/tailwindlabs/headlessui/pull/2067))\n- Improve SSR for Tabs in Vue ([#2068](https://github.com/tailwindlabs/headlessui/pull/2068))\n- Ignore pointer events in Listbox, Menu, and Combobox when cursor hasn't moved ([#2069](https://github.com/tailwindlabs/headlessui/pull/2069))\n- Allow clicks inside dialog panel when target is inside shadow root ([#2079](https://github.com/tailwindlabs/headlessui/pull/2079))\n\n## [1.7.4] - 2022-11-03\n\n### Fixed\n\n- Expose `close` function for `Menu` and `MenuItem` components ([#1897](https://github.com/tailwindlabs/headlessui/pull/1897))\n- Fix `useOutsideClick`, add improvements for ShadowDOM ([#1914](https://github.com/tailwindlabs/headlessui/pull/1914))\n- Prevent default slot warning when using a component for `as` prop ([#1915](https://github.com/tailwindlabs/headlessui/pull/1915))\n- Fire `ComboboxInput`'s `@change` handler when changing the value internally ([#1916](https://github.com/tailwindlabs/headlessui/pull/1916))\n\n## [1.7.3] - 2022-09-30\n\n### Fixed\n\n- Call `displayValue` with a v-model of `ref(undefined)` on `ComboboxInput` ([#1865](https://github.com/tailwindlabs/headlessui/pull/1865))\n- Improve `Portal` detection for `Popover` components ([#1842](https://github.com/tailwindlabs/headlessui/pull/1842))\n- Fix crash when `children` are `undefined` ([#1885](https://github.com/tailwindlabs/headlessui/pull/1885))\n- Fix `useOutsideClick` swallowing events inside ShadowDOM ([#1876](https://github.com/tailwindlabs/headlessui/pull/1876))\n- Fix `Tab` incorrectly activating on `focus` event ([#1887](https://github.com/tailwindlabs/headlessui/pull/1887))\n\n## [1.7.2] - 2022-09-15\n\n### Fixed\n\n- Prevent option selection in `ComboboxInput` while composing ([#1850](https://github.com/tailwindlabs/headlessui/issues/1850))\n- Ensure we handle the `static` prop in `TabPanel` components correctly ([#1856](https://github.com/tailwindlabs/headlessui/pull/1856))\n\n## [1.7.1] - 2022-09-12\n\n### Fixed\n\n- Improve iOS scroll locking ([#1830](https://github.com/tailwindlabs/headlessui/pull/1830))\n- Ensure `Tab` order stays consistent, and the currently active `Tab` stays active ([#1837](https://github.com/tailwindlabs/headlessui/pull/1837))\n\n## [1.7.0] - 2022-09-06\n\n### Added\n\n- Add `by` prop for `Listbox`, `Combobox` and `RadioGroup` ([#1482](https://github.com/tailwindlabs/headlessui/pull/1482), [#1717](https://github.com/tailwindlabs/headlessui/pull/1717), [#1814](https://github.com/tailwindlabs/headlessui/pull/1814), [#1815](https://github.com/tailwindlabs/headlessui/pull/1815))\n- Make form components uncontrollable ([#1683](https://github.com/tailwindlabs/headlessui/pull/1683))\n- Add `@headlessui/tailwindcss` plugin ([#1487](https://github.com/tailwindlabs/headlessui/pull/1487))\n\n### Fixed\n\n- Fixed SSR support on Deno ([#1671](https://github.com/tailwindlabs/headlessui/pull/1671))\n- Don’t close dialog when opened during mouse up event ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Don’t close dialog when drag ends outside dialog ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Fix outside clicks to close dialog when nested, unopened dialogs are present ([#1667](https://github.com/tailwindlabs/headlessui/pull/1667))\n- Close `Menu` component when using `tab` key ([#1673](https://github.com/tailwindlabs/headlessui/pull/1673))\n- Resync input when display value changes ([#1679](https://github.com/tailwindlabs/headlessui/pull/1679), [#1755](https://github.com/tailwindlabs/headlessui/pull/1755))\n- Ensure controlled `Tabs` don't change automagically ([#1680](https://github.com/tailwindlabs/headlessui/pull/1680))\n- Improve outside click on Safari iOS ([#1712](https://github.com/tailwindlabs/headlessui/pull/1712))\n- Improve event handler merging ([#1715](https://github.com/tailwindlabs/headlessui/pull/1715))\n- Fix incorrect scrolling to the bottom when opening a `Dialog` ([#1716](https://github.com/tailwindlabs/headlessui/pull/1716))\n- Don't overwrite `element.focus()` on `PopoverPanel` ([#1719](https://github.com/tailwindlabs/headlessui/pull/1719))\n- Improve `Combobox` re-opening keyboard issue on mobile ([#1732](https://github.com/tailwindlabs/headlessui/pull/1732))\n- Only select the active option when using \"singular\" mode when pressing `tab` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750))\n- Only restore focus to the `MenuButton` if necessary when activating a `MenuOption` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782))\n- Don't scroll when wrapping around in focus trap ([#1789](https://github.com/tailwindlabs/headlessui/pull/1789))\n- Improve accessibility when announcing `ListboxOption` and `ComboboxOption` components ([#1812](https://github.com/tailwindlabs/headlessui/pull/1812))\n- Expose the `value` from the `Combobox` and `Listbox` components slot ([#1822](https://github.com/tailwindlabs/headlessui/pull/1822))\n- Improve `scroll lock` on iOS ([#1824](https://github.com/tailwindlabs/headlessui/pull/1824))\n\n## [1.6.7] - 2022-07-12\n\n### Fixed\n\n- Prevent cancelling transitions due to focus trap ([#1664](https://github.com/tailwindlabs/headlessui/pull/1664))\n\n## [1.6.6] - 2022-07-07\n\n### Fixed\n\n- Fix getting Vue dom elements ([#1610](https://github.com/tailwindlabs/headlessui/pull/1610))\n- Ensure `CMD`+`Backspace` works in nullable mode for `Combobox` component ([#1617](https://github.com/tailwindlabs/headlessui/pull/1617))\n- Properly merge incoming props with own props ([#1651](https://github.com/tailwindlabs/headlessui/pull/1651))\n- Ensure `PopoverPanel` can be used inside `transition` ([#1653](https://github.com/tailwindlabs/headlessui/pull/1653))\n\n## [1.6.5] - 2022-06-20\n\n### Fixed\n\n- Support `slot` children when using `as=\"template\"` ([#1548](https://github.com/tailwindlabs/headlessui/pull/1548))\n- Improve outside click of `Dialog` component ([#1546](https://github.com/tailwindlabs/headlessui/pull/1546))\n- Detect outside clicks from within `iframe` elements ([#1552](https://github.com/tailwindlabs/headlessui/pull/1552))\n- Only render the `Dialog` on the client ([#1566](https://github.com/tailwindlabs/headlessui/pull/1566))\n- Improve Combobox input cursor position ([#1574](https://github.com/tailwindlabs/headlessui/pull/1574))\n- Fix scrolling issue in `Tab` component when using arrow keys ([#1584](https://github.com/tailwindlabs/headlessui/pull/1584))\n- Fix missing `aria-expanded` for `ComboboxInput` component ([#1605](https://github.com/tailwindlabs/headlessui/pull/1605))\n\n## [1.6.4] - 2022-05-29\n\n### Fixed\n\n- Ensure `Escape` propagates correctly in `Combobox` component ([#1511](https://github.com/tailwindlabs/headlessui/pull/1511))\n- Remove leftover code in Combobox component ([#1514](https://github.com/tailwindlabs/headlessui/pull/1514))\n\n## [1.6.3] - 2022-05-25\n\n### Fixed\n\n- Allow to override the `type` on the `ComboboxInput` ([#1476](https://github.com/tailwindlabs/headlessui/pull/1476))\n- Ensure the the `<PopoverPanel focus>` closes correctly ([#1477](https://github.com/tailwindlabs/headlessui/pull/1477))\n- Only render the `FocusSentinel` if required in the `Tabs` component ([#1493](https://github.com/tailwindlabs/headlessui/pull/1493))\n\n## [1.6.2] - 2022-05-19\n\n### Fixed\n\n- Ensure `DialogPanel` exposes its ref ([#1404](https://github.com/tailwindlabs/headlessui/pull/1404))\n- Ignore `Escape` when event got prevented in `Dialog` component ([#1424](https://github.com/tailwindlabs/headlessui/pull/1424))\n- Improve `FocusTrap` behaviour ([#1432](https://github.com/tailwindlabs/headlessui/pull/1432))\n- Simplify `Popover` Tab logic by using sentinel nodes instead of keydown event interception ([#1440](https://github.com/tailwindlabs/headlessui/pull/1440))\n- Ensure the `PopoverPanel` is clickable without closing the `Popover` ([#1443](https://github.com/tailwindlabs/headlessui/pull/1443))\n- Improve \"Scroll lock\" scrollbar width for `Dialog` component ([#1457](https://github.com/tailwindlabs/headlessui/pull/1457))\n- Don't throw when SSR rendering internal portals in Vue ([#1459](https://github.com/tailwindlabs/headlessui/pull/1459))\n\n## [1.6.1] - 2022-05-03\n\n### Fixed\n\n- Manually passthrough `attrs` for `Combobox`, `Listbox` and `TabsGroup` component ([#1372](https://github.com/tailwindlabs/headlessui/pull/1372))\n- Fix enter transitions in Vue ([#1395](https://github.com/tailwindlabs/headlessui/pull/1395))\n\n## [1.6.0] - 2022-04-25\n\n### Fixed\n\n- Make sure that the input syncs when the combobox closes ([#1137](https://github.com/tailwindlabs/headlessui/pull/1137))\n- Ensure that you can close the `Combobox` initially ([#1148](https://github.com/tailwindlabs/headlessui/pull/1148))\n- Fix `Dialog` usage in `Tab` component ([#1149](https://github.com/tailwindlabs/headlessui/pull/1149))\n- Ensure links are triggered inside `PopoverPanel` components ([#1153](https://github.com/tailwindlabs/headlessui/pull/1153))\n- Fix `hover` scroll issue in `Listbox`, `Combobox` and `Menu` components ([#1161](https://github.com/tailwindlabs/headlessui/pull/1161))\n- Guarantee DOM sort order when performing `Listbox`, `Combobox` and `Menu` actions ([#1168](https://github.com/tailwindlabs/headlessui/pull/1168))\n- Improve outside click support ([#1175](https://github.com/tailwindlabs/headlessui/pull/1175))\n- Reset `ComboboxInput` when the value gets reset ([#1181](https://github.com/tailwindlabs/headlessui/pull/1181))\n- Adjust active `item`/`option` index on `Listbox`, `Combobox` and `Menu` components ([#1184](https://github.com/tailwindlabs/headlessui/pull/1184))\n- Fix re-focusing element after close ([#1186](https://github.com/tailwindlabs/headlessui/pull/1186))\n- Fix `Dialog` cycling ([#553](https://github.com/tailwindlabs/headlessui/pull/553))\n- Only activate the `Tab` on mouseup ([#1192](https://github.com/tailwindlabs/headlessui/pull/1192))\n- Ignore \"outside click\" on removed elements ([#1193](https://github.com/tailwindlabs/headlessui/pull/1193))\n- Remove `focus()` from `Listbox.Option` ([#1218](https://github.com/tailwindlabs/headlessui/pull/1218))\n- Improve some internal code ([#1221](https://github.com/tailwindlabs/headlessui/pull/1221))\n- Don't drop initial character when searching in Combobox ([#1223](https://github.com/tailwindlabs/headlessui/pull/1223))\n- Use `ownerDocument` instead of `document` ([#1158](https://github.com/tailwindlabs/headlessui/pull/1158))\n- Fix, re-expose `el` from each component ([#1230](https://github.com/tailwindlabs/headlessui/pull/1230))\n- Ensure focus trapping plays well with the `Tab` and `Dialog` components ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231))\n- Improve syncing of `ComboboxInput` value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248))\n- Fix tree-shaking support ([#1247](https://github.com/tailwindlabs/headlessui/pull/1247))\n- Stop propagation on the `PopoverButton` ([#1263](https://github.com/tailwindlabs/headlessui/pull/1263))\n- Fix incorrect closing while interacting with third party libraries in `Dialog` component ([#1268](https://github.com/tailwindlabs/headlessui/pull/1268))\n- Mimic browser select on focus when navigating via `Tab` ([#1272](https://github.com/tailwindlabs/headlessui/pull/1272))\n- Resolve `initialFocusRef` correctly ([#1276](https://github.com/tailwindlabs/headlessui/pull/1276))\n- 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))\n- Support classic form submissions in `RadioGroup`, `Switch` and `Combobox` components ([#1285](https://github.com/tailwindlabs/headlessui/pull/1285))\n- Fix `nullable` prop for Vue ([2b109548b1a94a30858cf58c8f525554a1c12cbb](https://github.com/tailwindlabs/headlessui/commit/2b109548b1a94a30858cf58c8f525554a1c12cbb))\n- Prefer incoming `open` prop over OpenClosed state ([#1360](https://github.com/tailwindlabs/headlessui/pull/1360))\n\n### Added\n\n- Add classic form submission compatibility via new hidden inputs ([#1214](https://github.com/tailwindlabs/headlessui/pull/1214))\n- Add multiple value support to `Listbox` and `Combobox` components ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243), [#1355](https://github.com/tailwindlabs/headlessui/pull/1355))\n- Add support for clearing the value of a `Combobox` ([#1295](https://github.com/tailwindlabs/headlessui/pull/1295))\n- Add `DialogBackdrop` and `DialogPanel` components ([#1333](https://github.com/tailwindlabs/headlessui/pull/1333))\n\n## [1.5.0] - 2022-02-17\n\n### Fixed\n\n- Ensure correct order when conditionally rendering `MenuItem`, `ListboxOption` and `RadioGroupOption` ([#1045](https://github.com/tailwindlabs/headlessui/pull/1045))\n- Improve typeahead search logic ([#1051](https://github.com/tailwindlabs/headlessui/pull/1051))\n- Improve overal codebase, use modern tech like `esbuild` and TypeScript 4! ([#1055](https://github.com/tailwindlabs/headlessui/pull/1055))\n- Improve build files ([#1078](https://github.com/tailwindlabs/headlessui/pull/1078))\n- Ensure typeahead stays on same item if it still matches ([#1098](https://github.com/tailwindlabs/headlessui/pull/1098))\n\n### Added\n\n- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104), [#1106](https://github.com/tailwindlabs/headlessui/pull/1106), [#1109](https://github.com/tailwindlabs/headlessui/pull/1109))\n\n## [1.4.3] - 2022-01-14\n\n### Fixes\n\n- Fix missing key binding in examples ([#1036](https://github.com/tailwindlabs/headlessui/pull/1036), [#1006](https://github.com/tailwindlabs/headlessui/pull/1006))\n- Fix slice => splice typo in `Tabs` component ([#1037](https://github.com/tailwindlabs/headlessui/pull/1037), [#986](https://github.com/tailwindlabs/headlessui/pull/986))\n- Ensure correct DOM node order when performing focus actions ([#1038](https://github.com/tailwindlabs/headlessui/pull/1038))\n\n### Added\n\n- Allow for `TabGroup` to be controllable ([#909](https://github.com/tailwindlabs/headlessui/pull/909), [#970](https://github.com/tailwindlabs/headlessui/pull/970))\n\n## [1.4.2] - 2021-11-08\n\n### Fixes\n\n- Stop the event from propagating in the `Popover` component ([#798](https://github.com/tailwindlabs/headlessui/pull/798))\n- Allow clicking on elements inside a `DialogOverlay` ([#816](https://github.com/tailwindlabs/headlessui/pull/816))\n- Fix SSR crash because of `useWindowEvent` ([#817](https://github.com/tailwindlabs/headlessui/pull/817))\n- Improve tree shaking ([#859](https://github.com/tailwindlabs/headlessui/pull/859))\n- Add `type=\"button\"` to `Tabs` component ([#912](https://github.com/tailwindlabs/headlessui/pull/912))\n\n## [1.4.1] - 2021-08-30\n\n### Fixes\n\n- Only add `type=button` to real buttons ([#709](https://github.com/tailwindlabs/headlessui/pull/709))\n- Add Vue emit types ([#679](https://github.com/tailwindlabs/headlessui/pull/679), [#712](https://github.com/tailwindlabs/headlessui/pull/712))\n- Fix `escape` bug not closing Dialog after clicking in Dialog ([#754](https://github.com/tailwindlabs/headlessui/pull/754))\n- Use `console.warn` instead of throwing an error when there are no focusable elements ([#775](https://github.com/tailwindlabs/headlessui/pull/775))\n\n## [1.4.0] - 2021-07-29\n\n### Added\n\n- Add new `Tabs` component ([#674](https://github.com/tailwindlabs/headlessui/pull/674), [#698](https://github.com/tailwindlabs/headlessui/pull/698))\n- Make `DisclosureButton` close the disclosure inside a `DisclosurePanel` ([#682](https://github.com/tailwindlabs/headlessui/pull/682))\n- Add `aria-orientation` to `Listbox`, which swaps Up/Down with Left/Right keys ([#683](https://github.com/tailwindlabs/headlessui/pull/683))\n- Expose `close` function from the scoped slot for `Disclosure`, `DisclosurePanel`, `Popover` and `PopoverPanel` ([#697](https://github.com/tailwindlabs/headlessui/pull/697))\n\n## [1.3.0] - 2021-06-21\n\n### Added\n\n- Ensure that you can use `TransitionChild` when using implicit Transitions ([#503](https://github.com/tailwindlabs/headlessui/pull/503))\n- Add new `entered` prop for `Transition` and `TransitionChild` components ([#504](https://github.com/tailwindlabs/headlessui/pull/504))\n\n### Fixes\n\n- Add `aria-disabled` on disabled `RadioGroup.Option` components ([#543](https://github.com/tailwindlabs/headlessui/pull/543))\n- Improve `disabled` and `tabindex` prop handling ([#512](https://github.com/tailwindlabs/headlessui/pull/512))\n- Improve reactivity when destructuring from props ([#512](https://github.com/tailwindlabs/headlessui/pull/512))\n- Improve `aria-expanded` logic ([#592](https://github.com/tailwindlabs/headlessui/pull/592))\n\n## [1.2.0] - 2021-05-10\n\n### Added\n\n- Introduce Open/Closed state, to simplify component communication ([#466](https://github.com/tailwindlabs/headlessui/pull/466))\n\n## [1.1.1] - 2021-04-28\n\n### Fixes\n\n- Fix form submission within Dialog ([#460](https://github.com/tailwindlabs/headlessui/pull/460))\n- Fix TypeScript types for `Listbox` and `Switch` ([#459](https://github.com/tailwindlabs/headlessui/pull/459), [#461](https://github.com/tailwindlabs/headlessui/pull/461))\n\n### Added\n\n- Add `disabled` prop to `RadioGroup` and `RadioGroup.Option` ([#401](https://github.com/tailwindlabs/headlessui/pull/401))\n- Add `defaultOpen` prop to the `Disclosure` component ([#447](https://github.com/tailwindlabs/headlessui/pull/447))\n\n## [1.1.0] - 2021-04-26\n\n### Fixes\n\n- Improve search, make searching case insensitive ([#385](https://github.com/tailwindlabs/headlessui/pull/385))\n- Fix unreachable `RadioGroup` ([#401](https://github.com/tailwindlabs/headlessui/pull/401))\n- Fix `RadioGroupOption` value type ([#400](https://github.com/tailwindlabs/headlessui/pull/400))\n- Fix closing nested `Dialog` components when pressing `Escape` ([#430](https://github.com/tailwindlabs/headlessui/pull/430))\n\n### Added\n\n- Add `disabled` prop to `RadioGroup` and `RadioGroupOption` ([#401](https://github.com/tailwindlabs/headlessui/pull/401))\n- Add `defaultOpen` prop to the `Disclosure` component ([#447](https://github.com/tailwindlabs/headlessui/pull/447))\n\n## [1.0.0] - 2021-04-14\n\n### Fixes\n\n- Fix incorrect `DOM` node from ref ([#249](https://github.com/tailwindlabs/headlessui/pull/249))\n- Stop propagating keyboard/mouse events ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n\n### Added\n\n- Add `SwitchDescription` component, which adds the `aria-describedby` to the actual Switch ([#220](https://github.com/tailwindlabs/headlessui/pull/220))\n- Add `Disclosure`, `DisclosureButton`, `DisclosurePanel` components ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `Dialog`, `DialogOverlay`, `DialogTitle` and `DialogDescription` components ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `Portal` and `PortalGroup` components ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `FocusTrap` component ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `Popover`, `PopoverButton`, `PopoverOverlay`, `PopoverPanel` and `PopoverGroup` components ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `RadioGroup`, `RadioGroupOption`, `RadioGroupLabel` and `RadioGroupDescription` components ([#282](https://github.com/tailwindlabs/headlessui/pull/282))\n- Add `TransitionRoot` and `TransitionChild` components ([#326](https://github.com/tailwindlabs/headlessui/pull/326))\n\n## [0.3.1] - 2021-04-02\n\n### Fixes\n\n- Fix broken behaviour since Vue 3.0.5 ([#279](https://github.com/tailwindlabs/headlessui/pull/279))\n\n## [0.3.0] - 2021-02-06\n\n### Fixes\n\n- Ensure that you can't use Enter to invoke the Switch\n- Fix outside click refocus bug ([#114](https://github.com/tailwindlabs/headlessui/pull/114))\n- Prevent scrolling when refocusing items\n- Ensure `Switch` has `type=\"button\"` ([#192](https://github.com/tailwindlabs/headlessui/pull/192))\n- Added `emits` property to Vue components ([#199](https://github.com/tailwindlabs/headlessui/pull/199))\n- Fix `disabled` not working when inside a disabled fieldset ([#202](https://github.com/tailwindlabs/headlessui/pull/202))\n- Trigger \"outside click\" behaviour on mousedown ([#212](https://github.com/tailwindlabs/headlessui/pull/212))\n- Ensure the `active` MenuItem is scrolled into view\n- Ensure valid Menu accessibility tree ([#228](https://github.com/tailwindlabs/headlessui/pull/228))\n\n### Added\n\n- Add render features + render strategy (`static` and `unmount={true | false}`) ([#106](https://github.com/tailwindlabs/headlessui/pull/106))\n- Add `disabled` prop to `Listbox` itself, instead of the `ListboxButton` ([#229](https://github.com/tailwindlabs/headlessui/pull/229))\n\n## [0.2.0] - 2020-10-06\n\n### Added\n\n- Add `Listbox` component\n- Add `Switch` component\n\n## [0.1.3] - 2020-09-29\n\n### Fixes\n\n- Fix an issue where you couldn't click on menu items that were links.\n- Fix outside click behaviour. If you had multiple menu's, when menu 1 is open, menu 2 is closed and you click on menu button 2 it will open both menu's. This is now fixed.\n- Ensure when using keyboard navigation we prevent the default behaviour.\n\n## [0.1.2] - 2020-09-25\n\n### Fixes\n\n- Fix issue where button `MenuItem` instances didn't properly fire click events\n- Don't pass `disabled` prop through to children, only add `aria-disabled`\n\n## [0.1.1] - 2020-09-24\n\n### Added\n\n- Everything!\n\n[unreleased]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vu@v1.7.22...HEAD\n[1.7.22]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.21...@headlessui/vue@v1.7.22\n[1.7.21]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.20...@headlessui/vue@v1.7.21\n[1.7.20]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.19...@headlessui/vue@v1.7.20\n[1.7.19]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.18...@headlessui/vue@v1.7.19\n[1.7.18]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.17...@headlessui/vue@v1.7.18\n[1.7.17]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.16...@headlessui/vue@v1.7.17\n[1.7.16]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.15...@headlessui/vue@v1.7.16\n[1.7.15]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.14...@headlessui/vue@v1.7.15\n[1.7.14]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.13...@headlessui/vue@v1.7.14\n[1.7.13]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.12...@headlessui/vue@v1.7.13\n[1.7.12]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.11...@headlessui/vue@v1.7.12\n[1.7.11]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.10...@headlessui/vue@v1.7.11\n[1.7.10]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.9...@headlessui/vue@v1.7.10\n[1.7.9]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.8...@headlessui/vue@v1.7.9\n[1.7.8]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.7...@headlessui/vue@v1.7.8\n[1.7.7]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.6...@headlessui/vue@v1.7.7\n[1.7.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.5...@headlessui/vue@v1.7.6\n[1.7.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.4...@headlessui/vue@v1.7.5\n[1.7.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.3...@headlessui/vue@v1.7.4\n[1.7.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.2...@headlessui/vue@v1.7.3\n[1.7.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.1...@headlessui/vue@v1.7.2\n[1.7.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.7.0...@headlessui/vue@v1.7.1\n[1.7.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.7...@headlessui/vue@v1.7.0\n[1.6.7]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.6...@headlessui/vue@v1.6.7\n[1.6.6]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.5...@headlessui/vue@v1.6.6\n[1.6.5]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.4...@headlessui/vue@v1.6.5\n[1.6.4]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.3...@headlessui/vue@v1.6.4\n[1.6.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.2...@headlessui/vue@v1.6.3\n[1.6.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.1...@headlessui/vue@v1.6.2\n[1.6.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.6.0...@headlessui/vue@v1.6.1\n[1.6.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.5.0...@headlessui/vue@v1.6.0\n[1.5.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.4.3...@headlessui/vue@v1.5.0\n[1.4.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.4.2...@headlessui/vue@v1.4.3\n[1.4.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.4.1...@headlessui/vue@v1.4.2\n[1.4.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.4.0...@headlessui/vue@v1.4.1\n[1.4.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.3.0...@headlessui/vue@v1.4.0\n[1.3.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.2.0...@headlessui/vue@v1.3.0\n[1.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.1.1...@headlessui/vue@v1.2.0\n[1.1.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.1.0...@headlessui/vue@v1.1.1\n[1.1.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v1.0.0...@headlessui/vue@v1.1.0\n[1.0.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.3.1...@headlessui/vue@v1.0.0\n[0.3.1]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.3.0...@headlessui/vue@v0.3.1\n[0.3.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.2.0...@headlessui/vue@v0.3.0\n[0.2.0]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.1.3...@headlessui/vue@v0.2.0\n[0.1.3]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.1.2...@headlessui/vue@v0.1.3\n[0.1.2]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/vue@v0.1.1...@headlessui/vue@v0.1.2\n[0.1.1]: https://github.com/tailwindlabs/headlessui/releases/tag/@headlessui/vue@v0.1.1\n"
  },
  {
    "path": "packages/@headlessui-vue/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Tailwind Labs\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "packages/@headlessui-vue/README.md",
    "content": "<h3 align=\"center\">\n  @headlessui/vue\n</h3>\n\n<p align=\"center\">\n  A set of completely unstyled, fully accessible UI components for Vue 3, designed to integrate\n  beautifully with Tailwind CSS.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@headlessui/vue\"><img src=\"https://img.shields.io/npm/dt/@headlessui/vue.svg\" alt=\"Total Downloads\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/releases\"><img src=\"https://img.shields.io/npm/v/@headlessui/vue.svg\" alt=\"Latest Release\"></a>\n  <a href=\"https://github.com/tailwindlabs/headlessui/blob/main/LICENSE\"><img src=\"https://img.shields.io/npm/l/@headlessui/vue.svg\" alt=\"License\"></a>\n</p>\n\n## Installation\n\nPlease note that **this library only supports Vue 3**.\n\n```sh\nnpm install @headlessui/vue\n```\n\n## Documentation\n\nFor full documentation, visit [headlessui.com](https://headlessui.com/v1/vue).\n\n## Community\n\nFor help, discussion about best practices, or feature ideas:\n\n[Discuss Headless UI on GitHub](https://github.com/tailwindlabs/headlessui/discussions)\n"
  },
  {
    "path": "packages/@headlessui-vue/build/index.cjs",
    "content": "'use strict'\n\nif (process.env.NODE_ENV === 'production') {\n  module.exports = require('./headlessui.prod.cjs')\n} else {\n  module.exports = require('./headlessui.dev.cjs')\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/jest.config.cjs",
    "content": "let create = require('../../jest/create-jest-config.cjs')\nmodule.exports = create(__dirname, { displayName: ' Vue ' })\n"
  },
  {
    "path": "packages/@headlessui-vue/package.json",
    "content": "{\n  \"name\": \"@headlessui/vue\",\n  \"version\": \"1.7.22\",\n  \"description\": \"A set of completely unstyled, fully accessible UI components for Vue 3, designed to integrate beautifully with Tailwind CSS.\",\n  \"main\": \"dist/index.cjs\",\n  \"typings\": \"dist/index.d.ts\",\n  \"module\": \"dist/headlessui.esm.js\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"README.md\",\n    \"dist\"\n  ],\n  \"exports\": {\n    \"types\": {\n      \"import\": \"./dist/index.d.ts\",\n      \"require\": \"./dist/index.d.cts\"\n    },\n    \"import\": \"./dist/headlessui.esm.js\",\n    \"require\": \"./dist/index.cjs\"\n  },\n  \"type\": \"module\",\n  \"sideEffects\": false,\n  \"engines\": {\n    \"node\": \">=10\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/tailwindlabs/headlessui.git\",\n    \"directory\": \"packages/@headlessui-vue\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"scripts\": {\n    \"prepublishOnly\": \"npm run build\",\n    \"build\": \"../../scripts/build.sh --external:vue\",\n    \"watch\": \"../../scripts/watch.sh --external:vue\",\n    \"test\": \"../../scripts/test.sh\",\n    \"lint\": \"../../scripts/lint.sh\",\n    \"lint-types\": \"npm run attw -P --workspaces --if-present\",\n    \"playground\": \"npm run dev --workspace=playground-vue\",\n    \"clean\": \"rimraf ./dist\"\n  },\n  \"peerDependencies\": {\n    \"vue\": \"^3.2.0\"\n  },\n  \"devDependencies\": {\n    \"@testing-library/vue\": \"8.0.0\",\n    \"@vue/test-utils\": \"^2.4.1\",\n    \"vue\": \"3.2.37\"\n  },\n  \"dependencies\": {\n    \"@tanstack/vue-virtual\": \"3.13.6\"\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/combobox/combobox.test.ts",
    "content": "import { computed, defineComponent, h, nextTick, reactive, ref, watch, type PropType } from 'vue'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  ComboboxMode,\n  ComboboxState,\n  assertActiveComboboxOption,\n  assertActiveElement,\n  assertCombobox,\n  assertComboboxButton,\n  assertComboboxButtonLinkedWithCombobox,\n  assertComboboxButtonLinkedWithComboboxLabel,\n  assertComboboxInput,\n  assertComboboxLabel,\n  assertComboboxLabelLinkedWithCombobox,\n  assertComboboxList,\n  assertComboboxOption,\n  assertNoActiveComboboxOption,\n  assertNoSelectedComboboxOption,\n  assertNotActiveComboboxOption,\n  getByText,\n  getComboboxButton,\n  getComboboxButtons,\n  getComboboxInput,\n  getComboboxInputs,\n  getComboboxLabel,\n  getComboboxOptions,\n  getComboboxes,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport {\n  Keys,\n  MouseButton,\n  blur,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from './combobox'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nfunction getDefaultComponents() {\n  return {\n    Combobox,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxButton,\n    ComboboxOptions,\n    ComboboxOption,\n  }\n}\n\nconst renderTemplate = createRenderTemplate(getDefaultComponents())\n\ndescribe('safeguards', () => {\n  it.each([\n    ['ComboboxButton', ComboboxButton],\n    ['ComboboxLabel', ComboboxLabel],\n    ['ComboboxOptions', ComboboxOptions],\n    ['ComboboxOption', ComboboxOption],\n  ])(\n    'should error when we are using a <%s /> without a parent <Combobox />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <Combobox /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Combobox without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              <ComboboxOption value=\"b\">Option B</ComboboxOption>\n              <ComboboxOption value=\"c\">Option C</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Combobox', () => {\n    it(\n      'should be possible to render a Combobox using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\" v-slot=\"{ open }\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions v-show=\"open\">\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to disable a Combobox',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\" disabled>\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await press(Keys.Enter, getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        // The input should also be disabled\n        assertComboboxInput({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-input-1', disabled: '' },\n        })\n\n        // And even if we try to focus it, it should not open the combobox\n        await focus(getComboboxInput())\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not crash in multiple mode',\n      suppressConsoleLogs(async () => {\n        let options = [\n          { id: 1, name: 'Alice' },\n          { id: 2, name: 'Bob' },\n          { id: 3, name: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <Combobox multiple name=\"abc\">\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption\n                  v-for=\"option in options\"\n                  :key=\"option.id\"\n                  :value=\"option\"\n                  v-slot=\"data\"\n                  >{{ JSON.stringify(data) }}</ComboboxOption\n                >\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => {\n            let value = ref(options[1])\n            return { options, value }\n          },\n        })\n\n        await click(getComboboxButton())\n        let [alice, bob, charlie] = getComboboxOptions()\n\n        await click(alice)\n        await click(bob)\n        await click(charlie)\n      })\n    )\n\n    describe('Equality', () => {\n      let options = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n\n      it(\n        'should use object equality by default',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\">\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ComboboxOption\n                  >\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref(options[1])\n              return { options, value }\n            },\n          })\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare null values by a field',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\" by=\"id\">\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ComboboxOption\n                  >\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref(null)\n              return { options, value }\n            },\n          })\n\n          await click(getComboboxButton())\n\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveTextContent(\n            JSON.stringify({ active: true, selected: false, disabled: false })\n          )\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: false, selected: false, disabled: false })\n          )\n          expect(charlie).toHaveTextContent(\n            JSON.stringify({ active: false, selected: false, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a field',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\" by=\"id\">\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ComboboxOption\n                  >\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value }\n            },\n          })\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a comparator function',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\" :by=\"compare\">\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ComboboxOption\n                  >\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value, compare: (a: any, z: any) => a.id === z.id }\n            },\n          })\n\n          await click(getComboboxButton())\n\n          let bob = getComboboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (single mode)',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\" by=\"id\">\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ComboboxOption>\n                  <ComboboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ComboboxOption>\n                  <ComboboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ComboboxOption>\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value }\n            },\n          })\n\n          await click(getComboboxButton())\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getComboboxOptions()[2])\n          await click(getComboboxButton())\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getComboboxOptions()[1])\n          await click(getComboboxButton())\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (multiple mode)',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\" by=\"id\" multiple>\n                <ComboboxButton>Trigger</ComboboxButton>\n                <ComboboxOptions>\n                  <ComboboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ComboboxOption>\n                  <ComboboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ComboboxOption>\n                  <ComboboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ComboboxOption>\n                </ComboboxOptions>\n              </Combobox>\n            `,\n            setup: () => {\n              let value = ref([{ id: 2, name: 'Bob' }])\n              return { options, value }\n            },\n          })\n\n          await click(getComboboxButton())\n\n          await click(getComboboxOptions()[2])\n          let [alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getComboboxOptions()[2])\n          ;[alice, bob, charlie] = getComboboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n    })\n\n    it(\n      'should not crash when a defaultValue is not given',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"person\" name=\"assignee\" by=\"id\">\n              <ComboboxInput :displayValue=\"displayValue\" />\n              <ComboboxButton />\n              <ComboboxOptions>\n                <ComboboxOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                  {{ person.label }}\n                </ComboboxOption>\n              <ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ person: ref(data[0]), data, displayValue: () => String(Math.random()) }),\n        })\n\n        let value = getComboboxInput()?.value\n\n        // Toggle the state a few times combobox\n        await click(getComboboxButton())\n        await click(getComboboxButton())\n        await click(getComboboxButton())\n\n        // Verify the value is still the same\n        expect(getComboboxInput()?.value).toBe(value)\n\n        // Choose an option, which should update the value\n        await click(getComboboxOptions()[1])\n\n        // Verify the value changed\n        expect(getComboboxInput()?.value).not.toBe(value)\n      })\n    )\n\n    it(\n      'should not crash when a defaultValue is not given',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <Combobox name=\"assignee\" by=\"id\">\n              <ComboboxInput :displayValue=\"(value) => value.name\" />\n              <ComboboxOptions>\n                <ComboboxOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                  {{ person.label }}\n                </ComboboxOption>\n              <ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ data }),\n        })\n      })\n    )\n\n    it(\n      'should close the Combobox when the input is blurred',\n      suppressConsoleLogs(async () => {\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <Combobox name=\"assignee\" by=\"id\">\n              <ComboboxInput />\n              <ComboboxButton />\n              <ComboboxOptions>\n                <ComboboxOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                  {{ person.label }}\n                </ComboboxOption>\n              <ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ data }),\n        })\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Verify it is open\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        // Close the combobox\n        await blur(getComboboxInput())\n\n        // Verify it is closed\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      })\n    )\n  })\n\n  describe('ComboboxInput', () => {\n    it(\n      'selecting an option puts the value into Combobox.Input when displayValue is not provided',\n      suppressConsoleLogs(async () => {\n        let Example = defineComponent({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // TODO: Rendering Example directly reveals a vue bug — I think it's been fixed for a while but I can't find the commit\n        renderTemplate(Example)\n\n        assertComboboxInput({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxInput({ state: ComboboxState.Visible })\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        await click(getComboboxOptions()[1])\n\n        expect(getComboboxInput()).toHaveValue('b')\n      })\n    )\n\n    it(\n      'selecting an option puts the display value into Combobox.Input when displayValue is provided',\n      suppressConsoleLogs(async () => {\n        let Example = defineComponent({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput :displayValue=\"(str) => str?.toUpperCase() ?? ''\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        renderTemplate(Example)\n\n        await click(getComboboxButton())\n\n        assertComboboxList({ state: ComboboxState.Visible })\n\n        await click(getComboboxOptions()[1])\n\n        expect(getComboboxInput()).toHaveValue('B')\n      })\n    )\n\n    // This really is a bug in Vue but we have a workaround for it\n    it(\n      'selecting an option puts the display value into Combobox.Input when displayValue is provided (when v-model is undefined)',\n      suppressConsoleLogs(async () => {\n        let Example = defineComponent({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput :displayValue=\"(str) => str?.toUpperCase() ?? ''\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(undefined) }),\n        })\n\n        renderTemplate(Example)\n\n        // Focus the input\n        await focus(getComboboxInput())\n\n        // Type in it\n        await type(word('A'), getComboboxInput())\n\n        // Stop typing (and clear the input)\n        await press(Keys.Escape, getComboboxInput())\n\n        // Focus the body (so the input loses focus)\n        await focus(document.body)\n\n        expect(getComboboxInput()).toHaveValue('')\n      })\n    )\n\n    it('conditionally rendering the input should allow changing the display value', async () => {\n      let Example = defineComponent({\n        template: html`\n          <Combobox v-model=\"value\" v-slot=\"{ open }\" nullable>\n            <ComboboxInput\n              :displayValue=\"(str) => (str?.toUpperCase() ?? '') + (suffix ? ' with suffix' : ' no suffix')\"\n            />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              <ComboboxOption value=\"b\">Option B</ComboboxOption>\n              <ComboboxOption value=\"c\">Option C</ComboboxOption>\n            </ComboboxOptions>\n            <button @click=\"suffix = !suffix\">Toggle suffix</button>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null), suffix: ref(false) }),\n      })\n\n      renderTemplate(Example)\n\n      await nextFrame()\n\n      expect(getComboboxInput()).toHaveValue(' no suffix')\n\n      await click(getComboboxButton())\n\n      expect(getComboboxInput()).toHaveValue(' no suffix')\n\n      await click(getComboboxOptions()[1])\n\n      expect(getComboboxInput()).toHaveValue('B no suffix')\n\n      await click(getByText('Toggle suffix'))\n\n      expect(getComboboxInput()).toHaveValue('B with suffix')\n\n      await click(getComboboxButton())\n\n      expect(getComboboxInput()).toHaveValue('B with suffix')\n\n      await click(getComboboxOptions()[0])\n\n      expect(getComboboxInput()).toHaveValue('A with suffix')\n    })\n\n    it(\n      'should be possible to override the `type` on the input',\n      suppressConsoleLogs(async () => {\n        let Example = defineComponent({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput type=\"search\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        renderTemplate(Example)\n\n        expect(getComboboxInput()).toHaveAttribute('type', 'search')\n      })\n    )\n\n    xit(\n      'should reflect the value in the input when the value changes and when you are typing',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\" v-slot=\"{ open }\">\n              <ComboboxInput :displayValue=\"person => displayValue(person, open)\" />\n\n              <ComboboxButton />\n\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({\n            value: ref('bob'),\n            displayValue(person: string, open: boolean) {\n              return `${person ?? ''} - ${open ? 'open' : 'closed'}`\n            },\n          }),\n        })\n\n        await nextFrame()\n\n        // Check for proper state sync\n        expect(getComboboxInput()).toHaveValue('bob - closed')\n        await click(getComboboxButton())\n        expect(getComboboxInput()).toHaveValue('bob - open')\n        await click(getComboboxButton())\n        expect(getComboboxInput()).toHaveValue('bob - closed')\n\n        // Check if we can still edit the input\n        for (let _ of Array(' - closed'.length)) {\n          await press(Keys.Backspace, getComboboxInput())\n        }\n        getComboboxInput()?.select()\n        await type(word('alice'), getComboboxInput())\n        expect(getComboboxInput()).toHaveValue('alice')\n\n        // Open the combobox and choose an option\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie - closed')\n      })\n    )\n\n    it(\n      'should move the caret to the end of the input when syncing the value',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox>\n              <ComboboxInput />\n              <ComboboxButton />\n\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n        })\n\n        await nextFrame()\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Choose charlie\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie')\n\n        // Ensure the selection is in the correct position\n        expect(getComboboxInput()?.selectionStart).toBe('charlie'.length)\n        expect(getComboboxInput()?.selectionEnd).toBe('charlie'.length)\n      })\n    )\n\n    // Skipped because JSDOM doesn't implement this properly: https://github.com/jsdom/jsdom/issues/3524\n    xit(\n      'should not move the caret to the end of the input when syncing the value if a custom selection is made',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox>\n              <ComboboxInput />\n              <ComboboxButton />\n\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n        })\n\n        await nextFrame()\n\n        // Open the combobox\n        await click(getComboboxButton())\n\n        // Choose charlie\n        await click(getComboboxOptions()[2])\n        expect(getComboboxInput()).toHaveValue('charlie')\n\n        // Ensure the selection is in the correct position\n        expect(getComboboxInput()?.selectionStart).toBe(0)\n        expect(getComboboxInput()?.selectionEnd).toBe('charlie'.length)\n      })\n    )\n  })\n\n  describe('ComboboxLabel', () => {\n    it(\n      'should be possible to render a ComboboxLabel using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxLabel v-slot=\"data\">{{JSON.stringify(data)}}</ComboboxLabel>\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-3' },\n        })\n        assertComboboxLabel({\n          attributes: { id: 'headlessui-combobox-label-1' },\n          textContent: JSON.stringify({ open: false, disabled: false }),\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxLabel({\n          attributes: { id: 'headlessui-combobox-label-1' },\n          textContent: JSON.stringify({ open: true, disabled: false }),\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n        assertComboboxLabelLinkedWithCombobox()\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to link Input/Button and Label if Label is rendered last',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton />\n              <ComboboxLabel>Label</ComboboxLabel>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        await new Promise<void>(nextTick)\n\n        assertComboboxLabelLinkedWithCombobox()\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to render a ComboboxLabel using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxLabel as=\"p\" v-slot=\"data\">{{JSON.stringify(data)}}</ComboboxLabel>\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxLabel({\n          attributes: { id: 'headlessui-combobox-label-1' },\n          textContent: JSON.stringify({ open: false, disabled: false }),\n          tag: 'p',\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n        assertComboboxLabel({\n          attributes: { id: 'headlessui-combobox-label-1' },\n          textContent: JSON.stringify({ open: true, disabled: false }),\n          tag: 'p',\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n  })\n\n  describe('ComboboxButton', () => {\n    it(\n      'should be possible to render a ComboboxButton using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton v-slot=\"data\">{{JSON.stringify(data)}}</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n          textContent: JSON.stringify({ open: false, disabled: false, value: null }),\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          attributes: { id: 'headlessui-combobox-button-2' },\n          textContent: JSON.stringify({ open: true, disabled: false, value: null }),\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a ComboboxButton using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton as=\"div\" role=\"button\" v-slot=\"data\"\n                >{{JSON.stringify(data)}}</ComboboxButton\n              >\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n          textContent: JSON.stringify({ open: false, disabled: false, value: null }),\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          attributes: { id: 'headlessui-combobox-button-2' },\n          textContent: JSON.stringify({ open: true, disabled: false, value: null }),\n        })\n        assertComboboxList({ state: ComboboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a ComboboxButton and a ComboboxLabel and see them linked together',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxLabel>Label</ComboboxLabel>\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                <ComboboxOption value=\"c\">Option C</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        await new Promise(requestAnimationFrame)\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-3' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n        assertComboboxButtonLinkedWithComboboxLabel()\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getComboboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton type=\"submit\"> Trigger </ComboboxButton>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getComboboxButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          let CustomButton = defineComponent({\n            setup: (props) => () => h('button', { ...props }),\n          })\n\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\">\n                <ComboboxInput />\n                <ComboboxButton :as=\"CustomButton\"> Trigger </ComboboxButton>\n              </Combobox>\n            `,\n            setup: () => ({\n              value: ref(null),\n              CustomButton,\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getComboboxButton()).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton as=\"div\"> Trigger </ComboboxButton>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getComboboxButton()).not.toHaveAttribute('type')\n      })\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          let CustomButton = defineComponent({\n            setup: (props) => () => h('div', props),\n          })\n\n          renderTemplate({\n            template: html`\n              <Combobox v-model=\"value\">\n                <ComboboxInput />\n                <ComboboxButton :as=\"CustomButton\"> Trigger </ComboboxButton>\n              </Combobox>\n            `,\n            setup: () => ({\n              value: ref(null),\n              CustomButton,\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getComboboxButton()).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n\n  describe('ComboboxOptions', () => {\n    it(\n      'should be possible to render ComboboxOptions using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions v-slot=\"data\">\n                <ComboboxOption value=\"a\">{{JSON.stringify(data)}}</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n\n        assertComboboxList({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n\n        assertActiveElement(getComboboxInput())\n      })\n    )\n\n    it('should be possible to always render the ComboboxOptions if we provide it a `static` prop', () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions static>\n              <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              <ComboboxOption value=\"b\">Option B</ComboboxOption>\n              <ComboboxOption value=\"c\">Option C</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Let's verify that the combobox is already there\n      expect(getComboboxInput()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the ComboboxOptions', async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions :unmount=\"false\">\n              <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              <ComboboxOption value=\"b\">Option B</ComboboxOption>\n              <ComboboxOption value=\"c\">Option C</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertComboboxList({ state: ComboboxState.InvisibleHidden })\n\n      // Let's open the combobox, to see if it is not hidden anymore\n      await click(getComboboxButton())\n\n      assertComboboxList({ state: ComboboxState.Visible })\n    })\n  })\n\n  describe('ComboboxOption', () => {\n    it(\n      'should be possible to render a ComboboxOption using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\" v-slot=\"data\">{{JSON.stringify(data)}}</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertComboboxButton({\n          state: ComboboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n        await click(getComboboxButton())\n\n        assertComboboxButton({\n          state: ComboboxState.Visible,\n          attributes: { id: 'headlessui-combobox-button-2' },\n        })\n        assertComboboxList({\n          state: ComboboxState.Visible,\n          textContent: JSON.stringify({ active: true, selected: false, disabled: false }),\n        })\n      })\n    )\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    let props = reactive({ hide: false })\n\n    renderTemplate({\n      template: html`\n        <Combobox v-model=\"value\">\n          <ComboboxInput />\n          <ComboboxButton>Trigger</ComboboxButton>\n          <ComboboxOptions>\n            <ComboboxOption value=\"a\">Option 1</ComboboxOption>\n            <ComboboxOption v-if=\"!hide\" value=\"b\">Option 2</ComboboxOption>\n            <ComboboxOption value=\"c\">Option 3</ComboboxOption>\n          </ComboboxOptions>\n        </Combobox>\n      `,\n      setup() {\n        return {\n          value: ref(null),\n          get hide() {\n            return props.hide\n          },\n        }\n      },\n    })\n\n    // Open the combobox\n    await click(getByText('Trigger'))\n\n    props.hide = true\n    await nextFrame()\n\n    props.hide = false\n    await nextFrame()\n\n    assertComboboxList({ state: ComboboxState.Visible })\n\n    let options = getComboboxOptions()\n\n    // Verify that the first combobox option is active\n    assertActiveComboboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second combobox option is active\n    assertActiveComboboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third combobox option is active\n    assertActiveComboboxOption(options[2])\n  })\n\n  it('should guarantee the order of options based on `order` when performing actions', async () => {\n    let props = reactive({ hide: false })\n\n    renderTemplate({\n      template: html`\n        <Combobox v-model=\"value\">\n          <ComboboxInput />\n          <ComboboxButton>Trigger</ComboboxButton>\n          <ComboboxOptions>\n            <ComboboxOption value=\"a\" :order=\"1\">Option 1</ComboboxOption>\n            <ComboboxOption v-if=\"!hide\" value=\"b\" :order=\"2\">Option 2</ComboboxOption>\n            <ComboboxOption value=\"c\" :order=\"3\">Option 3</ComboboxOption>\n          </ComboboxOptions>\n        </Combobox>\n      `,\n      setup() {\n        return {\n          value: ref(null),\n          get hide() {\n            return props.hide\n          },\n        }\n      },\n    })\n\n    // Open the combobox\n    await click(getByText('Trigger'))\n\n    props.hide = true\n    await nextFrame()\n\n    props.hide = false\n    await nextFrame()\n\n    assertComboboxList({ state: ComboboxState.Visible })\n\n    let options = getComboboxOptions()\n\n    // Verify that the first combobox option is active\n    assertActiveComboboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second combobox option is active\n    assertActiveComboboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third combobox option is active\n    assertActiveComboboxOption(options[2])\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Combobox name=\"assignee\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose charlie\n      await click(getComboboxOptions()[2])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should expose the value via the render prop', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Combobox name=\"assignee\" v-slot=\"{ value }\">\n              <div data-testid=\"value\">{{value}}</div>\n              <ComboboxInput />\n              <ComboboxButton v-slot=\"{ value }\">\n                Trigger\n                <div data-testid=\"value-2\">{{value}}</div>\n              </ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n      expect(document.querySelector('[data-testid=\"value\"]')).toHaveTextContent('alice')\n      expect(document.querySelector('[data-testid=\"value-2\"]')).toHaveTextContent('alice')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose charlie\n      await click(getComboboxOptions()[2])\n      expect(document.querySelector('[data-testid=\"value\"]')).toHaveTextContent('charlie')\n      expect(document.querySelector('[data-testid=\"value-2\"]')).toHaveTextContent('charlie')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Combobox name=\"assignee\" defaultValue=\"bob\">\n              <ComboboxInput />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n                <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n                <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n    })\n\n    it(\n      'should be possible to reset to the default value if the form is reset',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <Combobox name=\"assignee\" defaultValue=\"bob\">\n                <ComboboxButton v-slot=\"{ value }\">{{ value ?? 'Trigger' }}</ComboboxButton>\n                <ComboboxInput :displayValue=\"(value) => value\" />\n                <ComboboxOptions>\n                  <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n                  <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n                  <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n                </ComboboxOptions>\n              </Combobox>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Open combobox\n        await click(getComboboxButton())\n\n        // Choose alice\n        await click(getComboboxOptions()[0])\n        expect(getComboboxButton()).toHaveTextContent('alice')\n        expect(getComboboxInput()).toHaveValue('alice')\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // The combobox should be reset to bob\n        expect(getComboboxButton()).toHaveTextContent('bob')\n        expect(getComboboxInput()).toHaveValue('bob')\n\n        // Open combobox\n        await click(getComboboxButton())\n        assertActiveComboboxOption(getComboboxOptions()[1])\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset (using objects)',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <Combobox name=\"assignee\" :defaultValue=\"{ id: 2, name: 'bob', label: 'Bob' }\" by=\"id\">\n                <ComboboxButton v-slot=\"{ value }\">{{ value ?? 'Trigger' }}</ComboboxButton>\n                <ComboboxInput :displayValue=\"(value) => value.name\" />\n                <ComboboxOptions>\n                  <ComboboxOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                    {{ person.label }}\n                  </ComboboxOption>\n                <ComboboxOptions>\n              </Combobox>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            data,\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n\n        // Open combobox\n        await click(getComboboxButton())\n\n        // Choose alice\n        await click(getComboboxOptions()[0])\n        expect(getComboboxButton()).toHaveTextContent('alice')\n        expect(getComboboxInput()).toHaveValue('alice')\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // The combobox should be reset to bob\n        expect(getComboboxButton()).toHaveTextContent('bob')\n        expect(getComboboxInput()).toHaveValue('bob')\n\n        // Open combobox\n        await click(getComboboxButton())\n        assertActiveComboboxOption(getComboboxOptions()[1])\n      })\n    )\n\n    it('should be possible to reset to the default value in multiple mode', async () => {\n      let data = ['alice', 'bob', 'charlie']\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Combobox name=\"assignee\" :defaultValue=\"['bob']\" multiple>\n              <ComboboxButton v-slot=\"{ value }\"\n                >{{ value.join(', ') || 'Trigger' }}</ComboboxButton\n              >\n              <ComboboxOptions>\n                <ComboboxOption v-for=\"person in data\" :key=\"person\" :value=\"person\">\n                  {{ person }}\n                </ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n            <button id=\"submit\">submit</button>\n            <button type=\"reset\" id=\"reset\">reset</button>\n          </form>\n        `,\n        setup: () => ({\n          data,\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n\n      await click(document.getElementById('reset'))\n      await click(document.getElementById('submit'))\n\n      // Bob is still the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <Combobox name=\"assignee\" @update:modelValue=\"handleChange\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">Alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">Bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">Charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({\n          handleChange,\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose alice\n      await click(getComboboxOptions()[0])\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Choose bob\n      await click(getComboboxOptions()[1])\n\n      // Change handler should have been called twice\n      expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n      expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n    })\n  })\n\n  it(\n    'should be possible to use a custom component using the `as` prop without crashing',\n    suppressConsoleLogs(async () => {\n      let CustomComponent = defineComponent({\n        template: html`<button><slot /></button>`,\n      })\n\n      renderTemplate({\n        template: html`\n          <Combobox name=\"assignee\">\n            <ComboboxInput />\n            <ComboboxButton />\n            <ComboboxOptions>\n              <ComboboxOption :as=\"CustomComponent\" value=\"alice\">Alice</RadioGroupOption>\n              <ComboboxOption :as=\"CustomComponent\" value=\"bob\">Bob</RadioGroupOption>\n              <ComboboxOption :as=\"CustomComponent\" value=\"charlie\">Charlie</RadioGroupOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ CustomComponent }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n    })\n  )\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to swap the Combobox option with a button for example',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption as=\"button\" value=\"a\"> Option A </ComboboxOption>\n              <ComboboxOption as=\"button\" value=\"b\"> Option B </ComboboxOption>\n              <ComboboxOption as=\"button\" value=\"c\"> Option C </ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify options are buttons now\n      getComboboxOptions().forEach((option) => assertComboboxOption(option, { tag: 'button' }))\n    })\n  )\n\n  it(\n    'should mark all the elements between Combobox.Options and Combobox.Option with role none',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton />\n            <div class=\"outer\">\n              <ComboboxOptions>\n                <div class=\"inner py-1\">\n                  <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                  <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                </div>\n                <div class=\"inner py-1\">\n                  <ComboboxOption value=\"c\">Option C</ComboboxOption>\n                  <ComboboxOption value=\"d\">\n                    <div>\n                      <div class=\"outer\">Option D</div>\n                    </div>\n                  </ComboboxOption>\n                </div>\n                <div class=\"inner py-1\">\n                  <form class=\"inner\">\n                    <ComboboxOption value=\"e\">Option E</ComboboxOption>\n                  </form>\n                </div>\n              </ComboboxOptions>\n            </div>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      expect.hasAssertions()\n\n      document.querySelectorAll('.outer').forEach((element) => {\n        expect(element).not.toHaveAttribute('role', 'none')\n      })\n\n      document.querySelectorAll('.inner').forEach((element) => {\n        expect(element).toHaveAttribute('role', 'none')\n      })\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  let OpenClosedWrite = defineComponent({\n    props: { open: { type: Boolean } },\n    setup(props, { slots }) {\n      useOpenClosedProvider(ref(props.open ? State.Open : State.Closed))\n      return () => slots.default?.()\n    },\n  })\n\n  let OpenClosedRead = defineComponent({\n    emits: ['read'],\n    setup(_, { slots, emit }) {\n      let state = useOpenClosed()\n      watch([state], ([value]) => emit('read', value))\n      return () => slots.default?.()\n    },\n  })\n\n  it(\n    'should always open the ComboboxOptions because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Combobox>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <OpenClosedWrite :open=\"true\">\n              <ComboboxOptions v-slot=\"data\"> {{JSON.stringify(data)}} </ComboboxOptions>\n            </OpenClosedWrite>\n          </Combobox>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the combobox is visible\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      // Let's try and open the combobox\n      await click(getComboboxButton())\n\n      // Verify the combobox is still visible\n      assertComboboxList({ state: ComboboxState.Visible })\n    })\n  )\n\n  it(\n    'should always close the ComboboxOptions because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Combobox>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <OpenClosedWrite :open=\"false\">\n              <ComboboxOptions v-slot=\"data\"> {{JSON.stringify(data)}} </ComboboxOptions>\n            </OpenClosedWrite>\n          </Combobox>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the combobox is hidden\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Let's try and open the combobox\n      await click(getComboboxButton())\n\n      // Verify the combobox is still hidden\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to read the OpenClosed state',\n    suppressConsoleLogs(async () => {\n      let readFn = jest.fn()\n      renderTemplate({\n        components: { OpenClosedRead },\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <OpenClosedRead @read=\"readFn\">\n              <ComboboxOptions>\n                <ComboboxOption value=\"a\">Option A</ComboboxOption>\n              </ComboboxOptions>\n            </OpenClosedRead>\n          </Combobox>\n        `,\n        setup() {\n          return { value: ref(null), readFn }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the combobox is hidden\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Let's toggle the combobox 3 times\n      await click(getComboboxButton())\n      await click(getComboboxButton())\n      await click(getComboboxButton())\n\n      // Verify the combobox is visible\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      expect(readFn).toHaveBeenCalledTimes(3)\n      expect(readFn).toHaveBeenNthCalledWith(1, State.Open)\n      expect(readFn).toHaveBeenNthCalledWith(2, State.Closed)\n      expect(readFn).toHaveBeenNthCalledWith(3, State.Open)\n    })\n  )\n})\n\n// TODO: Re-enable virtual tests once we migrated from `npm` to `pnpm` and\n// rolled back the `@tanstack/virtual-vue` version.\n//\n// We had to bump `@tanstack/virtual-vue` such that the `@tanstack/virtual-core`\n// version was the same _and_ hoisted such that we could write a patch for it.\n// Different versions meant that the `@tanstack/virtual-core` version was\n// embedded in\n// `node_modules/@tanstack/virtual-react/node_modules/@tanstack/virtual-core`\n// which wasn't patchable via patch-package. Pnpm will solve this.\ndescribe.each([{ virtual: false }, { virtual: false }])(\n  'Keyboard interactions %s',\n  ({ virtual }) => {\n    let data = ['Option A', 'Option B', 'Option C']\n    let MyCombobox = defineComponent({\n      components: getDefaultComponents(),\n      template: html`\n        <Combobox v-if=\"virtual !== null\" :virtual=\"virtual\" v-model=\"value\" v-bind=\"comboboxProps\">\n          <ComboboxInput v-bind=\"inputProps\" />\n          <ComboboxButton v-bind=\"buttonProps\">Trigger</ComboboxButton>\n          <ComboboxOptions v-if=\"useComboboxOptions\" v-slot=\"{ option }\">\n            <ComboboxOption v-bind=\"optionProps\" :value=\"option\">\n              {{ option?.children ?? option }}\n            </ComboboxOption>\n          </ComboboxOptions>\n        </Combobox>\n\n        <Combobox v-if=\"virtual === null\" v-model=\"value\" v-bind=\"comboboxProps\">\n          <ComboboxInput v-bind=\"inputProps\" />\n          <ComboboxButton v-bind=\"buttonProps\">Trigger</ComboboxButton>\n          <ComboboxOptions v-if=\"useComboboxOptions\">\n            <ComboboxOption\n              v-for=\"(option, idx) in options\"\n              :key=\"idx\"\n              :disabled=\"isDisabled(option)\"\n              v-bind=\"optionProps\"\n              :value=\"option\"\n            >\n              {{ option?.children ?? option }}\n            </ComboboxOption>\n          </ComboboxOptions>\n        </Combobox>\n      `,\n\n      props: {\n        options: { default: data.slice() },\n        useComboboxOptions: { default: true },\n        comboboxProps: {},\n        inputProps: { default: {} },\n        buttonProps: { default: {} },\n        optionProps: { default: {} },\n      },\n\n      setup(props) {\n        // @ts-expect-error\n        let { value = 'test', update, ...comboboxProps } = props.comboboxProps ?? {}\n        function isDisabled(option: any) {\n          return typeof option === 'string'\n            ? false\n            : typeof option === 'object' && option !== null && 'disabled' in option\n              ? option?.disabled ?? false\n              : false\n        }\n\n        let model = ref(value)\n        watch([model], () => update?.(model.value))\n\n        return {\n          value: model,\n          comboboxProps,\n          isDisabled,\n          virtual: computed(() => {\n            return virtual ? { options: props.options, disabled: isDisabled } : null\n          }),\n        }\n      },\n    })\n\n    describe('Button', () => {\n      describe('`Enter` key', () => {\n        it(\n          'should be possible to open the Combobox with Enter',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option, { selected: false }))\n\n            assertActiveComboboxOption(options[0])\n            assertNoSelectedComboboxOption()\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with Enter when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ disabled: true }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Try to focus the button\n            getComboboxButton()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.Enter)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option (when using the `hidden` render strategy)',\n          suppressConsoleLogs(async () => {\n            if (virtual) return // Incompatible with virtual rendering\n\n            renderTemplate({\n              template: html`\n                <Combobox v-model=\"value\">\n                  <ComboboxInput />\n                  <ComboboxButton>Trigger</ComboboxButton>\n                  <ComboboxOptions :unmount=\"false\">\n                    <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                    <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                    <ComboboxOption value=\"c\">Option C</ComboboxOption>\n                  </ComboboxOptions>\n                </Combobox>\n              `,\n              setup: () => ({ value: ref('b'), virtual }),\n            })\n\n            await new Promise<void>(nextTick)\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleHidden,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleHidden })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            let options = getComboboxOptions()\n\n            // Hover over Option A\n            await mouseMove(options[0])\n\n            // Verify that Option A is active\n            assertActiveComboboxOption(options[0])\n\n            // Verify that Option B is still selected\n            assertComboboxOption(options[1], { selected: true })\n\n            // Close/Hide the combobox\n            await press(Keys.Escape)\n\n            // Re-open the combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Enter, and focus the selected option (with a list of objects)',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Enter)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Space` key', () => {\n        it(\n          'should be possible to open the combobox with Space',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Space)\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with Space when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null, disabled: true }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.Space)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with Space, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({\n              state: ComboboxState.InvisibleUnmounted,\n            })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Space)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({\n              state: ComboboxState.InvisibleUnmounted,\n            })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Space)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should have no active combobox option upon Space key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'alice', children: 'alice', disabled: true },\n                  { value: 'bob', children: 'bob', disabled: true },\n                  { value: 'charlie', children: 'charlie', disabled: true },\n                ],\n              }),\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({\n              state: ComboboxState.InvisibleUnmounted,\n            })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.Space)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Escape` key', () => {\n        it(\n          'should be possible to close an open combobox with Escape',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Re-focus the button\n            getComboboxButton()?.focus()\n            assertActiveElement(getComboboxButton())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify the input is focused again\n            assertActiveElement(getComboboxInput())\n          })\n        )\n\n        it(\n          'should not propagate the Escape event when the combobox is open',\n          suppressConsoleLogs(async () => {\n            let handleKeyDown = jest.fn()\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            window.addEventListener('keydown', handleKeyDown)\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // We should never see the Escape event\n            expect(handleKeyDown).toHaveBeenCalledTimes(0)\n\n            window.removeEventListener('keydown', handleKeyDown)\n          })\n        )\n\n        it(\n          'should propagate the Escape event when the combobox is closed',\n          suppressConsoleLogs(async () => {\n            let handleKeyDown = jest.fn()\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            window.addEventListener('keydown', handleKeyDown)\n\n            // Focus the input field\n            await focus(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // We should never see the Escape event\n            expect(handleKeyDown).toHaveBeenCalledTimes(1)\n\n            window.removeEventListener('keydown', handleKeyDown)\n          })\n        )\n      })\n\n      describe('`ArrowDown` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowDown',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // Verify that the first combobox option is active\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowDown when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{disabled:true}\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowDown, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`ArrowUp` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowUp and the last option should be active',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // ! ALERT: The LAST option should now be active\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowUp and the last option should be active when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ disabled: true }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowUp, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'alice', children: 'alice', disabled: false },\n                  { value: 'bob', children: 'bob', disabled: true },\n                  { value: 'charlie', children: 'charlie', disabled: true },\n                ],\n              }),\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the button\n            getComboboxButton()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n          })\n        )\n      })\n    })\n\n    describe('Input', () => {\n      describe('`Enter` key', () => {\n        it(\n          'should be possible to close the combobox with Enter and choose the active combobox option',\n          suppressConsoleLogs(async () => {\n            let handleChange = jest.fn()\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value, update }\" />`,\n              setup: () => {\n                let model = ref(null)\n                return {\n                  value: model,\n                  update: (value: any) => {\n                    model.value = value\n                    handleChange(value)\n                  },\n                }\n              },\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n\n            // Activate the first combobox option\n            let options = getComboboxOptions()\n            await mouseMove(options[0])\n\n            // Choose option, and close combobox\n            await press(Keys.Enter)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify we got the change event\n            expect(handleChange).toHaveBeenCalledTimes(1)\n            expect(handleChange).toHaveBeenCalledWith('Option A')\n\n            // Verify the button is focused again\n            assertActiveElement(getComboboxInput())\n\n            // Open combobox again\n            await click(getComboboxButton())\n\n            // Verify the active option is the previously selected one\n            assertActiveComboboxOption(getComboboxOptions()[0])\n          })\n        )\n\n        it(\n          'should submit the form on `Enter`',\n          suppressConsoleLogs(async () => {\n            let submits = jest.fn()\n\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <form @submit=\"handleSubmit\" @keyup=\"handleKeyUp\">\n                  <MyCombobox :comboboxProps=\"{ value, update, name: 'option' }\" />\n                  <button>Submit</button>\n                </form>\n              `,\n              setup() {\n                let value = ref('b')\n                return {\n                  value,\n                  update(newValue: any) {\n                    value.value = newValue\n                  },\n                  handleKeyUp(event: KeyboardEvent) {\n                    // JSDom doesn't automatically submit the form but if we can\n                    // catch an `Enter` event, we can assume it was a submit.\n                    if (event.key === 'Enter') (event.currentTarget as HTMLFormElement).submit()\n                  },\n                  handleSubmit(event: SubmitEvent) {\n                    event.preventDefault()\n                    submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n                  },\n                  virtual,\n                }\n              },\n            })\n\n            // Focus the input field\n            getComboboxInput()?.focus()\n            assertActiveElement(getComboboxInput())\n\n            // Press enter (which should submit the form)\n            await press(Keys.Enter)\n\n            // Verify the form was submitted\n            expect(submits).toHaveBeenCalledTimes(1)\n            expect(submits).toHaveBeenCalledWith([['option', 'b']])\n          })\n        )\n\n        it(\n          'should submit the form on `Enter` (when no submit button was found)',\n          suppressConsoleLogs(async () => {\n            let submits = jest.fn()\n\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <form @submit=\"handleSubmit\" @keyup=\"handleKeyUp\">\n                  <MyCombobox :comboboxProps=\"{ value, update, name: 'option' }\" />\n                </form>\n              `,\n              setup() {\n                let value = ref('b')\n                return {\n                  value,\n                  update(newValue: any) {\n                    value.value = newValue\n                  },\n                  handleKeyUp(event: KeyboardEvent) {\n                    // JSDom doesn't automatically submit the form but if we can\n                    // catch an `Enter` event, we can assume it was a submit.\n                    if (event.key === 'Enter') (event.currentTarget as HTMLFormElement).submit()\n                  },\n                  handleSubmit(event: SubmitEvent) {\n                    event.preventDefault()\n                    submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n                  },\n                  virtual,\n                }\n              },\n            })\n\n            // Focus the input field\n            getComboboxInput()?.focus()\n            assertActiveElement(getComboboxInput())\n\n            // Press enter (which should submit the form)\n            await press(Keys.Enter)\n\n            // Verify the form was submitted\n            expect(submits).toHaveBeenCalledTimes(1)\n            expect(submits).toHaveBeenCalledWith([['option', 'b']])\n          })\n        )\n      })\n\n      describe('`Tab` key', () => {\n        it(\n          'pressing Tab should select the active item and move to the next DOM node',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <input id=\"before-combobox\" />\n                <MyCombobox :comboboxProps=\"{ value, update }\" />\n                <input id=\"after-combobox\" />\n              `,\n              setup: () => {\n                let value = ref(null)\n                return {\n                  value,\n                  update(newValue: any) {\n                    value.value = newValue\n                  },\n                }\n              },\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Select the 2nd option\n            await press(Keys.ArrowDown)\n\n            // Tab to the next DOM node\n            await press(Keys.Tab)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // That the selected value was the highlighted one\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // And focus has moved to the next element\n            assertActiveElement(document.querySelector('#after-combobox'))\n          })\n        )\n\n        it(\n          'pressing Shift+Tab should select the active item and move to the previous DOM node',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <input id=\"before-combobox\" />\n                <MyCombobox :comboboxProps=\"{ value, update }\" />\n                <input id=\"after-combobox\" />\n              `,\n              setup: () => {\n                let value = ref(null)\n                return {\n                  value,\n                  update(newValue: any) {\n                    value.value = newValue\n                  },\n                }\n              },\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Select the 2nd option\n            await press(Keys.ArrowDown)\n\n            // Tab to the next DOM node\n            await press(shift(Keys.Tab))\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // That the selected value was the highlighted one\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // And focus has moved to the next element\n            assertActiveElement(document.querySelector('#before-combobox'))\n          })\n        )\n      })\n\n      describe('`Escape` key', () => {\n        it(\n          'should be possible to close an open combobox with Escape',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify it is closed\n            assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Verify the button is focused again\n            assertActiveElement(getComboboxInput())\n          })\n        )\n\n        it(\n          'should bubble escape when using `static` on Combobox.Options',\n          suppressConsoleLogs(async () => {\n            if (virtual) return // Incompatible with virtual rendering\n\n            renderTemplate({\n              template: html`\n                <Combobox v-model=\"value\">\n                  <ComboboxInput />\n                  <ComboboxButton>Trigger</ComboboxButton>\n                  <ComboboxOptions static>\n                    <ComboboxOption value=\"a\">Option A</ComboboxOption>\n                    <ComboboxOption value=\"b\">Option B</ComboboxOption>\n                    <ComboboxOption value=\"c\">Option C</ComboboxOption>\n                  </ComboboxOptions>\n                </Combobox>\n              `,\n              setup: () => ({ value: ref(null), virtual }),\n            })\n\n            let spy = jest.fn()\n\n            window.addEventListener(\n              'keydown',\n              (evt) => {\n                if (evt.key === 'Escape') {\n                  spy()\n                }\n              },\n              { capture: true }\n            )\n\n            window.addEventListener('keydown', (evt) => {\n              if (evt.key === 'Escape') {\n                spy()\n              }\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input is focused\n            assertActiveElement(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is still focused\n            assertActiveElement(getComboboxInput())\n\n            // The external event handler should've been called twice\n            // Once in the capture phase and once in the bubble phase\n            expect(spy).toHaveBeenCalledTimes(2)\n          })\n        )\n\n        it(\n          'should bubble escape when not using Combobox.Options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :useComboboxOptions=\"false\" />`,\n            })\n\n            let spy = jest.fn()\n\n            window.addEventListener(\n              'keydown',\n              (evt) => {\n                if (evt.key === 'Escape') {\n                  spy()\n                }\n              },\n              { capture: true }\n            )\n\n            window.addEventListener('keydown', (evt) => {\n              if (evt.key === 'Escape') {\n                spy()\n              }\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input is focused\n            assertActiveElement(getComboboxInput())\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is still focused\n            assertActiveElement(getComboboxInput())\n\n            // The external event handler should've been called twice\n            // Once in the capture phase and once in the bubble phase\n            expect(spy).toHaveBeenCalledTimes(2)\n          })\n        )\n\n        it(\n          'should sync the input field correctly and reset it when pressing Escape',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify the input has the selected value\n            expect(getComboboxInput()?.value).toBe('Option B')\n\n            // Override the input by typing something\n            await type(word('test'), getComboboxInput())\n            expect(getComboboxInput()?.value).toBe('test')\n\n            // Close combobox\n            await press(Keys.Escape)\n\n            // Verify the input is reset correctly\n            expect(getComboboxInput()?.value).toBe('Option B')\n          })\n        )\n      })\n\n      describe('`ArrowDown` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowDown',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // Verify that the first combobox option is active\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowDown when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null, disabled: true }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowDown, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go down once\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down again\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n\n            // We should NOT be able to go down again (because last option).\n            // Current implementation won't go around.\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options and skip the first disabled one',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ],\n              }),\n            })\n\n            render\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down once\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowDown to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ],\n              }),\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // Open combobox\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to go to the next item if no value is set',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // Verify that we are on the first option\n            assertActiveComboboxOption(options[0])\n\n            // Go down once\n            await press(Keys.ArrowDown)\n\n            // We should be on the next item\n            assertActiveComboboxOption(options[1])\n          })\n        )\n      })\n\n      describe('`ArrowUp` key', () => {\n        it(\n          'should be possible to open the combobox with ArrowUp and the last option should be active',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n\n            // ! ALERT: The LAST option should now be active\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should not be possible to open the combobox with ArrowUp and the last option should be active when the button is disabled',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ disabled: true }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Try to open the combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is still closed\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n          })\n        )\n\n        it(\n          'should be possible to open the combobox with ArrowUp, and focus the selected option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n            // Verify that the second combobox option is active (because it is already selected)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should have no active combobox option when there are no combobox options at all',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"[]\" />`,\n            })\n\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n            assertComboboxList({ state: ComboboxState.Visible })\n            assertActiveElement(getComboboxInput())\n\n            assertNoActiveComboboxOption()\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options and jump to the first non-disabled one',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                ],\n              }),\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to navigate up or down if there is only a single non-disabled option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                ],\n              }),\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // Going up or down should select the single available option\n            await press(Keys.ArrowUp)\n\n            // We should not be able to go up (because those are disabled)\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go down (because this is the last option)\n            await press(Keys.ArrowDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use ArrowUp to navigate the combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            assertComboboxButton({\n              state: ComboboxState.InvisibleUnmounted,\n              attributes: { id: 'headlessui-combobox-button-2' },\n            })\n            assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            // Verify it is visible\n            assertComboboxButton({ state: ComboboxState.Visible })\n            assertComboboxList({\n              state: ComboboxState.Visible,\n              attributes: { id: 'headlessui-combobox-options-3' },\n            })\n            assertActiveElement(getComboboxInput())\n            assertComboboxButtonLinkedWithCombobox()\n\n            // Verify we have combobox options\n            let options = getComboboxOptions()\n            expect(options).toHaveLength(3)\n            options.forEach((option) => assertComboboxOption(option))\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go down once\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[1])\n\n            // We should be able to go down again\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[0])\n\n            // We should NOT be able to go up again (because first option). Current implementation won't go around.\n            await press(Keys.ArrowUp)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n      })\n\n      describe('`End` key', () => {\n        it(\n          'should be possible to use the End key to go to the last combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await press(Keys.End)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the End key to go to the last non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last non-disabled option\n            await press(Keys.End)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to use the End key to go to the first combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should not be able to go to the end (no-op)\n            await press(Keys.End)\n\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon End key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.End)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`PageDown` key', () => {\n        it(\n          'should be possible to use the PageDown key to go to the last combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox />`,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await press(Keys.PageDown)\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the PageDown key to go to the last non disabled Combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: false },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Open combobox\n            await press(Keys.Space)\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last non-disabled option\n            await press(Keys.PageDown)\n            assertActiveComboboxOption(options[1])\n          })\n        )\n\n        it(\n          'should be possible to use the PageDown key to go to the first combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: false },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[0])\n\n            // We should not be able to go to the end\n            await press(Keys.PageDown)\n\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon PageDown key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.PageDown)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Home` key', () => {\n        it(\n          'should be possible to use the Home key to go to the first combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go to the first option\n            await press(Keys.Home)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to use the Home key to go to the first non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            // We should be on the first non-disabled option\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the Home key to go to the last combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[3])\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            assertActiveComboboxOption(options[3])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon Home key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.Home)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`PageUp` key', () => {\n        it(\n          'should be possible to use the PageUp key to go to the first combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n            })\n\n            // Focus the input\n            getComboboxInput()?.focus()\n\n            // Open combobox\n            await press(Keys.ArrowUp)\n\n            let options = getComboboxOptions()\n\n            // We should be on the last option\n            assertActiveComboboxOption(options[2])\n\n            // We should be able to go to the first option\n            await press(Keys.PageUp)\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to use the PageUp key to go to the first non disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: false },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We opened via click, we default to the first non-disabled option\n            assertActiveComboboxOption(options[2])\n\n            // We should not be able to go to the end (no-op — already there)\n            await press(Keys.PageUp)\n\n            assertActiveComboboxOption(options[2])\n          })\n        )\n\n        it(\n          'should be possible to use the PageUp key to go to the last combobox option if that is the only non-disabled combobox option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: false },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options = getComboboxOptions()\n\n            // We opened via click, we default to the first non-disabled option\n            assertActiveComboboxOption(options[3])\n\n            // We should not be able to go to the end (no-op — already there)\n            await press(Keys.PageUp)\n\n            assertActiveComboboxOption(options[3])\n          })\n        )\n\n        it(\n          'should have no active combobox option upon PageUp key press, when there are no non-disabled combobox options',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`<MyCombobox :options=\"options\" />`,\n              setup: () => ({\n                options: [\n                  { value: 'a', children: 'Option A', disabled: true },\n                  { value: 'b', children: 'Option B', disabled: true },\n                  { value: 'c', children: 'Option C', disabled: true },\n                  { value: 'd', children: 'Option D', disabled: true },\n                ],\n              }),\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We opened via click, we don't have an active option\n            assertNoActiveComboboxOption()\n\n            // We should not be able to go to the end\n            await press(Keys.PageUp)\n\n            assertNoActiveComboboxOption()\n          })\n        )\n      })\n\n      describe('`Backspace` key', () => {\n        it(\n          'should reset the value when the last character is removed, when in `nullable` mode',\n          suppressConsoleLogs(async () => {\n            let handleChange = jest.fn()\n            renderTemplate({\n              template: html`\n                <Combobox v-model=\"value\" nullable>\n                  <ComboboxInput />\n                  <ComboboxButton>Trigger</ComboboxButton>\n                  <ComboboxOptions>\n                    <ComboboxOption :order=\"virtual ? 1 : undefined\" value=\"alice\"\n                      >Alice</ComboboxOption\n                    >\n                    <ComboboxOption :order=\"virtual ? 1 : undefined\" value=\"bob\"\n                      >Bob</ComboboxOption\n                    >\n                    <ComboboxOption :order=\"virtual ? 1 : undefined\" value=\"charlie\"\n                      >Charlie</ComboboxOption\n                    >\n                  </ComboboxOptions>\n                </Combobox>\n              `,\n              setup: () => {\n                let value = ref('bob')\n                watch([value], () => handleChange(value.value))\n                return { value, virtual }\n              },\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // Bob should be active\n            options = getComboboxOptions()\n            expect(getComboboxInput()).toHaveValue('bob')\n            assertActiveComboboxOption(options[1])\n\n            assertActiveElement(getComboboxInput())\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('bo')\n            assertActiveComboboxOption(options[1])\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('b')\n            assertActiveComboboxOption(options[1])\n\n            // Delete a character\n            await press(Keys.Backspace)\n            expect(getComboboxInput()?.value).toBe('')\n\n            // Verify that we don't have an selected option anymore since we are in `nullable` mode\n            assertNotActiveComboboxOption(options[1])\n            assertNoSelectedComboboxOption()\n\n            // Verify that we saw the `null` change coming in\n            expect(handleChange).toHaveBeenCalledTimes(1)\n            expect(handleChange).toHaveBeenCalledWith(null)\n          })\n        )\n      })\n\n      describe('`Any` key aka search', () => {\n        let MyCombobox = defineComponent({\n          components: getDefaultComponents(),\n\n          template: html`\n            <Combobox v-if=\"!virtual\" v-model=\"value\" by=\"value\">\n              <ComboboxInput @change=\"setQuery\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions>\n                <ComboboxOption\n                  v-for=\"(person, idx) in filteredPeople\"\n                  :key=\"person.value\"\n                  v-bind=\"person\"\n                  :value=\"person\"\n                >\n                  {{ person.name }}\n                </ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n            <Combobox v-if=\"virtual\" :virtual=\"virtual\" v-model=\"value\" by=\"value\">\n              <ComboboxInput @change=\"setQuery\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <ComboboxOptions v-slot=\"{ option: person }\">\n                <ComboboxOption v-bind=\"person\" :value=\"person\">{{ person.name }}</ComboboxOption>\n              </ComboboxOptions>\n            </Combobox>\n          `,\n\n          props: {\n            people: {\n              type: Array as PropType<{ value: string; name: string; disabled: boolean }[]>,\n              required: true,\n            },\n          },\n\n          setup(props) {\n            let value = ref<string | null>(null)\n            let query = ref('')\n            let filteredPeople = computed(() => {\n              return query.value === ''\n                ? props.people\n                : props.people.filter((person) =>\n                    person.name.toLowerCase().includes(query.value.toLowerCase())\n                  )\n            })\n\n            return {\n              value,\n              query,\n              filteredPeople,\n              setQuery: (event: Event & { target: HTMLInputElement }) => {\n                query.value = event.target.value\n              },\n              virtual: computed(() => {\n                return virtual\n                  ? {\n                      options: filteredPeople.value,\n                      disabled: (person: { value: string; name: string; disabled: boolean }) =>\n                        person?.disabled ?? false,\n                    }\n                  : null\n              }),\n            }\n          },\n        })\n\n        it(\n          'should be possible to type a full word that has a perfect match',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <MyCombobox\n                  :people=\"[\n                    { value: 'alice', name: 'alice', disabled: false },\n                    { value: 'bob', name: 'bob', disabled: false },\n                    { value: 'charlie', name: 'charlie', disabled: false },\n                  ]\"\n                />\n              `,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // Verify we moved focus to the input field\n            assertActiveElement(getComboboxInput())\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bob'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('alice'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('charlie'))\n            await press(Keys.Home)\n\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to type a partial of a word',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <MyCombobox\n                  :people=\"[\n                    { value: 'alice', name: 'alice', disabled: false },\n                    { value: 'bob', name: 'bob', disabled: false },\n                    { value: 'charlie', name: 'charlie', disabled: false },\n                  ]\"\n                />\n              `,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bo'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('ali'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('char'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should be possible to type words with spaces',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <MyCombobox\n                  :people=\"[\n                    { value: 'alice', name: 'alice jones', disabled: false },\n                    { value: 'bob', name: 'bob the builder', disabled: false },\n                    { value: 'charlie', name: 'charlie bit me', disabled: false },\n                  ]\"\n                />\n              `,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            // We should be able to go to the second option\n            await type(word('bob t'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('bob the builder')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the first option\n            await type(word('alice j'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('alice jones')\n            assertActiveComboboxOption(options[0])\n\n            // We should be able to go to the last option\n            await type(word('charlie b'))\n            await press(Keys.Home)\n            options = getComboboxOptions()\n            expect(options).toHaveLength(1)\n            expect(options[0]).toHaveTextContent('charlie bit me')\n            assertActiveComboboxOption(options[0])\n          })\n        )\n\n        it(\n          'should not be possible to search and activate a disabled option',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <MyCombobox\n                  :people=\"[\n                    { value: 'alice', name: 'alice', disabled: false },\n                    { value: 'bob', name: 'bob', disabled: true },\n                    { value: 'charlie', name: 'charlie', disabled: false },\n                  ]\"\n                />\n              `,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            // We should not be able to go to the disabled option\n            await type(word('bo'))\n            await press(Keys.Home)\n\n            assertNoActiveComboboxOption()\n            assertNoSelectedComboboxOption()\n          })\n        )\n\n        it(\n          'should maintain activeIndex and activeOption when filtering',\n          suppressConsoleLogs(async () => {\n            renderTemplate({\n              components: { MyCombobox },\n              template: html`\n                <MyCombobox\n                  :people=\"[\n                    { value: 'a', name: 'person a', disabled: false },\n                    { value: 'b', name: 'person b', disabled: false },\n                    { value: 'c', name: 'person c', disabled: false },\n                  ]\"\n                />\n              `,\n            })\n\n            // Open combobox\n            await click(getComboboxButton())\n\n            let options: ReturnType<typeof getComboboxOptions>\n\n            options = getComboboxOptions()\n            expect(options[0]).toHaveTextContent('person a')\n            assertActiveComboboxOption(options[0])\n\n            await press(Keys.ArrowDown)\n\n            // Person B should be active\n            options = getComboboxOptions()\n            expect(options[1]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[1])\n\n            // Filter more, remove `person a`\n            await type(word('person b'))\n            options = getComboboxOptions()\n            expect(options[0]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[0])\n\n            // Filter less, insert `person a` before `person b`\n            await type(word('person'))\n            options = getComboboxOptions()\n            expect(options[1]).toHaveTextContent('person b')\n            assertActiveComboboxOption(options[1])\n          })\n        )\n      })\n    })\n\n    it(\n      'should sync the active index properly',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Combobox v-model=\"value\" v-slot=\"{ activeIndex }\">\n              <ComboboxInput @input=\"filter\" />\n              <ComboboxButton>Trigger</ComboboxButton>\n              <span data-test=\"idx\">{{ activeIndex }}</span>\n              <ComboboxOptions>\n                <ComboboxOption v-for=\"(option, idx) in options\" :value=\"option\" :key=\"option\"\n                  >{{ option }}</ComboboxOption\n                >\n              </ComboboxOptions>\n            </Combobox>\n          `,\n          setup: () => {\n            let value = ref(null)\n            let options = ref(['Option A', 'Option B', 'Option C', 'Option D'])\n\n            let query = ref('')\n            let filteredOptions = computed(() => {\n              return query.value === ''\n                ? options.value\n                : options.value.filter((option) => option.includes(query.value))\n            })\n\n            function filter(event: Event & { target: HTMLInputElement }) {\n              query.value = event.target.value\n            }\n\n            return { value, options: filteredOptions, filter, virtual }\n          },\n        })\n\n        // Open combobox\n        await click(getComboboxButton())\n\n        let activeIndexEl = document.querySelector('[data-test=\"idx\"]')\n        function activeIndex() {\n          return Number(activeIndexEl?.innerHTML)\n        }\n\n        expect(activeIndex()).toEqual(0)\n\n        let options: ReturnType<typeof getComboboxOptions>\n\n        await focus(getComboboxInput())\n        await type(word('Option B'))\n\n        // Option B should be active\n        options = getComboboxOptions()\n        expect(options[activeIndex()]).toHaveTextContent('Option B')\n        assertActiveComboboxOption(options[activeIndex()])\n        await press(Keys.Enter)\n\n        // Reveal all options again\n        await type(word('Option'))\n\n        // Option B should still be active\n        options = getComboboxOptions()\n        expect(options[activeIndex()]).toHaveTextContent('Option B')\n        assertActiveComboboxOption(options[activeIndex()])\n      })\n    )\n  }\n)\n\n// TODO: Re-enable virtual tests once we migrated from `npm` to `pnpm` and\n// rolled back the `@tanstack/virtual-vue` version.\n//\n// We had to bump `@tanstack/virtual-vue` such that the `@tanstack/virtual-core`\n// version was the same _and_ hoisted such that we could write a patch for it.\n// Different versions meant that the `@tanstack/virtual-core` version was\n// embedded in\n// `node_modules/@tanstack/virtual-react/node_modules/@tanstack/virtual-core`\n// which wasn't patchable via patch-package. Pnpm will solve this.\ndescribe.each([{ virtual: false }, { virtual: false }])('Mouse interactions %s', ({ virtual }) => {\n  let data = ['Option A', 'Option B', 'Option C']\n  let MyCombobox = defineComponent({\n    components: getDefaultComponents(),\n    template: html`\n      <Combobox\n        :nullable\n        v-if=\"virtual !== null\"\n        :virtual=\"virtual\"\n        v-model=\"value\"\n        v-bind=\"comboboxProps\"\n      >\n        <ComboboxLabel v-if=\"label\">Label</ComboboxLabel>\n        <ComboboxInput v-bind=\"inputProps\" />\n        <ComboboxButton v-bind=\"buttonProps\">Trigger</ComboboxButton>\n        <ComboboxOptions v-if=\"useComboboxOptions\" v-bind=\"optionsProps\" v-slot=\"{ option }\">\n          <ComboboxOption v-bind=\"optionProps\" :value=\"option\">\n            {{ option?.children ?? option }}\n          </ComboboxOption>\n        </ComboboxOptions>\n      </Combobox>\n\n      <Combobox v-if=\"virtual === null\" v-model=\"value\" v-bind=\"comboboxProps\">\n        <ComboboxLabel v-if=\"label\">Label</ComboboxLabel>\n        <ComboboxInput v-bind=\"inputProps\" />\n        <ComboboxButton v-bind=\"buttonProps\">Trigger</ComboboxButton>\n        <ComboboxOptions v-if=\"useComboboxOptions\" v-bind=\"optionsProps\">\n          <ComboboxOption\n            v-for=\"(option, idx) in options\"\n            :key=\"idx\"\n            :disabled=\"isDisabled(option)\"\n            v-bind=\"optionProps\"\n            :value=\"option\"\n          >\n            {{ option?.children ?? option }}\n          </ComboboxOption>\n        </ComboboxOptions>\n      </Combobox>\n    `,\n\n    props: {\n      options: { default: data.slice() },\n      label: { default: true },\n      useComboboxOptions: { default: true },\n      comboboxProps: {},\n      inputProps: { default: {} },\n      buttonProps: { default: {} },\n      optionProps: { default: {} },\n      optionsProps: { default: {} },\n    },\n\n    setup(props) {\n      // @ts-expect-error\n      let { value = 'test', update, ...comboboxProps } = props.comboboxProps ?? {}\n      function isDisabled(option: any) {\n        return typeof option === 'string'\n          ? false\n          : typeof option === 'object' && option !== null && 'disabled' in option\n            ? option?.disabled ?? false\n            : false\n      }\n\n      let model = ref(value)\n      watch([model], () => update?.(model.value))\n\n      return {\n        value: model,\n        comboboxProps,\n        isDisabled,\n        virtual: computed(() => {\n          return virtual ? { options: props.options, disabled: isDisabled } : null\n        }),\n      }\n    },\n  })\n\n  it(\n    'should focus the ComboboxButton when we click the ComboboxLabel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n      })\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getComboboxLabel())\n\n      // Ensure that the actual button is focused instead\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should not focus the ComboboxInput when we right click the ComboboxLabel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ value: null }\" />`,\n      })\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getComboboxLabel(), MouseButton.Right)\n\n      // Ensure that the body is still active\n      assertActiveElement(document.body)\n    })\n  )\n\n  it(\n    'should be possible to open the combobox by focusing the input with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :label=\"false\" :comboboxProps=\"{ immediate: true }\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({\n        state: ComboboxState.Visible,\n        attributes: { id: 'headlessui-combobox-options-3' },\n      })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertComboboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox by focusing the input with immediate mode disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-3' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is invisible\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-options-3' },\n      })\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox by focusing the input with immediate mode enabled when button is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ immediate: true, disabled: true }\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-3' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Focus the input\n      await focus(getComboboxInput())\n\n      // Verify it is invisible\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-options-3' },\n      })\n    })\n  )\n\n  it(\n    'should be possible to close a combobox on click with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ immediate: true }\" />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to close a focused combobox on click with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ immediate: true }\" />`,\n      })\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox by focusing input\n      await focus(getComboboxInput())\n      assertActiveElement(getComboboxInput())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to open the combobox on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :label=\"false\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({\n        state: ComboboxState.Visible,\n        attributes: { id: 'headlessui-combobox-options-3' },\n      })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertComboboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox on right click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :label=\"false\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Try to open the combobox\n      await click(getComboboxButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open the combobox on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox\n          :comboboxProps=\"{ value: null, disabled: true }\"\n          :label=\"false\"\n        />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Try to open the combobox\n      await click(getComboboxButton())\n\n      // Verify it is still closed\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to open the combobox on click, and focus the selected option',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ value: 'Option B' }\" :label=\"false\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n      assertComboboxList({\n        state: ComboboxState.Visible,\n        attributes: { id: 'headlessui-combobox-options-3' },\n      })\n      assertActiveElement(getComboboxInput())\n      assertComboboxButtonLinkedWithCombobox()\n\n      // Verify we have combobox options\n      let options = getComboboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option, i) => assertComboboxOption(option, { selected: i === 1 }))\n\n      // Verify that the second combobox option is active (because it is already selected)\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to close a combobox on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify it is visible\n      assertComboboxButton({ state: ComboboxState.Visible })\n\n      // Click to close\n      await click(getComboboxButton())\n\n      // Verify it is closed\n      assertComboboxButton({ state: ComboboxState.InvisibleUnmounted })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be a no-op when we click outside of a closed combobox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Verify that the window is closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Click something that is not related to the combobox\n      await click(document.body)\n\n      // Should still be closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  // TODO: JSDOM doesn't quite work here\n  // Clicking outside on the body should fire a mousedown (which it does) and then change the active element (which it doesn't)\n  xit(\n    'should be possible to click outside of the combobox which should close the combobox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`\n          <MyCombobox />\n          <div tabindex=\"-1\" data-test-focusable>after</div>\n        `,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      // Click something that is not related to the combobox\n      await click(getByText('after'))\n\n      // Should be closed now\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Verify the input is focused again\n      assertActiveElement(getByText('after'))\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox on another combobox button which should close the current combobox and open the new combobox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`\n          <div>\n            <MyCombobox />\n            <MyCombobox />\n          </div>\n        `,\n      })\n\n      let [button1, button2] = getComboboxButtons()\n\n      // Click the first combobox button\n      await click(button1)\n      expect(getComboboxes()).toHaveLength(1) // Only 1 combobox should be visible\n\n      // Verify that the first input is focused\n      assertActiveElement(getComboboxInputs()[0])\n\n      // Click the second combobox button\n      await click(button2)\n\n      expect(getComboboxes()).toHaveLength(1) // Only 1 combobox should be visible\n\n      // Verify that the first input is focused\n      assertActiveElement(getComboboxInputs()[1])\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox which should close the combobox (even if we press the combobox button)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      // Click the combobox button again\n      await click(getComboboxButton())\n\n      // Should be closed now\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Verify the input is focused again\n      assertActiveElement(getComboboxInput())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the combobox, on an element which is within a focusable element, which closes the combobox',\n    suppressConsoleLogs(async () => {\n      let focusFn = jest.fn()\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`\n          <div>\n            <MyCombobox :inputProps=\"{ onFocus }\" />\n            <button id=\"btn\">\n              <span>Next</span>\n            </button>\n          </div>\n        `,\n        setup: () => ({\n          onFocus: focusFn,\n        }),\n      })\n\n      // Click the combobox button\n      await click(getComboboxButton())\n\n      // Ensure the combobox is open\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      // Click the span inside the button\n      await click(getByText('Next'))\n\n      // Ensure the combobox is closed\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      // Ensure the outside button is focused\n      assertActiveElement(document.getElementById('btn'))\n\n      // Ensure that the focus button only got focus once (first click)\n      expect(focusFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active when using `static`',\n    suppressConsoleLogs(async () => {\n      if (virtual) return // Incompatible with virtual rendering\n\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions static>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(null), virtual }),\n      })\n\n      await nextTick()\n\n      let options = getComboboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n    })\n  )\n\n  it(\n    'should make a combobox option active when you move the mouse over it',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the combobox option is already active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      await mouseMove(options[1])\n\n      // Nothing should be changed\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the combobox option is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :options=\"options\" />`,\n        setup: () => ({\n          options: [\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ],\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      await mouseMove(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to hover an option that is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :options=\"options\" />`,\n        setup: () => ({\n          options: [\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ],\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n\n      // We should not have option 1 as the active option now\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to mouse leave an option and make it inactive',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox />`,\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveComboboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNoActiveComboboxOption()\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveComboboxOption(options[0])\n\n      await mouseLeave(options[0])\n      assertNoActiveComboboxOption()\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveComboboxOption(options[2])\n\n      await mouseLeave(options[2])\n      assertNoActiveComboboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave a disabled option and be a no-op',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :options=\"options\" />`,\n        setup: () => ({\n          options: [\n            { value: 'alice', children: 'alice', disabled: false },\n            { value: 'bob', children: 'bob', disabled: true },\n            { value: 'charlie', children: 'charlie', disabled: false },\n          ],\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      let options = getComboboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n      assertNotActiveComboboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to click a combobox option, which closes the combobox',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ value, update }\" />`,\n        setup: () => {\n          let value = ref(null)\n          return {\n            value,\n            update(newValue: any) {\n              value.value = newValue\n              handleChange(newValue)\n            },\n          }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n      expect(handleChange).toHaveBeenCalledTimes(1)\n      expect(handleChange).toHaveBeenCalledWith('Option B')\n\n      // Verify the input is focused again\n      assertActiveElement(getComboboxInput())\n\n      // Open combobox again\n      await click(getComboboxButton())\n\n      // Verify the active option is the previously selected one\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n  )\n\n  it(\n    'should be possible to click a combobox option, which closes the combobox with immediate mode enabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ immediate: true }\" />`,\n      })\n\n      // Open combobox by focusing input\n      await focus(getComboboxInput())\n      assertActiveElement(getComboboxInput())\n\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click a disabled combobox option, which is a no-op',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :options=\"options\" :comboboxProps=\"{ value, update }\" />`,\n        setup: () => {\n          let value = ref(null)\n          return {\n            value,\n            options: [\n              { value: 'alice', children: 'Alice', disabled: false },\n              { value: 'bob', children: 'Bob', disabled: true },\n              { value: 'charlie', children: 'Charlie', disabled: false },\n            ],\n            update(newValue: any) {\n              value.value = newValue\n              handleChange(newValue)\n            },\n          }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should not be able to click the disabled option\n      await click(options[1])\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertNotActiveComboboxOption(options[1])\n      assertActiveElement(getComboboxInput())\n      expect(handleChange).toHaveBeenCalledTimes(0)\n\n      // Close the combobox\n      await click(getComboboxButton())\n\n      // Open combobox again\n      await click(getComboboxButton())\n\n      options = getComboboxOptions()\n\n      // Verify the active option is not the disabled one\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible focus a combobox option, so that it becomes active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :comboboxProps=\"{ value, update }\" />`,\n        setup: () => {\n          let value = ref(null)\n          return {\n            value,\n            update(newValue: any) {\n              value.value = newValue\n            },\n          }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // Verify that the first item is active\n      assertActiveComboboxOption(options[0])\n\n      // We should be able to focus the second option\n      await focus(options[1])\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to focus a combobox option which is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :options=\"options\" />`,\n        setup: () => ({\n          options: [\n            { value: 'alice', disabled: false, children: 'alice' },\n            { value: 'bob', disabled: true, children: 'bob' },\n            { value: 'charlie', disabled: false, children: 'charlie' },\n          ],\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertComboboxList({ state: ComboboxState.Visible })\n      assertActiveElement(getComboboxInput())\n\n      let options = getComboboxOptions()\n\n      // We should not be able to focus the first option\n      await focus(options[1])\n      assertNotActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to hold the last active option',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`<MyCombobox :optionsProps=\"{ hold: true }\" :label=\"false\" />`,\n      })\n\n      assertComboboxButton({\n        state: ComboboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.InvisibleUnmounted })\n\n      await click(getComboboxButton())\n\n      assertComboboxButton({\n        state: ComboboxState.Visible,\n        attributes: { id: 'headlessui-combobox-button-2' },\n      })\n      assertComboboxList({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // Hover the first item\n      await mouseMove(options[0])\n\n      // Verify that the first combobox option is active\n      assertActiveComboboxOption(options[0])\n\n      // Focus the second item\n      await mouseMove(options[1])\n\n      // Verify that the second combobox option is active\n      assertActiveComboboxOption(options[1])\n\n      // Move the mouse off of the second combobox option\n      await mouseLeave(options[1])\n      await mouseMove(document.body)\n\n      // Verify that the second combobox option is still active\n      assertActiveComboboxOption(options[1])\n    })\n  )\n\n  it(\n    'should sync the input field correctly and reset it when resetting the value from outside (to null)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-if=\"!virtual\" v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <Combobox v-if=\"virtual\" :virtual=\"virtual\" v-model=\"value\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions v-slot=\"{ option }\">\n              <ComboboxOption :value=\"option\">{{ option }}</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <button @click=\"value = null\">reset</button>\n        `,\n        setup: () => ({\n          value: ref('bob'),\n          virtual: computed(() => (virtual ? { options: ['alice', 'bob', 'charlie'] } : null)),\n        }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify the input has the selected value\n      expect(getComboboxInput()?.value).toBe('bob')\n\n      // Override the input by typing something\n      await type(word('test'), getComboboxInput())\n      expect(getComboboxInput()?.value).toBe('test')\n\n      // Reset from outside\n      await click(getByText('reset'))\n\n      // Verify the input is reset correctly\n      expect(getComboboxInput()?.value).toBe('')\n    })\n  )\n\n  it(\n    'should sync the input field correctly and reset it when resetting the value from outside (to undefined)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-if=\"virtual\" :virtual=\"virtual\" v-model=\"value\">\n            <ComboboxInput :displayValue=\"person => person?.name\" />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions v-slot=\"{ option: person }\">\n              <ComboboxOption :value=\"person\">{{ person.name }}</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <Combobox v-if=\"!virtual\" v-model=\"value\">\n            <ComboboxInput :displayValue=\"person => person?.name\" />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption v-for=\"(person, idx) in people\" :key=\"person.id\" :value=\"person\"\n                >{{ person.name }}</ComboboxOption\n              >\n            </ComboboxOptions>\n          </Combobox>\n          <button @click=\"value = null\">reset</button>\n        `,\n        setup: () => {\n          let people = [\n            { id: 1, name: 'Alice' },\n            { id: 2, name: 'Bob' },\n            { id: 3, name: 'Charlie' },\n          ]\n\n          return {\n            people,\n            value: ref(people[1]),\n            virtual: computed(() => (virtual ? { options: people } : null)),\n          }\n        },\n      })\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify the input has the selected value\n      expect(getComboboxInput()?.value).toBe('Bob')\n\n      // Override the input by typing something\n      await type(word('test'), getComboboxInput())\n      expect(getComboboxInput()?.value).toBe('test')\n\n      // Reset from outside\n      await click(getByText('reset'))\n\n      // Verify the input is reset correctly\n      expect(getComboboxInput()?.value).toBe('')\n    })\n  )\n\n  it(\n    'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)',\n    suppressConsoleLogs(async () => {\n      let people = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n      renderTemplate({\n        components: { MyCombobox },\n        template: html`\n          <MyCombobox\n            :options=\"people\"\n            :comboboxProps=\"{ value, update }\"\n            :inputProps=\"{displayValue: (person) => person?.name}\"\n          />\n          <button @click=\"value = null\">reset</button>\n        `,\n        setup: () => {\n          let value = ref(people[1])\n          return {\n            value,\n            update(newValue: any) {\n              value.value = newValue\n            },\n          }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify the input has the selected value\n      expect(getComboboxInput()?.value).toBe('Bob')\n\n      // Override the input by typing something\n      await type(word('test'), getComboboxInput())\n      expect(getComboboxInput()?.value).toBe('test')\n\n      // Reset from outside\n      await click(getByText('reset'))\n\n      // Verify the input is reset correctly\n      expect(getComboboxInput()?.value).toBe('')\n    })\n  )\n})\n\ndescribe('Multi-select', () => {\n  it(\n    'should be possible to pass multiple values to the Combobox component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify that we have an open combobox with multiple mode\n      assertCombobox({ state: ComboboxState.Visible, mode: ComboboxMode.Multiple })\n\n      // Verify that we have multiple selected combobox options\n      let options = getComboboxOptions()\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should make the first selected option the active item',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n\n      // Verify that bob is the active option\n      assertActiveComboboxOption(getComboboxOptions()[1])\n    })\n  )\n\n  it(\n    'should keep the combobox open when selecting an item via the keyboard',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      // Verify that bob is the active option\n      await click(getComboboxOptions()[0])\n\n      // Verify that the combobox is still open\n      assertCombobox({ state: ComboboxState.Visible })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"alice\">alice</ComboboxOption>\n              <ComboboxOption value=\"bob\">bob</ComboboxOption>\n              <ComboboxOption value=\"charlie\">charlie</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: false })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it (using objects instead of primitives)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption v-for=\"person in people\" :value=\"person\"\n                >{{ person.name }}</ComboboxOption\n              >\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => {\n          let people = [\n            { id: 1, name: 'alice' },\n            { id: 2, name: 'bob' },\n            { id: 3, name: 'charlie' },\n          ]\n\n          let value = ref([people[1], people[2]])\n          return { people, value }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: false })\n      assertComboboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getComboboxOptions()[1])\n\n      assertComboboxOption(options[0], { selected: false })\n      assertComboboxOption(options[1], { selected: true })\n      assertComboboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should reset the active option, if the active option gets unmounted',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Combobox v-model=\"value\" multiple>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption\n                v-for=\"user in users.filter(p => !value.includes(p))\"\n                :key=\"user\"\n                :value=\"user\"\n                >{{ user }}</ComboboxOption\n              >\n            </ComboboxOptions>\n          </Combobox>\n        `,\n        setup: () => {\n          let users = ['alice', 'bob', 'charlie']\n\n          let value = ref([])\n          return { users, value }\n        },\n      })\n\n      // Open combobox\n      await click(getComboboxButton())\n      assertCombobox({ state: ComboboxState.Visible })\n\n      let options = getComboboxOptions()\n\n      // Go to the next option\n      await press(Keys.ArrowDown)\n      assertActiveComboboxOption(options[1])\n\n      // Select the option\n      await press(Keys.Enter)\n\n      // The active option is reset to the very first one\n      assertActiveComboboxOption(options[0])\n    })\n  )\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <div>\n          <Combobox form=\"my-form\" v-model=\"value\" name=\"delivery\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"pickup\">Pickup</ComboboxOption>\n              <ComboboxOption value=\"home-delivery\">Home delivery</ComboboxOption>\n              <ComboboxOption value=\"dine-in\">Dine in</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <form id=\"my-form\" @submit=\"handleSubmit\">\n            <button>Submit</button>\n          </form>\n        </div>\n      `,\n      setup: () => {\n        let value = ref(null)\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should be possible to submit a form with a value', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <Combobox v-model=\"value\" name=\"delivery\">\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"pickup\">Pickup</ComboboxOption>\n              <ComboboxOption value=\"home-delivery\">Home delivery</ComboboxOption>\n              <ComboboxOption value=\"dine-in\">Dine in</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let value = ref(null)\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Open combobox again\n    await click(getComboboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Open combobox again\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should not submit the data if the Combobox is disabled', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <Combobox v-model=\"value\" name=\"delivery\" disabled>\n            <ComboboxInput />\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxOptions>\n              <ComboboxOption value=\"pickup\">Pickup</ComboboxOption>\n              <ComboboxOption value=\"home-delivery\">Home delivery</ComboboxOption>\n              <ComboboxOption value=\"dine-in\">Dine in</ComboboxOption>\n            </ComboboxOptions>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let value = ref('home-delivery')\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it('should be possible to submit a form with a complex value object', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <Combobox v-model=\"value\" name=\"delivery\">\n            <ComboboxButton>Trigger</ComboboxButton>\n            <ComboboxInput />\n            <ComboboxOptions>\n              <ComboboxOption v-for=\"option in options\" :key=\"option.id\" :value=\"option\"\n                >{{option.label}}</ComboboxOption\n              >\n            </ComboboxOptions>\n          </Combobox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let options = ref([\n          {\n            id: 1,\n            value: 'pickup',\n            label: 'Pickup',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 2,\n            value: 'home-delivery',\n            label: 'Home delivery',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 3,\n            value: 'dine-in',\n            label: 'Dine in',\n            extra: { info: 'Some extra info' },\n          },\n        ])\n        let value = ref(options.value[0])\n\n        return {\n          value,\n          options,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '2'],\n      ['delivery[value]', 'home-delivery'],\n      ['delivery[label]', 'Home delivery'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open combobox\n    await click(getComboboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/combobox/combobox.ts",
    "content": "import type { Virtualizer } from '@tanstack/virtual-core'\nimport { useVirtualizer } from '@tanstack/vue-virtual'\nimport {\n  Fragment,\n  cloneVNode,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  nextTick,\n  onMounted,\n  onUnmounted,\n  provide,\n  reactive,\n  ref,\n  toRaw,\n  watch,\n  watchEffect,\n  type ComputedRef,\n  type InjectionKey,\n  type PropType,\n  type Ref,\n  type UnwrapNestedRefs,\n} from 'vue'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useFrameDebounce } from '../../hooks/use-frame-debounce'\nimport { useId } from '../../hooks/use-id'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { useTreeWalker } from '../../hooks/use-tree-walker'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport { Keys } from '../../keyboard'\nimport { MouseButton } from '../../mouse'\nimport { history } from '../../utils/active-element-history'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport { disposables } from '../../utils/disposables'\nimport { dom } from '../../utils/dom'\nimport { sortByDomNode } from '../../utils/focus-management'\nimport { objectToFormEntries } from '../../utils/form'\nimport { match } from '../../utils/match'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { isMobile } from '../../utils/platform'\nimport { Features, compact, omit, render } from '../../utils/render'\n\nfunction defaultComparator<T>(a: T, z: T): boolean {\n  return a === z\n}\n\nenum ComboboxStates {\n  Open,\n  Closed,\n}\n\nenum ValueMode {\n  Single,\n  Multi,\n}\n\nenum ActivationTrigger {\n  Pointer,\n  Focus,\n  Other,\n}\n\ntype ComboboxOptionData = {\n  disabled: boolean\n  value: unknown\n  domRef: Ref<HTMLElement | null>\n  order: Ref<number | null>\n}\ntype StateDefinition = {\n  // State\n  comboboxState: Ref<ComboboxStates>\n  value: ComputedRef<unknown>\n  defaultValue: ComputedRef<unknown>\n\n  mode: ComputedRef<ValueMode>\n  nullable: ComputedRef<boolean>\n  immediate: ComputedRef<boolean>\n\n  virtual: ComputedRef<{\n    options: unknown[]\n    disabled: (value: unknown) => boolean\n  } | null>\n  calculateIndex(value: unknown): number\n  isSelected(value: unknown): boolean\n  isActive(value: unknown): boolean\n\n  compare: (a: unknown, z: unknown) => boolean\n\n  optionsPropsRef: Ref<{ static: boolean; hold: boolean }>\n\n  labelRef: Ref<HTMLLabelElement | null>\n  inputRef: Ref<HTMLInputElement | null>\n  buttonRef: Ref<HTMLButtonElement | null>\n  optionsRef: Ref<HTMLDivElement | null>\n\n  disabled: Ref<boolean>\n  options: Ref<{ id: string; dataRef: ComputedRef<ComboboxOptionData> }[]>\n  activeOptionIndex: Ref<number | null>\n  activationTrigger: Ref<ActivationTrigger>\n\n  // State mutators\n  closeCombobox(): void\n  openCombobox(): void\n  setActivationTrigger(trigger: ActivationTrigger): void\n  goToOption(focus: Focus, idx?: number, trigger?: ActivationTrigger): void\n  change(value: unknown): void\n  selectOption(id: string): void\n  selectActiveOption(): void\n  registerOption(id: string, dataRef: ComputedRef<ComboboxOptionData>): void\n  unregisterOption(id: string, active: boolean): void\n  select(value: unknown): void\n}\n\nlet ComboboxContext = Symbol('ComboboxContext') as InjectionKey<StateDefinition>\n\nfunction useComboboxContext(component: string) {\n  let context = inject(ComboboxContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Combobox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useComboboxContext)\n    throw err\n  }\n\n  return context\n}\n\n// ---\n\nlet VirtualContext = Symbol('VirtualContext') as InjectionKey<Ref<Virtualizer<any, any>> | null>\n\nlet VirtualProvider = defineComponent({\n  name: 'VirtualProvider',\n  setup(_, { slots }) {\n    let api = useComboboxContext('VirtualProvider')\n\n    let padding = computed(() => {\n      let el = dom(api.optionsRef)\n      if (!el) return { start: 0, end: 0 }\n\n      let styles = window.getComputedStyle(el)\n\n      return {\n        start: parseFloat(styles.paddingBlockStart || styles.paddingTop),\n        end: parseFloat(styles.paddingBlockEnd || styles.paddingBottom),\n      }\n    })\n\n    let virtualizer = useVirtualizer<HTMLDivElement, HTMLLIElement>(\n      // @ts-expect-error TODO: Drop when using `pnpm` and `@tanstack/virtual-vue`\n      // has been rolled back to the older version.\n      computed(() => {\n        return {\n          scrollPaddingStart: padding.value.start,\n          scrollPaddingEnd: padding.value.end,\n          count: api.virtual.value!.options.length,\n          estimateSize() {\n            return 40\n          },\n          getScrollElement() {\n            return dom(api.optionsRef)\n          },\n          overscan: 12,\n        }\n      })\n    )\n\n    let options = computed(() => api.virtual.value?.options)\n    let baseKey = ref(0)\n    watch([options], () => {\n      baseKey.value += 1\n    })\n\n    // @ts-expect-error TODO: Drop when using `pnpm` and `@tanstack/virtual-vue`\n    // has been rolled back to the older version.\n    provide(VirtualContext, api.virtual.value ? virtualizer : null)\n\n    return () => {\n      return [\n        h(\n          'div',\n          {\n            style: {\n              position: 'relative',\n              width: '100%',\n              height: `${virtualizer.value.getTotalSize()}px`,\n            },\n            ref: (el) => {\n              if (!el) {\n                return\n              }\n\n              // Do not scroll when the mouse/pointer is being used\n              if (api.activationTrigger.value === ActivationTrigger.Pointer) {\n                return\n              }\n\n              // Scroll to the active index\n              if (\n                api.activeOptionIndex.value !== null &&\n                api.virtual.value!.options.length > api.activeOptionIndex.value\n              ) {\n                virtualizer.value.scrollToIndex(api.activeOptionIndex.value)\n              }\n            },\n          },\n          virtualizer.value.getVirtualItems().map((item) => {\n            return cloneVNode(\n              slots.default!({\n                option: api.virtual.value!.options[item.index],\n                open: api.comboboxState.value === ComboboxStates.Open,\n              })![0],\n              {\n                key: `${baseKey.value}-${item.index}`,\n                'data-index': item.index,\n                'aria-setsize': api.virtual.value!.options.length,\n                'aria-posinset': item.index + 1,\n                style: {\n                  position: 'absolute',\n                  top: 0,\n                  left: 0,\n                  transform: `translateY(${item.start}px)`,\n                  overflowAnchor: 'none',\n                },\n              }\n            )\n          })\n        ),\n      ]\n    }\n  },\n})\n\n// ---\n\nexport let Combobox = defineComponent({\n  name: 'Combobox',\n  emits: { 'update:modelValue': (_value: any) => true },\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    disabled: { type: [Boolean], default: false },\n    by: { type: [String, Function], nullable: true, default: null },\n    modelValue: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n      default: undefined,\n    },\n    defaultValue: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n      default: undefined,\n    },\n    form: { type: String, optional: true },\n    name: { type: String, optional: true },\n    nullable: { type: Boolean, default: false },\n    multiple: { type: [Boolean], default: false },\n    immediate: { type: [Boolean], default: false },\n    virtual: {\n      type: Object as PropType<null | {\n        options: unknown[]\n        disabled?: (value: unknown) => boolean\n      }>,\n      default: null,\n    },\n  },\n  inheritAttrs: false,\n  setup(props, { slots, attrs, emit }) {\n    let comboboxState = ref<StateDefinition['comboboxState']['value']>(ComboboxStates.Closed)\n    let labelRef = ref<StateDefinition['labelRef']['value']>(null)\n    let inputRef = ref<StateDefinition['inputRef']['value']>(null) as StateDefinition['inputRef']\n    let buttonRef = ref<StateDefinition['buttonRef']['value']>(null) as StateDefinition['buttonRef']\n    let optionsRef = ref<StateDefinition['optionsRef']['value']>(\n      null\n    ) as StateDefinition['optionsRef']\n    let optionsPropsRef = ref<StateDefinition['optionsPropsRef']['value']>({\n      static: false,\n      hold: false,\n    }) as StateDefinition['optionsPropsRef']\n    let options = ref<StateDefinition['options']['value']>([])\n    let activeOptionIndex = ref<StateDefinition['activeOptionIndex']['value']>(null)\n    let activationTrigger = ref<StateDefinition['activationTrigger']['value']>(\n      ActivationTrigger.Other\n    )\n    let defaultToFirstOption = ref(false)\n\n    function adjustOrderedState(\n      adjustment: (\n        options: UnwrapNestedRefs<StateDefinition['options']['value']>\n      ) => UnwrapNestedRefs<StateDefinition['options']['value']> = (i) => i\n    ) {\n      let currentActiveOption =\n        activeOptionIndex.value !== null ? options.value[activeOptionIndex.value] : null\n\n      let list = adjustment(options.value.slice())\n\n      let sortedOptions =\n        list.length > 0 && list[0].dataRef.order.value !== null\n          ? // Prefer sorting based on the `order`\n            list.sort((a, z) => a.dataRef.order.value! - z.dataRef.order.value!)\n          : // Fallback to much slower DOM order\n            sortByDomNode(list, (option) => dom(option.dataRef.domRef))\n\n      // If we inserted an option before the current active option then the active option index\n      // would be wrong. To fix this, we will re-lookup the correct index.\n      let adjustedActiveOptionIndex = currentActiveOption\n        ? sortedOptions.indexOf(currentActiveOption)\n        : null\n\n      // Reset to `null` in case the currentActiveOption was removed.\n      if (adjustedActiveOptionIndex === -1) {\n        adjustedActiveOptionIndex = null\n      }\n\n      return {\n        options: sortedOptions,\n        activeOptionIndex: adjustedActiveOptionIndex,\n      }\n    }\n\n    let mode = computed(() => (props.multiple ? ValueMode.Multi : ValueMode.Single))\n    let nullable = computed(() => props.nullable)\n    let [directValue, theirOnChange] = useControllable(\n      computed(() => props.modelValue),\n      (value: unknown) => emit('update:modelValue', value),\n      computed(() => props.defaultValue)\n    )\n\n    let value = computed(() =>\n      directValue.value === undefined\n        ? match(mode.value, {\n            [ValueMode.Multi]: [],\n            [ValueMode.Single]: undefined,\n          })\n        : directValue.value\n    )\n\n    let goToOptionRaf: ReturnType<typeof requestAnimationFrame> | null = null\n    let orderOptionsRaf: ReturnType<typeof requestAnimationFrame> | null = null\n\n    function onChange(value: unknown) {\n      return match(mode.value, {\n        [ValueMode.Single]() {\n          return theirOnChange?.(value)\n        },\n        [ValueMode.Multi]: () => {\n          let copy = toRaw(api.value.value as unknown[]).slice()\n          let raw = toRaw(value)\n\n          let idx = copy.findIndex((value) => api.compare(raw, toRaw(value)))\n          if (idx === -1) {\n            copy.push(raw)\n          } else {\n            copy.splice(idx, 1)\n          }\n\n          return theirOnChange?.(copy)\n        },\n      })\n    }\n\n    let virtualOptions = computed(() => props.virtual?.options)\n    watch([virtualOptions], ([newOptions], [oldOptions]) => {\n      if (!api.virtual.value) return\n      if (!newOptions) return\n      if (!oldOptions) return\n\n      if (activeOptionIndex.value !== null) {\n        let idx = newOptions.indexOf(oldOptions[activeOptionIndex.value])\n        if (idx !== -1) {\n          activeOptionIndex.value = idx\n        } else {\n          activeOptionIndex.value = null\n        }\n      }\n    })\n\n    let api: StateDefinition = {\n      comboboxState,\n      value,\n      mode,\n      compare(a: any, z: any) {\n        if (typeof props.by === 'string') {\n          let property = props.by as unknown as any\n          return a?.[property] === z?.[property]\n        }\n\n        if (props.by === null) {\n          return defaultComparator(a, z)\n        }\n\n        return props.by(a, z)\n      },\n      calculateIndex(value: any) {\n        if (api.virtual.value) {\n          if (props.by === null) {\n            return api.virtual.value!.options.indexOf(value)\n          } else {\n            return api.virtual.value!.options.findIndex((other) => api.compare(other, value))\n          }\n        } else {\n          return options.value.findIndex((other) => api.compare(other.dataRef.value, value))\n        }\n      },\n      defaultValue: computed(() => props.defaultValue),\n      nullable,\n      immediate: computed(() => props.immediate),\n      virtual: computed(() => {\n        return props.virtual\n          ? {\n              options: props.virtual.options,\n              disabled: props.virtual.disabled ?? (() => false),\n            }\n          : null\n      }),\n      inputRef,\n      labelRef,\n      buttonRef,\n      optionsRef,\n      disabled: computed(() => props.disabled),\n      // @ts-expect-error dateRef types are incorrect due to unwrapped or wrapped refs\n      options,\n      change(value: unknown) {\n        theirOnChange(value as typeof props.modelValue)\n      },\n      activeOptionIndex: computed(() => {\n        if (\n          defaultToFirstOption.value &&\n          activeOptionIndex.value === null &&\n          (api.virtual.value ? api.virtual.value.options.length > 0 : options.value.length > 0)\n        ) {\n          if (api.virtual.value) {\n            let localActiveOptionIndex = api.virtual.value.options.findIndex(\n              (option) => !api.virtual.value?.disabled(option)\n            )\n\n            if (localActiveOptionIndex !== -1) {\n              return localActiveOptionIndex\n            }\n          }\n\n          let localActiveOptionIndex = options.value.findIndex((option) => !option.dataRef.disabled)\n          if (localActiveOptionIndex !== -1) {\n            return localActiveOptionIndex\n          }\n        }\n\n        return activeOptionIndex.value\n      }),\n      activationTrigger,\n      optionsPropsRef,\n      closeCombobox() {\n        defaultToFirstOption.value = false\n\n        if (props.disabled) return\n        if (comboboxState.value === ComboboxStates.Closed) return\n        comboboxState.value = ComboboxStates.Closed\n        activeOptionIndex.value = null\n      },\n      openCombobox() {\n        defaultToFirstOption.value = true\n\n        if (props.disabled) return\n        if (comboboxState.value === ComboboxStates.Open) return\n\n        // Check if we have a selected value that we can make active\n        if (api.value.value) {\n          let idx = api.calculateIndex(api.value.value)\n          if (idx !== -1) {\n            activeOptionIndex.value = idx\n          }\n        }\n\n        comboboxState.value = ComboboxStates.Open\n      },\n      setActivationTrigger(trigger: ActivationTrigger) {\n        activationTrigger.value = trigger\n      },\n      goToOption(focus: Focus, idx?: number, trigger?: ActivationTrigger) {\n        defaultToFirstOption.value = false\n\n        if (goToOptionRaf !== null) {\n          cancelAnimationFrame(goToOptionRaf)\n        }\n\n        goToOptionRaf = requestAnimationFrame(() => {\n          if (props.disabled) return\n          if (\n            optionsRef.value &&\n            !optionsPropsRef.value.static &&\n            comboboxState.value === ComboboxStates.Closed\n          ) {\n            return\n          }\n\n          if (api.virtual.value) {\n            activeOptionIndex.value =\n              focus === Focus.Specific\n                ? idx!\n                : calculateActiveIndex(\n                    { focus: focus as Exclude<Focus, Focus.Specific> },\n                    {\n                      resolveItems: () => api.virtual.value!.options,\n                      resolveActiveIndex: () => {\n                        return (\n                          api.activeOptionIndex.value ??\n                          api.virtual.value!.options.findIndex(\n                            (option) => !api.virtual.value?.disabled(option)\n                          ) ??\n                          null\n                        )\n                      },\n                      resolveDisabled: (item) => api.virtual.value!.disabled(item),\n                      resolveId() {\n                        throw new Error('Function not implemented.')\n                      },\n                    }\n                  )\n            activationTrigger.value = trigger ?? ActivationTrigger.Other\n            return\n          }\n\n          let adjustedState = adjustOrderedState()\n\n          // It's possible that the activeOptionIndex is set to `null` internally, but\n          // this means that we will fallback to the first non-disabled option by default.\n          // We have to take this into account.\n          if (adjustedState.activeOptionIndex === null) {\n            let localActiveOptionIndex = adjustedState.options.findIndex(\n              (option) => !option.dataRef.disabled\n            )\n\n            if (localActiveOptionIndex !== -1) {\n              adjustedState.activeOptionIndex = localActiveOptionIndex\n            }\n          }\n\n          let nextActiveOptionIndex =\n            focus === Focus.Specific\n              ? idx!\n              : calculateActiveIndex(\n                  { focus: focus as Exclude<Focus, Focus.Specific> },\n                  {\n                    resolveItems: () => adjustedState.options,\n                    resolveActiveIndex: () => adjustedState.activeOptionIndex,\n                    resolveId: (option) => option.id,\n                    resolveDisabled: (option) => option.dataRef.disabled,\n                  }\n                )\n\n          activeOptionIndex.value = nextActiveOptionIndex\n          activationTrigger.value = trigger ?? ActivationTrigger.Other\n          options.value = adjustedState.options\n        })\n      },\n      selectOption(id: string) {\n        let option = options.value.find((item) => item.id === id)\n        if (!option) return\n\n        let { dataRef } = option\n\n        onChange(dataRef.value)\n      },\n      selectActiveOption() {\n        if (api.activeOptionIndex.value === null) return\n\n        if (api.virtual.value) {\n          onChange(api.virtual.value.options[api.activeOptionIndex.value])\n        } else {\n          let { dataRef } = options.value[api.activeOptionIndex.value]\n          onChange(dataRef.value)\n        }\n\n        // It could happen that the `activeOptionIndex` stored in state is actually null,\n        // but we are getting the fallback active option back instead.\n        api.goToOption(Focus.Specific, api.activeOptionIndex.value)\n      },\n      registerOption(id: string, dataRef: ComputedRef<ComboboxOptionData>) {\n        let option = reactive({ id, dataRef }) as unknown as {\n          id: typeof id\n          dataRef: (typeof dataRef)['value']\n        }\n\n        if (api.virtual.value) {\n          options.value.push(option)\n          return\n        }\n\n        if (orderOptionsRaf) cancelAnimationFrame(orderOptionsRaf)\n\n        let adjustedState = adjustOrderedState((options) => {\n          options.push(option)\n          return options\n        })\n\n        // Check if we need to make the newly registered option active.\n        if (activeOptionIndex.value === null) {\n          if (api.isSelected(dataRef.value.value)) {\n            adjustedState.activeOptionIndex = adjustedState.options.indexOf(option)\n          }\n        }\n\n        options.value = adjustedState.options\n        activeOptionIndex.value = adjustedState.activeOptionIndex\n        activationTrigger.value = ActivationTrigger.Other\n\n        // If some of the DOM elements aren't ready yet, then we can retry in the next tick.\n        if (adjustedState.options.some((option) => !dom(option.dataRef.domRef))) {\n          orderOptionsRaf = requestAnimationFrame(() => {\n            let adjustedState = adjustOrderedState()\n            options.value = adjustedState.options\n\n            activeOptionIndex.value = adjustedState.activeOptionIndex\n          })\n        }\n      },\n      unregisterOption(id: string, active: boolean) {\n        if (goToOptionRaf !== null) {\n          cancelAnimationFrame(goToOptionRaf)\n        }\n\n        // When we are unregistering the currently active option, then we also have to make sure to\n        // reset the `defaultToFirstOption` flag, so that visually something is selected and the\n        // next time you press a key on your keyboard it will go to the proper next or previous\n        // option in the list.\n        //\n        // Since this was the active option and it could have been anywhere in the list, resetting\n        // to the very first option seems like a fine default. We _could_ be smarter about this by\n        // going to the previous / next item in list if we know the direction of the keyboard\n        // navigation, but that might be too complex/confusing from an end users perspective.\n        if (active) {\n          defaultToFirstOption.value = true\n        }\n\n        if (api.virtual.value) {\n          options.value = options.value.filter((option) => option.id !== id)\n          return\n        }\n\n        let adjustedState = adjustOrderedState((options) => {\n          let idx = options.findIndex((option) => option.id === id)\n          if (idx !== -1) options.splice(idx, 1)\n          return options\n        })\n\n        options.value = adjustedState.options\n        activeOptionIndex.value = adjustedState.activeOptionIndex\n        activationTrigger.value = ActivationTrigger.Other\n      },\n      isSelected(other) {\n        return match(mode.value, {\n          [ValueMode.Single]: () => api.compare(toRaw(api.value.value), toRaw(other)),\n          [ValueMode.Multi]: () =>\n            (toRaw(api.value.value) as unknown[]).some((option) =>\n              api.compare(toRaw(option), toRaw(other))\n            ),\n        })\n      },\n      isActive(other) {\n        return activeOptionIndex.value === api.calculateIndex(other)\n      },\n    }\n\n    // Handle outside click\n    useOutsideClick(\n      [inputRef, buttonRef, optionsRef],\n      () => api.closeCombobox(),\n      computed(() => comboboxState.value === ComboboxStates.Open)\n    )\n\n    provide(ComboboxContext, api)\n\n    useOpenClosedProvider(\n      computed(() =>\n        match(comboboxState.value, {\n          [ComboboxStates.Open]: State.Open,\n          [ComboboxStates.Closed]: State.Closed,\n        })\n      )\n    )\n\n    let form = computed(() => dom(inputRef)?.closest('form'))\n    onMounted(() => {\n      watch(\n        [form],\n        () => {\n          if (!form.value) return\n          if (props.defaultValue === undefined) return\n\n          function handle() {\n            api.change(props.defaultValue)\n          }\n\n          form.value.addEventListener('reset', handle)\n\n          return () => {\n            form.value?.removeEventListener('reset', handle)\n          }\n        },\n        { immediate: true }\n      )\n    })\n\n    return () => {\n      let { name, disabled, form, ...theirProps } = props\n      let slot = {\n        open: comboboxState.value === ComboboxStates.Open,\n        disabled,\n        activeIndex: api.activeOptionIndex.value,\n        activeOption:\n          api.activeOptionIndex.value === null\n            ? null\n            : api.virtual.value\n              ? api.virtual.value.options[api.activeOptionIndex.value ?? 0]\n              : api.options.value[api.activeOptionIndex.value]?.dataRef.value ?? null,\n        value: value.value,\n      }\n\n      return h(Fragment, [\n        ...(name != null && value.value != null\n          ? objectToFormEntries({ [name]: value.value }).map(([name, value]) => {\n              return h(\n                Hidden,\n                compact({\n                  features: HiddenFeatures.Hidden,\n                  key: name,\n                  as: 'input',\n                  type: 'hidden',\n                  hidden: true,\n                  readOnly: true,\n                  form,\n                  disabled,\n                  name,\n                  value,\n                })\n              )\n            })\n          : []),\n        render({\n          theirProps: {\n            ...attrs,\n            ...omit(theirProps, [\n              'by',\n              'defaultValue',\n              'immediate',\n              'modelValue',\n              'multiple',\n              'nullable',\n              'onUpdate:modelValue',\n              'virtual',\n            ]),\n          },\n          ourProps: {},\n          slot,\n          slots,\n          attrs,\n          name: 'Combobox',\n        }),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let ComboboxLabel = defineComponent({\n  name: 'ComboboxLabel',\n  props: {\n    as: { type: [Object, String], default: 'label' },\n    id: { type: String, default: () => `headlessui-combobox-label-${useId()}` },\n  },\n  setup(props, { attrs, slots }) {\n    let api = useComboboxContext('ComboboxLabel')\n\n    function handleClick() {\n      dom(api.inputRef)?.focus({ preventScroll: true })\n    }\n\n    return () => {\n      let slot = {\n        open: api.comboboxState.value === ComboboxStates.Open,\n        disabled: api.disabled.value,\n      }\n\n      let { id, ...theirProps } = props\n      let ourProps = { id, ref: api.labelRef, onClick: handleClick }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ComboboxLabel',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let ComboboxButton = defineComponent({\n  name: 'ComboboxButton',\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    id: { type: String, default: () => `headlessui-combobox-button-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useComboboxContext('ComboboxButton')\n\n    expose({ el: api.buttonRef, $el: api.buttonRef })\n\n    function handleClick(event: MouseEvent) {\n      if (api.disabled.value) return\n      if (api.comboboxState.value === ComboboxStates.Open) {\n        api.closeCombobox()\n      } else {\n        event.preventDefault()\n        api.openCombobox()\n      }\n\n      nextTick(() => dom(api.inputRef)?.focus({ preventScroll: true }))\n    }\n\n    function handleKeydown(event: KeyboardEvent) {\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n        case Keys.ArrowDown:\n          event.preventDefault()\n          event.stopPropagation()\n          if (api.comboboxState.value === ComboboxStates.Closed) {\n            api.openCombobox()\n          }\n          nextTick(() => api.inputRef.value?.focus({ preventScroll: true }))\n          return\n\n        case Keys.ArrowUp:\n          event.preventDefault()\n          event.stopPropagation()\n          if (api.comboboxState.value === ComboboxStates.Closed) {\n            api.openCombobox()\n            nextTick(() => {\n              if (!api.value.value) {\n                api.goToOption(Focus.Last)\n              }\n            })\n          }\n          nextTick(() => api.inputRef.value?.focus({ preventScroll: true }))\n          return\n\n        case Keys.Escape:\n          if (api.comboboxState.value !== ComboboxStates.Open) return\n          event.preventDefault()\n          if (api.optionsRef.value && !api.optionsPropsRef.value.static) {\n            event.stopPropagation()\n          }\n          api.closeCombobox()\n          nextTick(() => api.inputRef.value?.focus({ preventScroll: true }))\n          return\n      }\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      api.buttonRef\n    )\n\n    return () => {\n      let slot = {\n        open: api.comboboxState.value === ComboboxStates.Open,\n        disabled: api.disabled.value,\n        value: api.value.value,\n      }\n      let { id, ...theirProps } = props\n      let ourProps = {\n        ref: api.buttonRef,\n        id,\n        type: type.value,\n        tabindex: '-1',\n        'aria-haspopup': 'listbox',\n        'aria-controls': dom(api.optionsRef)?.id,\n        'aria-expanded': api.comboboxState.value === ComboboxStates.Open,\n        'aria-labelledby': api.labelRef.value ? [dom(api.labelRef)?.id, id].join(' ') : undefined,\n        disabled: api.disabled.value === true ? true : undefined,\n        onKeydown: handleKeydown,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ComboboxButton',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let ComboboxInput = defineComponent({\n  name: 'ComboboxInput',\n  props: {\n    as: { type: [Object, String], default: 'input' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    displayValue: { type: Function as PropType<(item: unknown) => string> },\n    defaultValue: { type: String, default: undefined },\n    id: { type: String, default: () => `headlessui-combobox-input-${useId()}` },\n  },\n  emits: {\n    change: (_value: Event & { target: HTMLInputElement }) => true,\n  },\n  setup(props, { emit, attrs, slots, expose }) {\n    let api = useComboboxContext('ComboboxInput')\n    let ownerDocument = computed(() => getOwnerDocument(dom(api.inputRef)))\n\n    let isTyping = { value: false }\n\n    expose({ el: api.inputRef, $el: api.inputRef })\n\n    function clear() {\n      api.change(null)\n      let options = dom(api.optionsRef)\n      if (options) {\n        options.scrollTop = 0\n      }\n      api.goToOption(Focus.Nothing)\n    }\n\n    // When a `displayValue` prop is given, we should use it to transform the current selected\n    // option(s) so that the format can be chosen by developers implementing this. This is useful if\n    // your data is an object and you just want to pick a certain property or want to create a dynamic\n    // value like `firstName + ' ' + lastName`.\n    //\n    // Note: This can also be used with multiple selected options, but this is a very simple transform\n    // which should always result in a string (since we are filling in the value of the text input),\n    // you don't have to use this at all, a more common UI is a \"tag\" based UI, which you can render\n    // yourself using the selected option(s).\n    let currentDisplayValue = computed(() => {\n      let value = api.value.value\n      if (!dom(api.inputRef)) return ''\n\n      if (typeof props.displayValue !== 'undefined' && value !== undefined) {\n        return props.displayValue(value as unknown) ?? ''\n      } else if (typeof value === 'string') {\n        return value\n      } else {\n        return ''\n      }\n    })\n\n    onMounted(() => {\n      // Syncing the input value has some rules attached to it to guarantee a smooth and expected user\n      // experience:\n      //\n      // - When a user is not typing in the input field, it is safe to update the input value based on\n      //   the selected option(s). See `currentDisplayValue` computation from above.\n      // - The value can be updated when:\n      //   - The `value` is set from outside of the component\n      //   - The `value` is set when the user uses their keyboard (confirm via enter or space)\n      //   - The `value` is set when the user clicks on a value to select it\n      // - The value will be reset to the current selected option(s), when:\n      //   - The user is _not_ typing (otherwise you will loose your current state / query)\n      //   - The user cancels the current changes:\n      //     - By pressing `escape`\n      //     - By clicking `outside` of the Combobox\n      watch(\n        [currentDisplayValue, api.comboboxState, ownerDocument],\n        ([currentDisplayValue, state], [oldCurrentDisplayValue, oldState]) => {\n          // When the user is typing, we want to not touch the `input` at all. Especially when they\n          // are using an IME, we don't want to mess with the input at all.\n          if (isTyping.value) return\n\n          let input = dom(api.inputRef)\n          if (!input) return\n\n          if (oldState === ComboboxStates.Open && state === ComboboxStates.Closed) {\n            input.value = currentDisplayValue\n          } else if (currentDisplayValue !== oldCurrentDisplayValue) {\n            input.value = currentDisplayValue\n          }\n\n          // Once we synced the input value, we want to make sure the cursor is at the end of the\n          // input field. This makes it easier to continue typing and append to the query. We will\n          // bail out if the user is currently typing, because we don't want to mess with the cursor\n          // position while typing.\n          requestAnimationFrame(() => {\n            if (isTyping.value) return\n            if (!input) return\n\n            // Bail when the input is not the currently focused element. When it is not the focused\n            // element, and we call the `setSelectionRange`, then it will become the focused\n            // element which may be unwanted.\n            if (ownerDocument.value?.activeElement !== input) return\n\n            let { selectionStart, selectionEnd } = input\n\n            // A custom selection is used, no need to move the caret\n            if (Math.abs((selectionEnd ?? 0) - (selectionStart ?? 0)) !== 0) return\n\n            // A custom caret position is used, no need to move the caret\n            if (selectionStart !== 0) return\n\n            // Move the caret to the end\n            input.setSelectionRange(input.value.length, input.value.length)\n          })\n        },\n        { immediate: true }\n      )\n\n      // Trick VoiceOver in behaving a little bit better. Manually \"resetting\" the input makes\n      // VoiceOver a bit more happy and doesn't require some changes manually first before\n      // announcing items correctly. This is a bit of a hacks, but it is a workaround for a\n      // VoiceOver bug.\n      //\n      // TODO: VoiceOver is still relatively buggy if you start VoiceOver while the Combobox is\n      // already in an open state.\n      watch([api.comboboxState], ([newState], [oldState]) => {\n        if (newState === ComboboxStates.Open && oldState === ComboboxStates.Closed) {\n          // When the user is typing, we want to not touch the `input` at all. Especially when they\n          // are using an IME, we don't want to mess with the input at all.\n          if (isTyping.value) return\n\n          let input = dom(api.inputRef)\n          if (!input) return\n\n          // Capture current state\n          let currentValue = input.value\n          let { selectionStart, selectionEnd, selectionDirection } = input\n\n          // Trick VoiceOver into announcing the value\n          input.value = ''\n\n          // Rollback to original state\n          input.value = currentValue\n          if (selectionDirection !== null) {\n            input.setSelectionRange(selectionStart, selectionEnd, selectionDirection)\n          } else {\n            input.setSelectionRange(selectionStart, selectionEnd)\n          }\n        }\n      })\n    })\n\n    let isComposing = ref(false)\n    function handleCompositionstart() {\n      isComposing.value = true\n    }\n    function handleCompositionend() {\n      disposables().nextFrame(() => {\n        isComposing.value = false\n      })\n    }\n\n    let debounce = useFrameDebounce()\n    function handleKeyDown(event: KeyboardEvent) {\n      isTyping.value = true\n      debounce(() => {\n        if (isComposing.value) return\n        isTyping.value = false\n      })\n\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n        case Keys.Enter:\n          isTyping.value = false\n          if (api.comboboxState.value !== ComboboxStates.Open) return\n\n          // When the user is still in the middle of composing by using an IME, then we don't want\n          // to submit this value and close the Combobox yet. Instead, we will fallback to the\n          // default behaviour which is to \"end\" the composition.\n          if (isComposing.value) return\n\n          event.preventDefault()\n          event.stopPropagation()\n\n          if (api.activeOptionIndex.value === null) {\n            api.closeCombobox()\n            return\n          }\n\n          api.selectActiveOption()\n          if (api.mode.value === ValueMode.Single) {\n            api.closeCombobox()\n          }\n          break\n\n        case Keys.ArrowDown:\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return match(api.comboboxState.value, {\n            [ComboboxStates.Open]: () => api.goToOption(Focus.Next),\n            [ComboboxStates.Closed]: () => api.openCombobox(),\n          })\n\n        case Keys.ArrowUp:\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return match(api.comboboxState.value, {\n            [ComboboxStates.Open]: () => api.goToOption(Focus.Previous),\n            [ComboboxStates.Closed]: () => {\n              api.openCombobox()\n              nextTick(() => {\n                if (!api.value.value) {\n                  api.goToOption(Focus.Last)\n                }\n              })\n            },\n          })\n\n        case Keys.Home:\n          if (event.shiftKey) {\n            break\n          }\n\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.First)\n\n        case Keys.PageUp:\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.First)\n\n        case Keys.End:\n          if (event.shiftKey) {\n            break\n          }\n\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.Last)\n\n        case Keys.PageDown:\n          isTyping.value = false\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.Last)\n\n        case Keys.Escape:\n          isTyping.value = false\n          if (api.comboboxState.value !== ComboboxStates.Open) return\n          event.preventDefault()\n          if (api.optionsRef.value && !api.optionsPropsRef.value.static) {\n            event.stopPropagation()\n          }\n\n          if (api.nullable.value && api.mode.value === ValueMode.Single) {\n            // We want to clear the value when the user presses escape if and only if the current\n            // value is not set (aka, they didn't select anything yet, or they cleared the input which\n            // caused the value to be set to `null`). If the current value is set, then we want to\n            // fallback to that value when we press escape (this part is handled in the watcher that\n            // syncs the value with the input field again).\n            if (api.value.value === null) {\n              clear()\n            }\n          }\n\n          api.closeCombobox()\n          break\n\n        case Keys.Tab:\n          isTyping.value = false\n          if (api.comboboxState.value !== ComboboxStates.Open) return\n          if (\n            api.mode.value === ValueMode.Single &&\n            api.activationTrigger.value !== ActivationTrigger.Focus\n          ) {\n            api.selectActiveOption()\n          }\n          api.closeCombobox()\n          break\n      }\n    }\n\n    function handleInput(event: Event & { target: HTMLInputElement }) {\n      // Always call the onChange listener even if the user is still typing using an IME (Input Method\n      // Editor).\n      //\n      // The main issue is Android, where typing always uses the IME APIs. Just waiting until the\n      // compositionend event is fired to trigger an onChange is not enough, because then filtering\n      // options while typing won't work at all because we are still in \"composing\" mode.\n      emit('change', event)\n\n      // When the value becomes empty in a single value mode while being nullable then we want to clear\n      // the option entirely.\n      //\n      // This is can happen when you press backspace, but also when you select all the text and press\n      // ctrl/cmd+x.\n      if (api.nullable.value && api.mode.value === ValueMode.Single) {\n        if (event.target.value === '') {\n          clear()\n        }\n      }\n\n      // Open the combobox to show the results based on what the user has typed\n      api.openCombobox()\n    }\n\n    function handleBlur(event: FocusEvent) {\n      let relatedTarget =\n        (event.relatedTarget as HTMLElement) ?? history.find((x) => x !== event.currentTarget)\n      isTyping.value = false\n\n      // Focus is moved into the list, we don't want to close yet.\n      if (dom(api.optionsRef)?.contains(relatedTarget)) {\n        return\n      }\n\n      if (dom(api.buttonRef)?.contains(relatedTarget)) {\n        return\n      }\n\n      if (api.comboboxState.value !== ComboboxStates.Open) return\n      event.preventDefault()\n\n      if (api.mode.value === ValueMode.Single) {\n        // We want to clear the value when the user presses escape if and only if the current\n        // value is not set (aka, they didn't select anything yet, or they cleared the input which\n        // caused the value to be set to `null`). If the current value is set, then we want to\n        // fallback to that value when we press escape (this part is handled in the watcher that\n        // syncs the value with the input field again).\n        if (api.nullable.value && api.value.value === null) {\n          clear()\n        }\n\n        // We do have a value, so let's select the active option, unless we were just going through\n        // the form and we opened it due to the focus event.\n        else if (api.activationTrigger.value !== ActivationTrigger.Focus) {\n          api.selectActiveOption()\n        }\n      }\n\n      return api.closeCombobox()\n    }\n\n    function handleFocus(event: FocusEvent) {\n      let relatedTarget =\n        (event.relatedTarget as HTMLElement) ?? history.find((x) => x !== event.currentTarget)\n\n      if (dom(api.buttonRef)?.contains(relatedTarget)) return\n      if (dom(api.optionsRef)?.contains(relatedTarget)) return\n      if (api.disabled.value) return\n\n      if (!api.immediate.value) return\n      if (api.comboboxState.value === ComboboxStates.Open) return\n\n      api.openCombobox()\n\n      // We need to make sure that tabbing through a form doesn't result in incorrectly setting the\n      // value of the combobox. We will set the activation trigger to `Focus`, and we will ignore\n      // selecting the active option when the user tabs away.\n      disposables().nextFrame(() => {\n        api.setActivationTrigger(ActivationTrigger.Focus)\n      })\n    }\n\n    let defaultValue = computed(() => {\n      return (\n        props.defaultValue ??\n        (api.defaultValue.value !== undefined\n          ? props.displayValue?.(api.defaultValue.value)\n          : null) ??\n        api.defaultValue.value ??\n        ''\n      )\n    })\n\n    return () => {\n      let slot = { open: api.comboboxState.value === ComboboxStates.Open }\n      let { id, displayValue, onChange: _onChange, ...theirProps } = props\n      let ourProps = {\n        'aria-controls': api.optionsRef.value?.id,\n        'aria-expanded': api.comboboxState.value === ComboboxStates.Open,\n        'aria-activedescendant':\n          api.activeOptionIndex.value === null\n            ? undefined\n            : api.virtual.value\n              ? api.options.value.find((option) => {\n                  return (\n                    !api.virtual.value!.disabled(option.dataRef.value) &&\n                    api.compare(\n                      option.dataRef.value,\n                      api.virtual.value!.options[api.activeOptionIndex.value!]\n                    )\n                  )\n                })?.id\n              : api.options.value[api.activeOptionIndex.value]?.id,\n        'aria-labelledby': dom(api.labelRef)?.id ?? dom(api.buttonRef)?.id,\n        'aria-autocomplete': 'list',\n        id,\n        onCompositionstart: handleCompositionstart,\n        onCompositionend: handleCompositionend,\n        onKeydown: handleKeyDown,\n        onInput: handleInput,\n        onFocus: handleFocus,\n        onBlur: handleBlur,\n        role: 'combobox',\n        type: attrs.type ?? 'text',\n        tabIndex: 0,\n        ref: api.inputRef,\n        defaultValue: defaultValue.value,\n        disabled: api.disabled.value === true ? true : undefined,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        features: Features.RenderStrategy | Features.Static,\n        name: 'ComboboxInput',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let ComboboxOptions = defineComponent({\n  name: 'ComboboxOptions',\n  props: {\n    as: { type: [Object, String], default: 'ul' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    hold: { type: [Boolean], default: false },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useComboboxContext('ComboboxOptions')\n    let id = `headlessui-combobox-options-${useId()}`\n\n    expose({ el: api.optionsRef, $el: api.optionsRef })\n\n    watchEffect(() => {\n      api.optionsPropsRef.value.static = props.static\n    })\n\n    watchEffect(() => {\n      api.optionsPropsRef.value.hold = props.hold\n    })\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.comboboxState.value === ComboboxStates.Open\n    })\n\n    useTreeWalker({\n      container: computed(() => dom(api.optionsRef)),\n      enabled: computed(() => api.comboboxState.value === ComboboxStates.Open),\n      accept(node) {\n        if (node.getAttribute('role') === 'option') return NodeFilter.FILTER_REJECT\n        if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP\n        return NodeFilter.FILTER_ACCEPT\n      },\n      walk(node) {\n        node.setAttribute('role', 'none')\n      },\n    })\n\n    /**\n     * Prevent focus from being lost if the user clicks on the scrollbar.\n     * Otherwise the `ComboboxInput` will lose focus and the combobox will\n     * close.\n     */\n    function handleMouseDown(event: MouseEvent) {\n      event.preventDefault()\n    }\n\n    return () => {\n      let slot = { open: api.comboboxState.value === ComboboxStates.Open }\n      let ourProps = {\n        'aria-labelledby': dom(api.labelRef)?.id ?? dom(api.buttonRef)?.id,\n        id,\n        ref: api.optionsRef,\n        role: 'listbox',\n        'aria-multiselectable': api.mode.value === ValueMode.Multi ? true : undefined,\n        onMousedown: handleMouseDown,\n      }\n      let theirProps = omit(props, ['hold'])\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots:\n          api.virtual.value && api.comboboxState.value === ComboboxStates.Open\n            ? {\n                ...slots,\n                default: () => [h(VirtualProvider, {}, slots.default)],\n              }\n            : slots,\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'ComboboxOptions',\n      })\n    }\n  },\n})\n\nexport let ComboboxOption = defineComponent({\n  name: 'ComboboxOption',\n  props: {\n    as: { type: [Object, String], default: 'li' },\n    value: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n    },\n    disabled: { type: Boolean, default: false },\n    order: { type: [Number], default: null },\n  },\n  setup(props, { slots, attrs, expose }) {\n    let api = useComboboxContext('ComboboxOption')\n    let id = `headlessui-combobox-option-${useId()}`\n    let internalOptionRef = ref<HTMLElement | null>(null)\n    let disabled = computed(() => {\n      return props.disabled || api.virtual.value?.disabled(props.value)\n    })\n\n    expose({ el: internalOptionRef, $el: internalOptionRef })\n\n    let active = computed(() => {\n      return api.virtual.value\n        ? api.activeOptionIndex.value === api.calculateIndex(props.value)\n        : api.activeOptionIndex.value === null\n          ? false\n          : api.options.value[api.activeOptionIndex.value]?.id === id\n    })\n\n    let selected = computed(() => api.isSelected(props.value))\n\n    let virtualizer = inject(VirtualContext, null)\n    let dataRef = computed<ComboboxOptionData>(() => ({\n      disabled: props.disabled,\n      value: props.value,\n      domRef: internalOptionRef,\n      order: computed(() => props.order),\n    }))\n\n    onMounted(() => api.registerOption(id, dataRef))\n    onUnmounted(() => api.unregisterOption(id, active.value))\n\n    watchEffect(() => {\n      let el = dom(internalOptionRef)\n      if (!el) return\n\n      virtualizer?.value.measureElement(el)\n    })\n\n    watchEffect(() => {\n      if (api.comboboxState.value !== ComboboxStates.Open) return\n      if (!active.value) return\n      if (api.virtual.value) return\n      if (api.activationTrigger.value === ActivationTrigger.Pointer) return\n      nextTick(() => dom(internalOptionRef)?.scrollIntoView?.({ block: 'nearest' }))\n    })\n\n    function handleMouseDown(event: MouseEvent) {\n      // We use the `mousedown` event here since it fires before the focus\n      // event, allowing us to cancel the event before focus is moved from the\n      // `ComboboxInput` to the `ComboboxOption`. This keeps the input focused,\n      // preserving the cursor position and any text selection.\n      event.preventDefault()\n\n      // Since we're using the `mousedown` event instead of a `click` event here\n      // to preserve the focus of the `ComboboxInput`, we need to also check\n      // that the `left` mouse button was clicked.\n      if (event.button !== MouseButton.Left) {\n        return\n      }\n\n      if (disabled.value) return\n      api.selectOption(id)\n\n      // We want to make sure that we don't accidentally trigger the virtual\n      // keyboard.\n      //\n      // This would happen if the input is focused, the options are open, you\n      // select an option (which would blur the input, and focus the option\n      // (button), then we re-focus the input).\n      //\n      // This would be annoying on mobile (or on devices with a virtual\n      // keyboard). Right now we are assuming that the virtual keyboard would open\n      // on mobile devices (iOS / Android). This assumption is not perfect, but\n      // will work in the majority of the cases.\n      //\n      // Ideally we can have a better check where we can explicitly check for\n      // the virtual keyboard. But right now this is still an experimental\n      // feature:\n      // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/virtualKeyboard\n      if (!isMobile()) {\n        requestAnimationFrame(() => dom(api.inputRef)?.focus({ preventScroll: true }))\n      }\n\n      if (api.mode.value === ValueMode.Single) {\n        api.closeCombobox()\n      }\n    }\n\n    function handleFocus() {\n      if (props.disabled || api.virtual.value?.disabled(props.value)) {\n        return api.goToOption(Focus.Nothing)\n      }\n      let idx = api.calculateIndex(props.value)\n      api.goToOption(Focus.Specific, idx)\n    }\n\n    let pointer = useTrackedPointer()\n\n    function handleEnter(evt: PointerEvent) {\n      pointer.update(evt)\n    }\n\n    function handleMove(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled || api.virtual.value?.disabled(props.value)) return\n      if (active.value) return\n      let idx = api.calculateIndex(props.value)\n      api.goToOption(Focus.Specific, idx, ActivationTrigger.Pointer)\n    }\n\n    function handleLeave(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled || api.virtual.value?.disabled(props.value)) return\n      if (!active.value) return\n      if (api.optionsPropsRef.value.hold) return\n      api.goToOption(Focus.Nothing)\n    }\n\n    return () => {\n      let { disabled } = props\n      let slot = { active: active.value, selected: selected.value, disabled }\n      let ourProps = {\n        id,\n        ref: internalOptionRef,\n        role: 'option',\n        tabIndex: disabled === true ? undefined : -1,\n        'aria-disabled': disabled === true ? true : undefined,\n        // According to the WAI-ARIA best practices, we should use aria-checked for\n        // multi-select,but Voice-Over disagrees. So we use aria-selected instead for\n        // both single and multi-select.\n        'aria-selected': selected.value,\n        disabled: undefined, // Never forward the `disabled` prop\n        onMousedown: handleMouseDown,\n        onFocus: handleFocus,\n        onPointerenter: handleEnter,\n        onMouseenter: handleEnter,\n        onPointermove: handleMove,\n        onMousemove: handleMove,\n        onPointerleave: handleLeave,\n        onMouseleave: handleLeave,\n      }\n\n      let theirProps = omit(props, ['order', 'value'])\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ComboboxOption',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/description/__snapshots__/description.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to update a prop from the parent and it should reflect in the Description component 1`] = `\"<div><div aria-describedby=\\\\\"headlessui-description-1\\\\\"><p data-count=\\\\\"0\\\\\" id=\\\\\"headlessui-description-1\\\\\">I am a description</p><button>+1</button></div></div>\"`;\n\nexports[`should be possible to update a prop from the parent and it should reflect in the Description component 2`] = `\"<div><div aria-describedby=\\\\\"headlessui-description-1\\\\\"><p data-count=\\\\\"1\\\\\" id=\\\\\"headlessui-description-1\\\\\">I am a description</p><button>+1</button></div></div>\"`;\n\nexports[`should be possible to use useDescriptions and a single Description, and have them linked 1`] = `\"<div><div aria-describedby=\\\\\"headlessui-description-1\\\\\"><p id=\\\\\"headlessui-description-1\\\\\">I am a description</p><span>Contents</span></div></div>\"`;\n\nexports[`should be possible to use useDescriptions and multiple Description components, and have them linked 1`] = `\"<div><div aria-describedby=\\\\\"headlessui-description-1 headlessui-description-2\\\\\"><p id=\\\\\"headlessui-description-1\\\\\">I am a description</p><span>Contents</span><p id=\\\\\"headlessui-description-2\\\\\">I am also a description</p></div></div>\"`;\n\nexports[`should be possible to use useDescriptions without using a Description 1`] = `\"<div><div>No description</div></div>\"`;\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/description/description.test.ts",
    "content": "import { defineComponent, h, nextTick, ref } from 'vue'\nimport { getByText } from '../../test-utils/accessibility-assertions'\nimport { click } from '../../test-utils/interactions'\nimport { render } from '../../test-utils/vue-testing-library'\nimport { Description, useDescriptions } from './description'\n\nfunction format(input: Element | null | string) {\n  if (input === null) throw new Error('input is null')\n  let contents = (typeof input === 'string' ? input : (input as HTMLElement).outerHTML).trim()\n  return contents\n}\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nit('should be possible to use useDescriptions without using a Description', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Description },\n      setup() {\n        let describedby = useDescriptions()\n\n        return () =>\n          h('div', [h('div', { 'aria-describedby': describedby.value }, ['No description'])])\n      },\n    })\n  )\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to use useDescriptions and a single Description, and have them linked', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Description },\n      setup() {\n        let describedby = useDescriptions()\n\n        return () =>\n          h('div', [\n            h('div', { 'aria-describedby': describedby.value }, [\n              h(Description, () => 'I am a description'),\n              h('span', 'Contents'),\n            ]),\n          ])\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to use useDescriptions and multiple Description components, and have them linked', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Description },\n      setup() {\n        let describedby = useDescriptions()\n\n        return () =>\n          h('div', [\n            h('div', { 'aria-describedby': describedby.value }, [\n              h(Description, () => 'I am a description'),\n              h('span', 'Contents'),\n              h(Description, () => 'I am also a description'),\n            ]),\n          ])\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to update a prop from the parent and it should reflect in the Description component', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Description },\n      setup() {\n        let count = ref(0)\n        let describedby = useDescriptions({ props: { 'data-count': count } })\n\n        return () => {\n          return h('div', [\n            h('div', { 'aria-describedby': describedby.value }, [\n              h(Description, () => 'I am a description'),\n              h('button', { onClick: () => count.value++ }, '+1'),\n            ]),\n          ])\n        }\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n\n  await click(getByText('+1'))\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/description/description.ts",
    "content": "import {\n  computed,\n  defineComponent,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  unref,\n  type ComputedRef,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport { render } from '../../utils/render'\n\n// ---\n\nlet DescriptionContext = Symbol('DescriptionContext') as InjectionKey<{\n  register(value: string): () => void\n  slot: Ref<Record<string, any>>\n  name: string\n  props: Record<string, any>\n}>\n\nfunction useDescriptionContext() {\n  let context = inject(DescriptionContext, null)\n  if (context === null) {\n    throw new Error('Missing parent')\n  }\n  return context\n}\n\nexport function useDescriptions({\n  slot = ref({}),\n  name = 'Description',\n  props = {},\n}: {\n  slot?: Ref<Record<string, unknown>>\n  name?: string\n  props?: Record<string, unknown>\n} = {}): ComputedRef<string | undefined> {\n  let descriptionIds = ref<string[]>([])\n\n  function register(value: string) {\n    descriptionIds.value.push(value)\n\n    return () => {\n      let idx = descriptionIds.value.indexOf(value)\n      if (idx === -1) return\n      descriptionIds.value.splice(idx, 1)\n    }\n  }\n\n  provide(DescriptionContext, { register, slot, name, props })\n\n  // The actual id's as string or undefined.\n  return computed(() =>\n    descriptionIds.value.length > 0 ? descriptionIds.value.join(' ') : undefined\n  )\n}\n\n// ---\n\nexport let Description = defineComponent({\n  name: 'Description',\n  props: {\n    as: { type: [Object, String], default: 'p' },\n    id: { type: String, default: () => `headlessui-description-${useId()}` },\n  },\n  setup(myProps, { attrs, slots }) {\n    let context = useDescriptionContext()\n\n    onMounted(() => onUnmounted(context.register(myProps.id)))\n\n    return () => {\n      let { name = 'Description', slot = ref({}), props = {} } = context\n      let { id, ...theirProps } = myProps\n      let ourProps = {\n        ...Object.entries(props).reduce(\n          (acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),\n          {}\n        ),\n        id,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot: slot.value,\n        attrs,\n        slots,\n        name,\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/dialog/dialog.test.ts",
    "content": "import {\n  computed,\n  defineComponent,\n  h,\n  nextTick,\n  onMounted,\n  ref,\n  type ConcreteComponent,\n  type PropType,\n} from 'vue'\nimport { State, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  DialogState,\n  PopoverState,\n  assertActiveElement,\n  assertDialog,\n  assertDialogDescription,\n  assertDialogOverlay,\n  assertDialogTitle,\n  assertPopoverPanel,\n  getByText,\n  getDialog,\n  getDialogBackdrop,\n  getDialogOverlay,\n  getDialogOverlays,\n  getDialogs,\n  getPopoverButton,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, click, focus, mouseDrag, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { Popover, PopoverButton, PopoverPanel } from '../popover/popover'\nimport { TransitionRoot } from '../transitions/transition'\nimport {\n  Dialog,\n  DialogBackdrop,\n  DialogDescription,\n  DialogOverlay,\n  DialogPanel,\n  DialogTitle,\n} from './dialog'\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return frames(1)\n}\n\nasync function frames(count: number) {\n  for (let n = 0; n <= count; n++) {\n    await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))\n  }\n}\n\nlet TabSentinel = defineComponent({\n  name: 'TabSentinel',\n  template: html`<button></button>`,\n})\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({\n  Dialog,\n  DialogOverlay,\n  DialogBackdrop,\n  DialogPanel,\n  DialogTitle,\n  DialogDescription,\n  TabSentinel,\n})\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['DialogOverlay', DialogOverlay],\n    ['DialogTitle', DialogTitle],\n    ['DialogBackdrop', DialogBackdrop],\n    ['DialogPanel', DialogPanel],\n  ])(\n    'should error when we are using a <%s /> without a parent <Dialog />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <Dialog /> component.`\n      )\n      expect.hasAssertions()\n    })\n  )\n\n  it(\n    'should be possible to render a Dialog without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate(\n        `\n          <Dialog :open=\"false\" @close=\"() => {}\">\n            <button>Trigger</button>\n            <DialogOverlay />\n            <DialogTitle />\n            <p>Contents</p>\n            <DialogDescription />\n          </Dialog>\n        `\n      )\n\n      assertDialog({\n        state: DialogState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-dialog-1' },\n      })\n    })\n  )\n})\n\ndescribe('refs', () => {\n  it('should be possible to access the ref on the DialogBackdrop', async () => {\n    expect.assertions(2)\n    renderTemplate({\n      template: `\n        <Dialog :open=\"true\">\n          <DialogBackdrop ref=\"backdrop\" />\n          <DialogPanel>\n            <button>element</button>\n          </DialogPanel>\n        </Dialog>\n      `,\n      setup() {\n        let backdrop = ref<{ el: Element; $el: Element } | null>(null)\n        onMounted(() => {\n          nextTick(() => {\n            expect(backdrop.value?.el).toBeInstanceOf(HTMLDivElement)\n            expect(backdrop.value?.$el).toBeInstanceOf(HTMLDivElement)\n          })\n        })\n        return { backdrop }\n      },\n    })\n  })\n\n  it('should be possible to access the ref on the DialogPanel', async () => {\n    expect.assertions(2)\n    renderTemplate({\n      template: `\n        <Dialog :open=\"true\">\n          <DialogPanel ref=\"panel\">\n            <button>element</button>\n          </DialogPanel>\n        </Dialog>\n      `,\n      setup() {\n        let panel = ref<{ el: Element; $el: Element } | null>(null)\n        onMounted(() => {\n          nextTick(() => {\n            expect(panel.value?.el).toBeInstanceOf(HTMLDivElement)\n            expect(panel.value?.$el).toBeInstanceOf(HTMLDivElement)\n          })\n        })\n        return { panel }\n      },\n    })\n  })\n})\n\ndescribe('Rendering', () => {\n  describe('Dialog', () => {\n    it(\n      'should complain when an `open` prop is missing',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          renderTemplate(\n            `\n              <Dialog as=\"div\" @close=\"() => {}\" />\n            `\n          )\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You forgot to provide an \\`open\\` prop to the \\`Dialog\\`.\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should be able to explicitly choose role=dialog',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"setIsOpen(true)\">Trigger</button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" class=\"relative bg-blue-500\" role=\"dialog\">\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'dialog' } })\n      })\n    )\n\n    it(\n      'should be able to explicitly choose role=alertdialog',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"setIsOpen(true)\">Trigger</button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" class=\"relative bg-blue-500\" role=\"alertdialog\">\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'alertdialog' } })\n      })\n    )\n\n    it(\n      'should fall back to role=dialog for an invalid role',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"setIsOpen(true)\">Trigger</button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" class=\"relative bg-blue-500\" role=\"foobar\">\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        await nextFrame()\n\n        assertDialog({ state: DialogState.Visible, attributes: { role: 'dialog' } })\n      })\n    )\n\n    it(\n      'should complain when an `open` prop is not a boolean',\n      suppressConsoleLogs(async () => {\n        expect(() =>\n          renderTemplate(\n            `\n              <Dialog as=\"div\" :open=\"null\" @close=\"() => {}\" />\n            `\n          )\n        ).toThrowErrorMatchingInlineSnapshot(\n          `\"You provided an \\`open\\` prop to the \\`Dialog\\`, but the value is not a boolean. Received: null\"`\n        )\n        expect.hasAssertions()\n      })\n    )\n\n    it(\n      'should be possible to render a Dialog using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"setIsOpen(true)\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" v-slot=\"data\">\n                <pre>{{JSON.stringify(data)}}</pre>\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        await click(document.getElementById('trigger'))\n\n        assertDialog({ state: DialogState.Visible, textContent: JSON.stringify({ open: true }) })\n      })\n    )\n\n    it('should be possible to pass props to the Dialog itself', async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"setIsOpen(true)\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\" class=\"relative bg-blue-500\">\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      await click(document.getElementById('trigger'))\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.Visible, attributes: { class: 'relative bg-blue-500' } })\n    })\n\n    it('should be possible to always render the Dialog if we provide it a `static` prop (and enable focus trapping based on `open`)', async () => {\n      let focusCounter = jest.fn()\n      renderTemplate({\n        template: `\n          <div>\n            <button>Trigger</button>\n            <Dialog :open=\"true\" @close=\"() => {}\" static>\n              <p>Contents</p>\n              <TabSentinel @focus=\"focusCounter\" />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          return { focusCounter }\n        },\n      })\n\n      await nextFrame()\n\n      // Let's verify that the Dialog is already there\n      expect(getDialog()).not.toBe(null)\n      expect(focusCounter).toHaveBeenCalledTimes(1)\n    })\n\n    it('should be possible to always render the Dialog if we provide it a `static` prop (and disable focus trapping based on `open`)', async () => {\n      let focusCounter = jest.fn()\n      renderTemplate({\n        template: `\n          <div>\n            <button>Trigger</button>\n            <Dialog :open=\"false\" @close=\"() => {}\" static>\n              <p>Contents</p>\n              <TabSentinel @focus=\"focusCounter\" />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          return { focusCounter }\n        },\n      })\n\n      await nextFrame()\n\n      // Let's verify that the Dialog is already there\n      expect(getDialog()).not.toBe(null)\n      expect(focusCounter).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to use a different render strategy for the Dialog', async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"isOpen = !isOpen\">Trigger</button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\" :unmount=\"false\">\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.InvisibleHidden })\n\n      // Let's open the Dialog, to see if it is not hidden anymore\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      // Let's close the Dialog\n      await press(Keys.Escape)\n\n      assertDialog({ state: DialogState.InvisibleHidden })\n    })\n\n    it(\n      'should add a scroll lock to the html tag',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <input id=\"a\" type=\"text\" />\n                <input id=\"b\" type=\"text\" />\n                <input id=\"c\" type=\"text\" />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let btn = document.getElementById('trigger')\n\n        // Open the dialog\n        await click(btn)\n\n        // Expect overflow\n        expect(document.documentElement.style.overflow).toBe('hidden')\n      })\n    )\n\n    it(\n      'should wait to add a scroll lock to the html tag when unmount is false in a Transition',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          components: {\n            Dialog,\n            TransitionRoot,\n          },\n\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n\n              <TransitionRoot :show=\"isOpen\" :unmount=\"false\">\n                <Dialog @close=\"setIsOpen\" :unmount=\"false\">\n                  <input id=\"a\" type=\"text\" />\n                  <input id=\"b\" type=\"text\" />\n                  <input id=\"c\" type=\"text\" />\n                </Dialog>\n              </TransitionRoot>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let btn = document.getElementById('trigger')\n\n        // Open the dialog\n        await click(btn)\n\n        // Expect overflow\n        expect(document.documentElement.style.overflow).toBe('hidden')\n      })\n    )\n\n    it(\n      'scroll locking should work when transitioning between dialogs',\n      suppressConsoleLogs(async () => {\n        // While we don't support multiple dialogs\n        // We at least want to work towards supporting it at some point\n        // The first step is just making sure that scroll locking works\n        // when there are multiple dialogs open at the same time\n        let DialogWrapper = defineComponent({\n          components: {\n            TransitionRoot,\n            Dialog,\n          },\n          props: {\n            id: String,\n            dialogs: Array as PropType<string[]>,\n            toggle: Function as PropType<(id: string, state: string) => void>,\n          },\n          template: `\n            <button :id=\"id_open\" @click=\"toggle(id, 'open')\">\n              Open {{ id }}\n            </button>\n            <TransitionRoot as=\"template\" :show=\"dialogs.includes(id)\">\n              <Dialog @close=\"toggle(id, 'close')\" :data-debug=\"id\">\n                <button :id=\"id_close\" @click=\"toggle(id, 'close')\">\n                  Close {{ id }}\n                </button>\n              </Dialog>\n            </TransitionRoot>\n          `,\n\n          setup(props) {\n            return {\n              id_open: computed(() => `open_${props.id}`),\n              id_close: computed(() => `close_${props.id}`),\n            }\n          },\n        })\n\n        let Example = defineComponent({\n          components: { DialogWrapper },\n          template: `\n            <DialogWrapper id=\"d1\" :dialogs=\"dialogs\" :toggle=\"toggle\" />\n            <DialogWrapper id=\"d2\" :dialogs=\"dialogs\" :toggle=\"toggle\" />\n            <DialogWrapper id=\"d3\" :dialogs=\"dialogs\" :toggle=\"toggle\" />\n          `,\n\n          setup() {\n            let dialogs = ref<string[]>([])\n            return {\n              dialogs,\n              toggle(id: string, state: 'open' | 'close') {\n                if (state === 'open' && !dialogs.value.includes(id)) {\n                  dialogs.value = [id]\n                } else if (state === 'close' && dialogs.value.includes(id)) {\n                  dialogs.value = dialogs.value.filter((x) => x !== id)\n                }\n              },\n            }\n          },\n        })\n\n        renderTemplate(Example)\n\n        // No overflow yet\n        expect(document.documentElement.style.overflow).toBe('')\n\n        let open1 = () => document.getElementById('open_d1')\n        let open2 = () => document.getElementById('open_d2')\n        let open3 = () => document.getElementById('open_d3')\n        let close3 = () => document.getElementById('close_d3')\n\n        // Open the dialog & expect overflow\n        await click(open1())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Open the dialog & expect overflow\n        await click(open2())\n        await frames(2)\n        // expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Open the dialog & expect overflow\n        await click(open3())\n        await frames(2)\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // At this point only the last dialog should be open\n        // Close the dialog & dont expect overflow\n        await click(close3())\n        await frames(2)\n\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should remove the scroll lock when the open closed state is `Closing`',\n      suppressConsoleLogs(async () => {\n        let Wrapper = defineComponent({\n          props: {\n            state: {\n              type: Number as PropType<State>,\n              default: State.Open,\n            },\n          },\n          setup(props, { slots }) {\n            useOpenClosedProvider(ref(props.state))\n            return () => h('div', {}, slots.default?.())\n          },\n        })\n\n        let x = renderTemplate({\n          components: {\n            Dialog,\n            Wrapper,\n          },\n          template: `\n            <Wrapper :state=\"state\">\n              <Dialog :open=\"true\" @close=\"() => {}\">\n                <input id=\"a\" type=\"text\" />\n                <input id=\"b\" type=\"text\" />\n                <input id=\"c\" type=\"text\" />\n              </Dialog>\n            </Wrapper>\n          `,\n\n          setup: () => ({ state: State.Open }),\n        })\n\n        await nextFrame()\n\n        // The overflow should be there\n        expect(document.documentElement.style.overflow).toBe('hidden')\n\n        // Reset the document\n        x.unmount()\n        await nextFrame()\n\n        // Re-render but with the `Closing` state\n        renderTemplate({\n          components: {\n            Dialog,\n            Wrapper,\n          },\n          template: `\n            <Wrapper :state=\"state\">\n              <Dialog :open=\"true\" @close=\"() => {}\">\n                <input id=\"a\" type=\"text\" />\n                <input id=\"b\" type=\"text\" />\n                <input id=\"c\" type=\"text\" />\n              </Dialog>\n            </Wrapper>\n          `,\n\n          setup: () => ({ state: State.Open | State.Closing }),\n        })\n\n        await nextFrame()\n\n        // The moment the dialog is closing, the overflow should be gone\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should not have a scroll lock when the transition marked as not shown',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          components: {\n            Dialog,\n            TransitionRoot,\n          },\n          template: `\n            <TransitionRoot as=\"template\" :show=\"false\" :unmount=\"false\">\n              <Dialog as=\"div\">\n                <input type=\"text\" />\n              </Dialog>\n            </TransitionRoot>\n          `,\n        })\n\n        await nextFrame()\n\n        // The overflow should NOT be there\n        expect(document.documentElement.style.overflow).toBe('')\n      })\n    )\n\n    it(\n      'should not have a scroll lock when the transition marked as not shown',\n      suppressConsoleLogs(async () => {\n        let dialogRef = ref(null)\n\n        renderTemplate({\n          components: {\n            Dialog,\n            TransitionRoot,\n          },\n          template: `\n            <button @click=\"show = !show\">toggle</button>\n            <div id=\"output\">{{ hasRef ? \"Yes\" : \"No\" }}</div>\n            <TransitionRoot as=\"template\" :show=\"show\">\n              <Dialog as=\"div\" ref=\"dialogRef\">\n                <input type=\"text\" />\n              </Dialog>\n            </TransitionRoot>\n          `,\n          setup() {\n            let show = ref(false)\n\n            return {\n              show,\n              dialogRef,\n            }\n          },\n        })\n\n        expect(dialogRef.value).toBeNull()\n\n        await click(getByText('toggle'))\n        await nextFrame()\n\n        expect(dialogRef.value).not.toBeNull()\n\n        await click(getByText('toggle'))\n        await nextFrame()\n\n        expect(dialogRef.value).toBeNull()\n      })\n    )\n  })\n\n  describe('DialogOverlay', () => {\n    it(\n      'should be possible to render DialogOverlay using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogOverlay v-slot=\"data\">{{JSON.stringify(data)}}</DialogOverlay>\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialogOverlay({\n          state: DialogState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-dialog-overlay-2' },\n        })\n\n        await click(document.getElementById('trigger'))\n\n        assertDialogOverlay({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-overlay-2' },\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n  })\n\n  describe('DialogBackdrop', () => {\n    it(\n      'should throw an error if a DialogBackdrop is used without a DialogPanel',\n      suppressConsoleLogs(async () => {\n        expect.hasAssertions()\n\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogBackdrop />\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n          errorCaptured(err) {\n            expect(err as Error).toEqual(\n              new Error(\n                'A <DialogBackdrop /> component is being used, but a <DialogPanel /> component is missing.'\n              )\n            )\n\n            return false\n          },\n        })\n\n        await click(document.getElementById('trigger'))\n      })\n    )\n\n    it(\n      'should not throw an error if a DialogBackdrop is used with a DialogPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogBackdrop />\n                <DialogPanel>\n                  <TabSentinel />\n                </DialogPanel>\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        await click(document.getElementById('trigger'))\n      })\n    )\n\n    it(\n      'should portal the DialogBackdrop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogBackdrop />\n                <DialogPanel>\n                  <TabSentinel />\n                <DialogPanel>\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        await click(document.getElementById('trigger'))\n\n        let dialog = getDialog()\n        let backdrop = getDialogBackdrop()\n\n        expect(dialog).not.toBe(null)\n        dialog = dialog as HTMLElement\n\n        expect(backdrop).not.toBe(null)\n        backdrop = backdrop as HTMLElement\n\n        // It should not be nested\n        let position = dialog.compareDocumentPosition(backdrop)\n        expect(position & Node.DOCUMENT_POSITION_CONTAINED_BY).not.toBe(\n          Node.DOCUMENT_POSITION_CONTAINED_BY\n        )\n\n        // It should be a sibling\n        expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBe(Node.DOCUMENT_POSITION_FOLLOWING)\n      })\n    )\n  })\n\n  describe('DialogTitle', () => {\n    it(\n      'should be possible to render DialogTitle using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(\n          `\n            <Dialog :open=\"true\" @close=\"() => {}\">\n              <DialogTitle v-slot=\"data\">{{JSON.stringify(data)}}</DialogTitle>\n              <TabSentinel />\n            </Dialog>\n          `\n        )\n\n        await nextFrame()\n\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n        assertDialogTitle({\n          state: DialogState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n  })\n\n  describe('DialogDescription', () => {\n    it(\n      'should be possible to render DialogDescription using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(\n          `\n            <Dialog :open=\"true\" @close=\"() => {}\">\n              <DialogDescription v-slot=\"data\">{{JSON.stringify(data)}}</DialogDescription>\n              <TabSentinel />\n            </Dialog>\n          `\n        )\n\n        await nextFrame()\n\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n        assertDialogDescription({\n          state: DialogState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n  })\n})\n\ndescribe('Composition', () => {\n  it(\n    'should be possible to open a dialog from inside a Popover (and then close it)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { Popover, PopoverButton, PopoverPanel },\n        template: `\n          <div>\n            <Popover>\n              <PopoverButton>Open Popover</PopoverButton>\n              <PopoverPanel>\n                <button id=\"openDialog\" @click=\"isDialogOpen = true\">Open dialog</button>\n              </PopoverPanel>\n            </Popover>\n\n            <Dialog :open=\"isDialogOpen\">\n              <DialogPanel>\n                <button id=\"closeDialog\" @click=\"isDialogOpen = false\">Close Dialog</button>\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isDialogOpen = ref(false)\n          return {\n            isDialogOpen,\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Nothing is open initially\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(document.body)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The popover should be open but the dialog should not\n      assertPopoverPanel({ state: PopoverState.Visible })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(getPopoverButton())\n\n      // Open the dialog from inside the popover\n      await click(document.getElementById('openDialog'))\n\n      // The dialog should be open but the popover should not\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.Visible })\n      assertActiveElement(document.getElementById('closeDialog'))\n\n      // Close the dialog from inside itself\n      await click(document.getElementById('closeDialog'))\n\n      // Nothing should be open\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to open the Dialog via a Transition component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { TransitionRoot },\n        template: `\n          <TransitionRoot show>\n            <Dialog @close=\"() => {}\">\n              <DialogDescription v-slot=\"data\">{{JSON.stringify(data)}}</DialogDescription>\n              <TabSentinel />\n            </Dialog>\n          </Transition>\n        `,\n      })\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.Visible })\n      assertDialogDescription({\n        state: DialogState.Visible,\n        textContent: JSON.stringify({ open: true }),\n      })\n    })\n  )\n\n  it(\n    'should be possible to close the Dialog via a Transition component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { TransitionRoot },\n        template: `\n          <TransitionRoot :show=\"false\">\n            <Dialog @close=\"() => {}\">\n              <DialogDescription v-slot=\"data\">{{JSON.stringify(data)}}</DialogDescription>\n              <TabSentinel />\n            </Dialog>\n          </Transition>\n        `,\n      })\n\n      await nextFrame()\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Escape` key', () => {\n    it(\n      'should be possible to close the dialog with Escape',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                Contents\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Close dialog\n        await press(Keys.Escape)\n\n        // Verify it is close\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the dialog with Escape, when a field is focused',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                Contents\n                <input id=\"name\" />\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Close dialog\n        await press(Keys.Escape)\n\n        // Verify it is close\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to close the dialog with Escape, when a field is focused but cancels the event',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                Contents\n                <input\n                  id=\"name\"\n                  @keydown=\"cancel\"\n                />\n                <TabSentinel />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            return {\n              isOpen,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n              cancel(event: KeyboardEvent) {\n                event.preventDefault()\n                event.stopPropagation()\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Try to close the dialog\n        await press(Keys.Escape)\n\n        // Verify it is still open\n        assertDialog({ state: DialogState.Visible })\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to tab around when using the initialFocus ref',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" :initialFocus=\"initialFocusRef\">\n                Contents\n                <TabSentinel id=\"a\" />\n                <input type=\"text\" id=\"b\" ref=\"initialFocusRef\" />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            let initialFocusRef = ref(null)\n            return {\n              isOpen,\n              initialFocusRef,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n\n    it(\n      'should be possible to tab around when using the initialFocus ref on a component',\n      suppressConsoleLogs(async () => {\n        let CustomComponent = defineComponent({\n          name: 'CustomComponent',\n          setup() {\n            return () => h('input')\n          },\n        })\n\n        renderTemplate({\n          components: {\n            CustomComponent,\n          },\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\" :initialFocus=\"initialFocusRef\">\n                Contents\n                <TabSentinel id=\"a\" />\n                <CustomComponent type=\"text\" id=\"b\" ref=\"initialFocusRef\" />\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            let initialFocusRef = ref(null)\n            return {\n              isOpen,\n              initialFocusRef,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('b'))\n\n        // Verify that we can tab around\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n\n    it(\n      'should not escape the FocusTrap when there is only 1 focusable element (going forwards)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogPanel>\n                  <input type=\"text\" id=\"a\" />\n                <DialogPanel>\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            let initialFocusRef = ref(null)\n            return {\n              isOpen,\n              initialFocusRef,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(Keys.Tab)\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n\n    it(\n      'should not escape the FocusTrap when there is only 1 focusable element (going backwards)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: `\n            <div>\n              <button id=\"trigger\" @click=\"toggleOpen\">\n                Trigger\n              </button>\n              <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n                <DialogPanel>\n                  <input type=\"text\" id=\"a\" />\n                <DialogPanel>\n              </Dialog>\n            </div>\n          `,\n          setup() {\n            let isOpen = ref(false)\n            let initialFocusRef = ref(null)\n            return {\n              isOpen,\n              initialFocusRef,\n              setIsOpen(value: boolean) {\n                isOpen.value = value\n              },\n              toggleOpen() {\n                isOpen.value = !isOpen.value\n              },\n            }\n          },\n        })\n\n        assertDialog({ state: DialogState.InvisibleUnmounted })\n\n        // Open dialog\n        await click(document.getElementById('trigger'))\n\n        // Verify it is open\n        assertDialog({\n          state: DialogState.Visible,\n          attributes: { id: 'headlessui-dialog-1' },\n        })\n\n        // Verify that the input field is focused\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n\n        // Verify that we stay within the Dialog\n        await press(shift(Keys.Tab))\n        assertActiveElement(document.getElementById('a'))\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to close a Dialog using a click on the DialogOverlay',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"toggleOpen\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogOverlay />\n              Contents\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      // Open dialog\n      await click(document.getElementById('trigger'))\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click to close\n      await click(getDialogOverlay())\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not close the Dialog when clicking on contents of the Dialog.Overlay',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"toggleOpen\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogOverlay>\n                <button>hi</button>\n              </DialogOverlay>\n              Contents\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      // Open dialog\n      await click(document.getElementById('trigger'))\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click on an element inside the overlay\n      await click(getByText('hi'))\n\n      // Verify it is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to close the dialog, and re-focus the button when we click outside on the body element',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button @click=\"isOpen = !isOpen\">Trigger</button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              Contents\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      // Open dialog\n      await click(getByText('Trigger'))\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the body to close\n      await click(document.body)\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getByText('Trigger'))\n    })\n  )\n\n  it(\n    'should be possible to close the dialog, and keep focus on the focusable element',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button>Hello</button>\n            <button @click=\"isOpen = !isOpen\">Trigger</button>\n            <Dialog v-if=\"true\" :open=\"isOpen\" @close=\"setIsOpen\">\n              Contents\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      // Open dialog\n      await click(getByText('Trigger'))\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button to close (outside click)\n      await click(getByText('Hello'))\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getByText('Hello'))\n    })\n  )\n\n  it(\n    'should stop propagating click events when clicking on the Dialog.Overlay',\n    suppressConsoleLogs(async () => {\n      let wrapperFn = jest.fn()\n      renderTemplate({\n        template: `\n          <div @click=\"wrapperFn\">\n            <Dialog v-if=\"true\" :open=\"isOpen\" @close=\"setIsOpen\">\n              Contents\n              <DialogOverlay />\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            isOpen,\n            wrapperFn,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n\n      // Click the Dialog.Overlay to close the Dialog\n      await click(getDialogOverlay())\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n    })\n  )\n\n  it(\n    'should be possible to submit a form inside a Dialog',\n    suppressConsoleLogs(async () => {\n      let submitFn = jest.fn()\n      renderTemplate({\n        template: `\n          <Dialog v-if=\"true\" :open=\"isOpen\" @close=\"setIsOpen\">\n            <form @submit.prevent=\"submitFn\">\n              <input type=\"hidden\" value=\"abc\" />\n              <button type=\"submit\">Submit</button>\n            </form>\n            <TabSentinel />\n          </Dialog>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            isOpen,\n            submitFn,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      // Verify that the submitFn function has been called\n      expect(submitFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should stop propagating click events when clicking on an element inside the Dialog',\n    suppressConsoleLogs(async () => {\n      let wrapperFn = jest.fn()\n      renderTemplate({\n        template: `\n          <div @click=\"wrapperFn\">\n            <Dialog v-if=\"true\" :open=\"isOpen\" @close=\"setIsOpen\">\n              Contents\n              <button @click=\"setIsOpen(false)\">Inside</button>\n              <TabSentinel />\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            isOpen,\n            wrapperFn,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n\n      // Click the button inside the Dialog\n      await click(getByText('Inside'))\n\n      // Verify it is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n\n      // Verify that the wrapper function has not been called yet\n      expect(wrapperFn).toHaveBeenCalledTimes(0)\n    })\n  )\n\n  it(\n    'should should be possible to click on removed elements without closing the Dialog',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n            <div ref=\"wrapper\">\n              Contents\n              <!-- Remove this button before the Dialog's mousedown listener fires: -->\n              <button @mousedown=\"wrapper.remove()\">Inside</button>\n              <TabSentinel />\n            </div>\n          </Dialog>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          let wrapper = ref<HTMLDivElement | null>(null)\n          return {\n            isOpen,\n            wrapper,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the Dialog\n      await click(getByText('Inside'))\n\n      // Verify it is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to click on elements created by third party libraries',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n\n      let ThirdPartyLibrary = defineComponent({\n        template: html`\n          <teleport to=\"body\">\n            <button data-lib @click=\"fn\">3rd party button</button>\n          </teleport>\n        `,\n        setup: () => ({ fn }),\n      })\n\n      renderTemplate({\n        components: { ThirdPartyLibrary },\n        template: `\n          <div>\n            <span>Main app</span>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <div>\n                Contents\n                <TabSentinel />\n              </div>\n            </Dialog>\n            <ThirdPartyLibrary />\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the 3rd party library\n      await click(document.querySelector('[data-lib]'))\n\n      // Verify we clicked on the 3rd party button\n      expect(fn).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to focus elements created by third party libraries',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n      let handleFocus = jest.fn()\n\n      let ThirdPartyLibrary = defineComponent({\n        template: html`\n          <teleport to=\"body\">\n            <button data-lib @click=\"fn\">3rd party button</button>\n          </teleport>\n        `,\n        setup: () => ({ fn }),\n      })\n\n      renderTemplate({\n        components: { ThirdPartyLibrary },\n        template: `\n          <div>\n            <span>Main app</span>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <div>\n                Contents\n                <TabSentinel @focus=\"handleFocus\" />\n              </div>\n            </Dialog>\n            <ThirdPartyLibrary />\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            handleFocus,\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the 3rd party library\n      await focus(document.querySelector('[data-lib]'))\n\n      // Verify that the focus is on the 3rd party button, and that we are not redirecting focus to\n      // the dialog again.\n      assertActiveElement(document.querySelector('[data-lib]'))\n\n      // This should only have been called once (when opening the Dialog)\n      expect(handleFocus).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should be possible to click elements inside the dialog when they reside inside a shadow boundary',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n\n      let ShadowChildren = defineComponent({\n        props: ['id', 'buttonId'],\n        setup(props) {\n          let container = ref<HTMLDivElement | null>(null)\n\n          onMounted(() => {\n            if (!container.value || container.value.shadowRoot) {\n              return\n            }\n\n            let shadowRoot = container.value.attachShadow({ mode: 'open' })\n            let button = document.createElement('button')\n            button.id = props.buttonId\n            button.textContent = 'Inside shadow root'\n            button.addEventListener('click', fn)\n            shadowRoot.appendChild(button)\n          })\n\n          return () => h('div', { id: props.id, ref: container })\n        },\n      })\n\n      renderTemplate({\n        components: { ShadowChildren },\n        template: `\n          <div>\n            <button @click=\"setIsOpen(true)\">open</button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen(false)\">\n              <div>\n                <button id=\"btn_outside_light\" @click=\"fn\">\n                  Button\n                </button>\n                <ShadowChildren id=\"outside_shadow\" buttonId=\"btn_outside_shadow\" />\n              </div>\n              <DialogPanel>\n                <button id=\"btn_inside_light\" @click=\"fn\">\n                  Button\n                </button>\n                <ShadowChildren id=\"inside_shadow\" buttonId=\"btn_inside_shadow\" />\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            fn,\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (light DOM)\n      await click(document.querySelector('#btn_inside_light'))\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (shadow DOM)\n      await click(\n        document.querySelector('#inside_shadow')?.shadowRoot?.querySelector('#btn_inside_shadow') ??\n          null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(2)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button outside the dialog (shadow DOM)\n      await click(\n        document\n          .querySelector('#outside_shadow')\n          ?.shadowRoot?.querySelector('#btn_outside_shadow') ?? null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(3)\n\n      // Verify the dialog is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click elements inside the dialog when they reside inside a shadow boundary',\n    suppressConsoleLogs(async () => {\n      let fn = jest.fn()\n\n      let ShadowChildren = defineComponent({\n        props: ['id', 'buttonId'],\n        setup(props) {\n          let container = ref<HTMLDivElement | null>(null)\n\n          onMounted(() => {\n            if (!container.value || container.value.shadowRoot) {\n              return\n            }\n\n            let shadowRoot = container.value.attachShadow({ mode: 'open' })\n            let button = document.createElement('button')\n            button.id = props.buttonId\n            button.textContent = 'Inside shadow root'\n            button.addEventListener('click', fn)\n            shadowRoot.appendChild(button)\n          })\n\n          return () => h('div', { id: props.id, ref: container })\n        },\n      })\n\n      renderTemplate({\n        components: { ShadowChildren },\n        template: `\n          <div>\n            <button @click=\"setIsOpen(true)\">open</button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen(false)\">\n              <div>\n                <button id=\"btn_outside_light\" @click=\"fn\">\n                  Button\n                </button>\n                <ShadowChildren id=\"outside_shadow\" buttonId=\"btn_outside_shadow\" />\n              </div>\n              <DialogPanel>\n                <button id=\"btn_inside_light\" @click=\"fn\">\n                  Button\n                </button>\n                <ShadowChildren id=\"inside_shadow\" buttonId=\"btn_inside_shadow\" />\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(true)\n          return {\n            fn,\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n          }\n        },\n      })\n\n      await nextFrame()\n\n      // Verify it is open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (light DOM)\n      await click(document.querySelector('#btn_inside_light'))\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(1)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button inside the dialog (shadow DOM)\n      await click(\n        document.querySelector('#inside_shadow')?.shadowRoot?.querySelector('#btn_inside_shadow') ??\n          null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(2)\n\n      // Verify the dialog is still open\n      assertDialog({ state: DialogState.Visible })\n\n      // Click the button outside the dialog (shadow DOM)\n      await click(\n        document\n          .querySelector('#outside_shadow')\n          ?.shadowRoot?.querySelector('#btn_outside_shadow') ?? null\n      )\n\n      // Verify the button was clicked\n      expect(fn).toHaveBeenCalledTimes(3)\n\n      // Verify the dialog is closed\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should close the Dialog if we click outside the DialogPanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"toggleOpen\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogBackdrop />\n              <DialogPanel>\n                <TabSentinel />\n              </DialogPanel>\n              <button id=\"outside\">Outside, technically</button>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('outside'))\n\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not close the Dialog if we click inside the DialogPanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"toggleOpen\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogBackdrop />\n              <DialogPanel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('inside'))\n\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should not close the dialog if opened during mouse up',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @mouseup.capture=\"toggleOpen\">\n              Trigger\n            </button>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogBackdrop />\n              <DialogPanel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('inside'))\n\n      assertDialog({ state: DialogState.Visible })\n    })\n  )\n\n  it(\n    'should not close the dialog if click starts inside the dialog but ends outside',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <div>\n            <button id=\"trigger\" @click=\"toggleOpen\">\n              Trigger\n            </button>\n            <div id=\"i-am-outside\">this thing</div>\n            <Dialog :open=\"isOpen\" @close=\"setIsOpen\">\n              <DialogBackdrop />\n              <DialogPanel>\n                <button id=\"inside\">Inside</button>\n                <TabSentinel />\n              </DialogPanel>\n            </Dialog>\n          </div>\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return {\n            isOpen,\n            setIsOpen(value: boolean) {\n              isOpen.value = value\n            },\n            toggleOpen() {\n              isOpen.value = !isOpen.value\n            },\n          }\n        },\n      })\n\n      // Open the dialog\n      await click(document.getElementById('trigger'))\n\n      assertDialog({ state: DialogState.Visible })\n\n      // Start a click inside the dialog and end it outside\n      await mouseDrag(document.getElementById('inside'), document.getElementById('i-am-outside'))\n\n      // It should not have hidden\n      assertDialog({ state: DialogState.Visible })\n\n      await click(document.getElementById('i-am-outside'))\n\n      // It's gone\n      assertDialog({ state: DialogState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Nesting', () => {\n  let Nested: ConcreteComponent = defineComponent({\n    components: { Dialog, DialogOverlay },\n    emits: ['close'],\n    props: ['open', 'level', 'renderWhen'],\n    setup(props, { emit }) {\n      let showChild = ref(false)\n      function onClose() {\n        emit('close', false)\n      }\n\n      return () => {\n        let level = props.level ?? 1\n        return h(Dialog, { open: props.open ?? true, onClose }, () => [\n          h(DialogOverlay),\n          h('div', [\n            h('p', `Level: ${level}`),\n            h('button', { onClick: () => (showChild.value = true) }, `Open ${level + 1} a`),\n            h('button', { onClick: () => (showChild.value = true) }, `Open ${level + 1} b`),\n            h('button', { onClick: () => (showChild.value = true) }, `Open ${level + 1} c`),\n          ]),\n          props.renderWhen === 'always'\n            ? h(Nested, {\n                open: showChild.value,\n                onClose: () => (showChild.value = false),\n                level: level + 1,\n                renderWhen: props.renderWhen,\n              })\n            : showChild.value &&\n              h(Nested, {\n                open: true,\n                onClose: () => (showChild.value = false),\n                level: level + 1,\n                renderWhen: props.renderWhen,\n              }),\n        ])\n      }\n    },\n  })\n\n  it.each`\n    strategy                            | when         | action\n    ${'with `Escape`'}                  | ${'mounted'} | ${() => press(Keys.Escape)}\n    ${'with `Outside Click`'}           | ${'mounted'} | ${() => click(document.body)}\n    ${'with `Click on Dialog.Overlay`'} | ${'mounted'} | ${() => click(getDialogOverlays().pop()!)}\n    ${'with `Escape`'}                  | ${'always'}  | ${() => press(Keys.Escape)}\n    ${'with `Outside Click`'}           | ${'always'}  | ${() => click(document.body)}\n  `(\n    'should be possible to open nested Dialog components and close them $strategy',\n    async ({ when, action }) => {\n      renderTemplate({\n        components: { Nested },\n        template: `\n          <button @click=\"isOpen = true\">Open 1</button>\n          <Nested v-if=\"isOpen\" @close=\"isOpen = false\" renderWhen=\"${when}\" />\n        `,\n        setup() {\n          let isOpen = ref(false)\n          return { isOpen }\n        },\n      })\n\n      // Verify we have no open dialogs\n      expect(getDialogs()).toHaveLength(0)\n\n      // Open Dialog 1\n      await click(getByText('Open 1'))\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 a` has focus\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Open Dialog 2 via the second button\n      await click(getByText('Open 2 b'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Verify that the `Open 3 a` has focus\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 b` button got focused again\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Open Dialog 2 via button b\n      await click(getByText('Open 2 b'))\n\n      // Verify that the `Open 3 a` has focus\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Open Dialog 3 via button c\n      await click(getByText('Open 3 c'))\n\n      // Verify that the `Open 4 a` has focus\n      assertActiveElement(getByText('Open 4 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 4 a'))\n\n      // Verify that we have 3 open dialogs\n      expect(getDialogs()).toHaveLength(3)\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that the `Open 3 c` button got focused again\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 3 c'))\n\n      // Verify that we have 2 open dialogs\n      expect(getDialogs()).toHaveLength(2)\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 1 open dialog\n      expect(getDialogs()).toHaveLength(1)\n\n      // Verify that the `Open 2 b` button got focused again\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 c'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 a'))\n\n      // Verify that we can tab around\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Open 2 b'))\n\n      // Close the top most Dialog\n      await action()\n\n      // Verify that we have 0 open dialogs\n      expect(getDialogs()).toHaveLength(0)\n\n      // Verify that the `Open 1` button got focused again\n      assertActiveElement(getByText('Open 1'))\n    }\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/dialog/dialog.ts",
    "content": "// WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/\nimport {\n  computed,\n  defineComponent,\n  h,\n  inject,\n  nextTick,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  watchEffect,\n  type InjectionKey,\n  type PropType,\n  type Ref,\n} from 'vue'\nimport { FocusTrap } from '../../components/focus-trap/focus-trap'\nimport { useDocumentOverflowLockedEffect } from '../../hooks/document-overflow/use-document-overflow'\nimport { useEventListener } from '../../hooks/use-event-listener'\nimport { useId } from '../../hooks/use-id'\nimport { useInert } from '../../hooks/use-inert'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useRootContainers } from '../../hooks/use-root-containers'\nimport { State, useOpenClosed } from '../../internal/open-closed'\nimport { ForcePortalRoot } from '../../internal/portal-force-root'\nimport { StackMessage, useStackProvider } from '../../internal/stack-context'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport { match } from '../../utils/match'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { Features, render } from '../../utils/render'\nimport { Description, useDescriptions } from '../description/description'\nimport { Portal, PortalGroup, useNestedPortals } from '../portal/portal'\n\nenum DialogStates {\n  Open,\n  Closed,\n}\n\ninterface StateDefinition {\n  dialogState: Ref<DialogStates>\n\n  titleId: Ref<string | null>\n  panelRef: Ref<HTMLDivElement | null>\n\n  setTitleId(id: string | null): void\n\n  close(): void\n}\n\nlet DialogContext = Symbol('DialogContext') as InjectionKey<StateDefinition>\n\nfunction useDialogContext(component: string) {\n  let context = inject(DialogContext, null)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Dialog /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDialogContext)\n    throw err\n  }\n  return context\n}\n\n// ---\n\nlet Missing = 'DC8F892D-2EBD-447C-A4C8-A03058436FF4'\n\nexport let Dialog = defineComponent({\n  name: 'Dialog',\n  inheritAttrs: false, // Manually handling this\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    open: { type: [Boolean, String], default: Missing },\n    initialFocus: { type: Object as PropType<HTMLElement | null>, default: null },\n    id: { type: String, default: () => `headlessui-dialog-${useId()}` },\n    role: { type: String as PropType<'dialog' | 'alertdialog'>, default: 'dialog' },\n  },\n  emits: { close: (_close: boolean) => true },\n  setup(props, { emit, attrs, slots, expose }) {\n    let ready = ref(false)\n    onMounted(() => {\n      ready.value = true\n    })\n\n    let didWarnOnRole = false\n    let role = computed(() => {\n      if (props.role === 'dialog' || props.role === 'alertdialog') {\n        return props.role\n      }\n\n      if (!didWarnOnRole) {\n        didWarnOnRole = true\n        console.warn(\n          `Invalid role [${role}] passed to <Dialog />. Only \\`dialog\\` and and \\`alertdialog\\` are supported. Using \\`dialog\\` instead.`\n        )\n      }\n\n      return 'dialog'\n    })\n\n    let nestedDialogCount = ref(0)\n\n    let usesOpenClosedState = useOpenClosed()\n    let open = computed(() => {\n      if (props.open === Missing && usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n      return props.open\n    })\n\n    let internalDialogRef = ref<HTMLDivElement | null>(null)\n\n    let ownerDocument = computed(() => getOwnerDocument(internalDialogRef))\n\n    expose({ el: internalDialogRef, $el: internalDialogRef })\n\n    // Validations\n    let hasOpen = props.open !== Missing || usesOpenClosedState !== null\n\n    if (!hasOpen) {\n      throw new Error(`You forgot to provide an \\`open\\` prop to the \\`Dialog\\`.`)\n    }\n\n    if (typeof open.value !== 'boolean') {\n      throw new Error(\n        `You provided an \\`open\\` prop to the \\`Dialog\\`, but the value is not a boolean. Received: ${\n          open.value === Missing ? undefined : props.open\n        }`\n      )\n    }\n\n    let dialogState = computed(() =>\n      !ready.value ? DialogStates.Closed : open.value ? DialogStates.Open : DialogStates.Closed\n    )\n    let enabled = computed(() => dialogState.value === DialogStates.Open)\n\n    let hasNestedDialogs = computed(() => nestedDialogCount.value > 1) // 1 is the current dialog\n    let hasParentDialog = inject(DialogContext, null) !== null\n    let [portals, PortalWrapper] = useNestedPortals()\n    let {\n      resolveContainers: resolveRootContainers,\n      mainTreeNodeRef,\n      MainTreeNode,\n    } = useRootContainers({\n      portals,\n      defaultContainers: [computed(() => api.panelRef.value ?? internalDialogRef.value)],\n    })\n\n    // If there are multiple dialogs, then you can be the root, the leaf or one\n    // in between. We only care about whether you are the top most one or not.\n    let position = computed(() => (!hasNestedDialogs.value ? 'leaf' : 'parent'))\n\n    // When the `Dialog` is wrapped in a `Transition` (or another Headless UI component that exposes\n    // the OpenClosed state) then we get some information via context about its state. When the\n    // `Transition` is about to close, then the `State.Closing` state will be exposed. This allows us\n    // to enable/disable certain functionality in the `Dialog` upfront instead of waiting until the\n    // `Transition` is done transitioning.\n    let isClosing = computed(() =>\n      usesOpenClosedState !== null\n        ? (usesOpenClosedState.value & State.Closing) === State.Closing\n        : false\n    )\n\n    // Ensure other elements can't be interacted with\n    let inertOthersEnabled = computed(() => {\n      // Nested dialogs should not modify the `inert` property, only the root one should.\n      if (hasParentDialog) return false\n      if (isClosing.value) return false\n      return enabled.value\n    })\n    let resolveRootOfMainTreeNode = computed(() => {\n      return (Array.from(ownerDocument.value?.querySelectorAll('body > *') ?? []).find((root) => {\n        // Skip the portal root, we don't want to make that one inert\n        if (root.id === 'headlessui-portal-root') return false\n\n        // Find the root of the main tree node\n        return root.contains(dom(mainTreeNodeRef)) && root instanceof HTMLElement\n      }) ?? null) as HTMLElement | null\n    })\n    useInert(resolveRootOfMainTreeNode, inertOthersEnabled)\n\n    // This would mark the parent dialogs as inert\n    let inertParentDialogs = computed(() => {\n      if (hasNestedDialogs.value) return true\n      return enabled.value\n    })\n    let resolveRootOfParentDialog = computed(() => {\n      return (Array.from(\n        ownerDocument.value?.querySelectorAll('[data-headlessui-portal]') ?? []\n      ).find((root) => root.contains(dom(mainTreeNodeRef)) && root instanceof HTMLElement) ??\n        null) as HTMLElement | null\n    })\n    useInert(resolveRootOfParentDialog, inertParentDialogs)\n\n    useStackProvider({\n      type: 'Dialog',\n      enabled: computed(() => dialogState.value === DialogStates.Open),\n      element: internalDialogRef,\n      onUpdate: (message, type) => {\n        if (type !== 'Dialog') return\n\n        return match(message, {\n          [StackMessage.Add]: () => (nestedDialogCount.value += 1),\n          [StackMessage.Remove]: () => (nestedDialogCount.value -= 1),\n        })\n      },\n    })\n\n    let describedby = useDescriptions({\n      name: 'DialogDescription',\n      slot: computed(() => ({ open: open.value })),\n    })\n\n    let titleId = ref<StateDefinition['titleId']['value']>(null)\n\n    let api = {\n      titleId,\n      panelRef: ref(null),\n      dialogState,\n      setTitleId(id: string | null) {\n        if (titleId.value === id) return\n        titleId.value = id\n      },\n      close() {\n        emit('close', false)\n      },\n    }\n\n    provide(DialogContext, api)\n\n    // Handle outside click\n    let outsideClickEnabled = computed(() => {\n      if (!enabled.value) return false\n      if (hasNestedDialogs.value) return false\n      return true\n    })\n    useOutsideClick(\n      resolveRootContainers,\n      (event, target) => {\n        event.preventDefault()\n        api.close()\n        nextTick(() => target?.focus())\n      },\n      outsideClickEnabled\n    )\n\n    // Handle `Escape` to close\n    let escapeToCloseEnabled = computed(() => {\n      if (hasNestedDialogs.value) return false\n      if (dialogState.value !== DialogStates.Open) return false\n      return true\n    })\n    useEventListener(ownerDocument.value?.defaultView, 'keydown', (event) => {\n      if (!escapeToCloseEnabled.value) return\n      if (event.defaultPrevented) return\n      if (event.key !== Keys.Escape) return\n\n      event.preventDefault()\n      event.stopPropagation()\n      api.close()\n    })\n\n    // Scroll lock\n    let scrollLockEnabled = computed(() => {\n      if (isClosing.value) return false\n      if (dialogState.value !== DialogStates.Open) return false\n      if (hasParentDialog) return false\n      return true\n    })\n    useDocumentOverflowLockedEffect(ownerDocument, scrollLockEnabled, (meta) => ({\n      containers: [...(meta.containers ?? []), resolveRootContainers],\n    }))\n\n    // Trigger close when the FocusTrap gets hidden\n    watchEffect((onInvalidate) => {\n      if (dialogState.value !== DialogStates.Open) return\n      let container = dom(internalDialogRef)\n      if (!container) return\n\n      let observer = new ResizeObserver((entries) => {\n        for (let entry of entries) {\n          let rect = entry.target.getBoundingClientRect()\n          if (rect.x === 0 && rect.y === 0 && rect.width === 0 && rect.height === 0) {\n            api.close()\n          }\n        }\n      })\n\n      observer.observe(container)\n\n      onInvalidate(() => observer.disconnect())\n    })\n\n    return () => {\n      let { id, open: _, initialFocus, ...theirProps } = props\n      let ourProps = {\n        // Manually passthrough the attributes, because Vue can't automatically pass\n        // it to the underlying div because of all the wrapper components below.\n        ...attrs,\n        ref: internalDialogRef,\n        id,\n        role: role.value,\n        'aria-modal': dialogState.value === DialogStates.Open ? true : undefined,\n        'aria-labelledby': titleId.value,\n        'aria-describedby': describedby.value,\n      }\n\n      let slot = { open: dialogState.value === DialogStates.Open }\n\n      return h(ForcePortalRoot, { force: true }, () => [\n        h(Portal, () =>\n          h(PortalGroup, { target: internalDialogRef.value }, () =>\n            h(ForcePortalRoot, { force: false }, () =>\n              h(\n                FocusTrap,\n                {\n                  initialFocus,\n                  containers: resolveRootContainers,\n                  features: enabled.value\n                    ? match(position.value, {\n                        parent: FocusTrap.features.RestoreFocus,\n                        leaf: FocusTrap.features.All & ~FocusTrap.features.FocusLock,\n                      })\n                    : FocusTrap.features.None,\n                },\n                () =>\n                  h(PortalWrapper, {}, () =>\n                    render({\n                      ourProps,\n                      theirProps: { ...theirProps, ...attrs },\n                      slot,\n                      attrs,\n                      slots,\n                      visible: dialogState.value === DialogStates.Open,\n                      features: Features.RenderStrategy | Features.Static,\n                      name: 'Dialog',\n                    })\n                  )\n              )\n            )\n          )\n        ),\n        h(MainTreeNode),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let DialogOverlay = defineComponent({\n  name: 'DialogOverlay',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    id: { type: String, default: () => `headlessui-dialog-overlay-${useId()}` },\n  },\n  setup(props, { attrs, slots }) {\n    let api = useDialogContext('DialogOverlay')\n\n    function handleClick(event: MouseEvent) {\n      if (event.target !== event.currentTarget) return\n      event.preventDefault()\n      event.stopPropagation()\n      api.close()\n    }\n\n    return () => {\n      let { id, ...theirProps } = props\n      let ourProps = {\n        id,\n        'aria-hidden': true,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot: { open: api.dialogState.value === DialogStates.Open },\n        attrs,\n        slots,\n        name: 'DialogOverlay',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let DialogBackdrop = defineComponent({\n  name: 'DialogBackdrop',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    id: { type: String, default: () => `headlessui-dialog-backdrop-${useId()}` },\n  },\n  inheritAttrs: false,\n  setup(props, { attrs, slots, expose }) {\n    let api = useDialogContext('DialogBackdrop')\n    let internalBackdropRef = ref(null)\n\n    expose({ el: internalBackdropRef, $el: internalBackdropRef })\n\n    onMounted(() => {\n      if (api.panelRef.value === null) {\n        throw new Error(\n          `A <DialogBackdrop /> component is being used, but a <DialogPanel /> component is missing.`\n        )\n      }\n    })\n\n    return () => {\n      let { id, ...theirProps } = props\n      let ourProps = {\n        id,\n        ref: internalBackdropRef,\n        'aria-hidden': true,\n      }\n\n      return h(ForcePortalRoot, { force: true }, () =>\n        h(Portal, () =>\n          render({\n            ourProps,\n            theirProps: { ...attrs, ...theirProps },\n            slot: { open: api.dialogState.value === DialogStates.Open },\n            attrs,\n            slots,\n            name: 'DialogBackdrop',\n          })\n        )\n      )\n    }\n  },\n})\n\n// ---\n\nexport let DialogPanel = defineComponent({\n  name: 'DialogPanel',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    id: { type: String, default: () => `headlessui-dialog-panel-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useDialogContext('DialogPanel')\n\n    expose({ el: api.panelRef, $el: api.panelRef })\n\n    function handleClick(event: MouseEvent) {\n      event.stopPropagation()\n    }\n\n    return () => {\n      let { id, ...theirProps } = props\n      let ourProps = {\n        id,\n        ref: api.panelRef,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot: { open: api.dialogState.value === DialogStates.Open },\n        attrs,\n        slots,\n        name: 'DialogPanel',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let DialogTitle = defineComponent({\n  name: 'DialogTitle',\n  props: {\n    as: { type: [Object, String], default: 'h2' },\n    id: { type: String, default: () => `headlessui-dialog-title-${useId()}` },\n  },\n  setup(props, { attrs, slots }) {\n    let api = useDialogContext('DialogTitle')\n\n    onMounted(() => {\n      api.setTitleId(props.id)\n      onUnmounted(() => api.setTitleId(null))\n    })\n\n    return () => {\n      let { id, ...theirProps } = props\n      let ourProps = { id }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot: { open: api.dialogState.value === DialogStates.Open },\n        attrs,\n        slots,\n        name: 'DialogTitle',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let DialogDescription = Description\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/disclosure/disclosure.srr.test.ts",
    "content": "import { defineComponent } from 'vue'\nimport { html } from '../../test-utils/html'\nimport { renderHydrate, renderSSR } from '../../test-utils/ssr'\nimport { Disclosure, DisclosureButton, DisclosurePanel } from './disclosure'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nlet Example = defineComponent({\n  components: { Disclosure, DisclosureButton, DisclosurePanel },\n  template: html`\n    <Disclosure>\n      <DisclosureButton>Toggle</DisclosureButton>\n      <DisclosurePanel>Contents</DisclosurePanel>\n    </Disclosure>\n  `,\n})\n\ndescribe('Rendering', () => {\n  describe('SSR', () => {\n    it('should be possible to server side render the Disclosure in a closed state', async () => {\n      let { contents } = await renderSSR(Example)\n\n      expect(contents).toContain(`Toggle`)\n      expect(contents).not.toContain('aria-controls')\n      expect(contents).not.toContain(`aria-expanded=\"true\"`)\n      expect(contents).not.toContain(`Contents`)\n    })\n\n    it('should be possible to server side render the Disclosure in an open state', async () => {\n      let { contents } = await renderSSR(Example, { defaultOpen: true })\n\n      let ariaControlsId = contents.match(\n        /aria-controls=\"(headlessui-disclosure-panel-[^\"]+)\"/\n      )?.[1]\n      let id = contents.match(/id=\"(headlessui-disclosure-panel-[^\"]+)\"/)?.[1]\n\n      expect(id).toEqual(ariaControlsId)\n\n      expect(contents).toContain(`Toggle`)\n      expect(contents).toContain('aria-controls')\n      expect(contents).toContain(`aria-expanded=\"true\"`)\n      expect(contents).toContain(`Contents`)\n    })\n  })\n\n  describe('Hydration', () => {\n    it('should be possible to server side render the Disclosure in a closed state', async () => {\n      let { contents } = await renderHydrate(Example)\n\n      expect(contents).toContain(`Toggle`)\n      expect(contents).not.toContain('aria-controls')\n      expect(contents).not.toContain(`aria-expanded=\"true\"`)\n      expect(contents).not.toContain(`Contents`)\n    })\n\n    it('should be possible to server side render the Disclosure in an open state', async () => {\n      let { contents } = await renderHydrate(Example, { defaultOpen: true })\n\n      let ariaControlsId = contents.match(\n        /aria-controls=\"(headlessui-disclosure-panel-[^\"]+)\"/\n      )?.[1]\n      let id = contents.match(/id=\"(headlessui-disclosure-panel-[^\"]+)\"/)?.[1]\n\n      expect(id).toEqual(ariaControlsId)\n\n      expect(contents).toContain(`Toggle`)\n      expect(contents).toContain('aria-controls')\n      expect(contents).toContain(`aria-expanded=\"true\"`)\n      expect(contents).toContain(`Contents`)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/disclosure/disclosure.test.ts",
    "content": "import { defineComponent, h, nextTick, ref, watch } from 'vue'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  DisclosureState,\n  assertActiveElement,\n  assertDisclosureButton,\n  assertDisclosurePanel,\n  getByText,\n  getDisclosureButton,\n  getDisclosurePanel,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, MouseButton, click, press } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { Disclosure, DisclosureButton, DisclosurePanel } from './disclosure'\n\njest.mock('../../hooks/use-id')\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({\n  Disclosure,\n  DisclosureButton,\n  DisclosurePanel,\n})\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['DisclosureButton', DisclosureButton],\n    ['DisclosurePanel', DisclosurePanel],\n  ])(\n    'should error when we are using a <%s /> without a parent <Disclosure />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <Disclosure /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Disclosure without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Disclosure', () => {\n    it(\n      'should be possible to render a Disclosure using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure v-slot=\"{ open }\">\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>Panel is: {{open ? 'open' : 'closed'}}</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })\n      })\n    )\n\n    it('should be possible to render a Disclosure in an open state by default', async () => {\n      renderTemplate(html`\n        <Disclosure v-slot=\"{ open }\" defaultOpen>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel>Panel is: {{open ? 'open' : 'closed'}}</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertDisclosureButton({\n        state: DisclosureState.Visible,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.Visible, textContent: 'Panel is: open' })\n\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n    })\n\n    it(\n      'should expose a close function that closes the disclosure',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure v-slot=\"{ close }\">\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>\n              <button @click=\"close()\">Close me</button>\n            </DisclosurePanel>\n          </Disclosure>\n        `)\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the Disclosure.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button id=\"test\">restorable</button>\n            <Disclosure v-slot=\"{ close }\">\n              <DisclosureButton>Trigger</DisclosureButton>\n              <DisclosurePanel>\n                <button @click=\"close(document.getElementById('test'))\">Close me</button>\n              </DisclosurePanel>\n            </Disclosure>\n          `,\n          setup: () => ({ document }),\n        })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a ref',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button ref=\"elementRef\">restorable</button>\n            <Disclosure v-slot=\"{ close }\">\n              <DisclosureButton>Trigger</DisclosureButton>\n              <DisclosurePanel>\n                <button @click=\"close(elementRef)\">Close me</button>}\n              </DisclosurePanel>\n            </Disclosure>\n          `,\n          setup: () => ({ elementRef: ref() }),\n        })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n  })\n\n  describe('DisclosureButton', () => {\n    it(\n      'should be possible to render a DisclosureButton using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton v-slot=\"slot\">{{JSON.stringify(slot)}}</DisclosureButton>\n            <DisclosurePanel></DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n          textContent: JSON.stringify({ open: false }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a DisclosureButton using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton as=\"div\" role=\"button\" v-slot=\"slot\">\n              {{JSON.stringify(slot)}}\n            </DisclosureButton>\n            <DisclosurePanel />\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n          textContent: JSON.stringify({ open: false }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertDisclosurePanel({ state: DisclosureState.Visible })\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton> Trigger </DisclosureButton>\n          </Disclosure>\n        `)\n\n        expect(getDisclosureButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton type=\"submit\"> Trigger </DisclosureButton>\n          </Disclosure>\n        `)\n\n        expect(getDisclosureButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Disclosure>\n                <DisclosureButton :as=\"CustomButton\"> Trigger </DisclosureButton>\n              </Disclosure>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('button', { ...props }),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getDisclosureButton()).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton as=\"div\"> Trigger </DisclosureButton>\n          </Disclosure>\n        `)\n\n        expect(getDisclosureButton()).not.toHaveAttribute('type')\n      })\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Disclosure>\n                <DisclosureButton :as=\"CustomButton\"> Trigger </DisclosureButton>\n              </Disclosure>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('div', props),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getDisclosureButton()).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n\n  describe('DisclosurePanel', () => {\n    it(\n      'should be possible to render DisclosurePanel using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel v-slot=\"slot\">{{JSON.stringify(slot)}}</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        await click(getDisclosureButton())\n\n        assertDisclosureButton({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n\n    it('should be possible to always render the DisclosurePanel if we provide it a `static` prop', () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel static>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      // Let's verify that the Disclosure is already there\n      expect(getDisclosurePanel()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the DisclosurePanel', async () => {\n      renderTemplate(\n        `\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel :unmount=\"false\">Contents</DisclosurePanel>\n          </Disclosure>\n        `\n      )\n\n      // TODO: Figure out a way so that we _don't_ require this.\n      await new Promise<void>(nextTick)\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleHidden })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })\n\n      // Let's open the Disclosure, to see if it is not hidden anymore\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      // Let's re-click the Disclosure, to see if it is hidden again\n      await click(getDisclosureButton())\n\n      assertDisclosureButton({ state: DisclosureState.InvisibleHidden })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleHidden })\n    })\n\n    it(\n      'should expose a close function that closes the disclosure',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel v-slot=\"{ close }\">\n              <button @click=\"close()\">Close me</button>\n            </DisclosurePanel>\n          </Disclosure>\n        `)\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the Disclosure.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button id=\"test\">restorable</button>\n            <Disclosure>\n              <DisclosureButton>Trigger</DisclosureButton>\n              <DisclosurePanel v-slot=\"{ close }\">\n                <button @click=\"close(document.getElementById('test'))\">Close me</button>\n              </DisclosurePanel>\n            </Disclosure>\n          `,\n          setup: () => ({ document }),\n        })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the disclosure and restores to a ref',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button ref=\"elementRef\">restorable</button>\n            <Disclosure>\n              <DisclosureButton>Trigger</DisclosureButton>\n              <DisclosurePanel v-slot=\"{ close }\">\n                <button @click=\"close(elementRef)\">Close me</button>}\n              </DisclosurePanel>\n            </Disclosure>\n          `,\n          setup: () => ({ elementRef: ref() }),\n        })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getDisclosureButton())\n\n        // Open the disclosure\n        await click(getDisclosureButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the disclosure is closed\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n  })\n})\n\ndescribe('Composition', () => {\n  let OpenClosedWrite = defineComponent({\n    props: { open: { type: Boolean } },\n    setup(props, { slots }) {\n      useOpenClosedProvider(ref(props.open ? State.Open : State.Closed))\n      return () => slots.default?.()\n    },\n  })\n\n  let OpenClosedRead = defineComponent({\n    emits: ['read'],\n    setup(_, { slots, emit }) {\n      let state = useOpenClosed()\n      watch([state], ([value]) => emit('read', value))\n      return () => slots.default?.()\n    },\n  })\n\n  it(\n    'should always open the DisclosurePanel because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: `\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <OpenClosedWrite :open=\"true\">\n              <DisclosurePanel v-slot=\"data\">\n                {{JSON.stringify(data)}}\n              </DisclosurePanel>\n            </OpenClosedWrite>\n          </Disclosure>\n        `,\n      })\n\n      // Verify the Disclosure is visible\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      // Let's try and open the Disclosure\n      await click(getDisclosureButton())\n\n      // Verify the Disclosure is still visible\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n    })\n  )\n\n  it(\n    'should always close the DisclosurePanel because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: `\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <OpenClosedWrite :open=\"false\">\n              <DisclosurePanel v-slot=\"data\">\n                {{JSON.stringify(data)}}\n              </DisclosurePanel>\n            </OpenClosedWrite>\n          </Disclosure>\n        `,\n      })\n\n      // Verify the Disclosure is hidden\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Let's try and open the Disclosure\n      await click(getDisclosureButton())\n\n      // Verify the Disclosure is still hidden\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to read the OpenClosed state',\n    suppressConsoleLogs(async () => {\n      let readFn = jest.fn()\n      renderTemplate({\n        components: { OpenClosedRead },\n        template: `\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <OpenClosedRead @read=\"readFn\">\n              <DisclosurePanel></DisclosurePanel>\n            </OpenClosedRead>\n          </Disclosure>\n        `,\n        setup() {\n          return { readFn }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Disclosure is hidden\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Let's toggle the Disclosure 3 times\n      await click(getDisclosureButton())\n      await click(getDisclosureButton())\n      await click(getDisclosureButton())\n\n      // Verify the Disclosure is visible\n      assertDisclosurePanel({ state: DisclosureState.Visible })\n\n      expect(readFn).toHaveBeenCalledTimes(3)\n      expect(readFn).toHaveBeenNthCalledWith(1, State.Open)\n      expect(readFn).toHaveBeenNthCalledWith(2, State.Closed)\n      expect(readFn).toHaveBeenNthCalledWith(3, State.Open)\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the Disclosure with Enter',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Open disclosure\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-panel-2' },\n        })\n\n        // Close disclosure\n        await press(Keys.Enter)\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to open the disclosure with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton disabled>Trigger</DisclosureButton>\n            <DisclosurePanel>Content</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Try to open the disclosure\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the disclosure with Enter when the disclosure is open',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Open disclosure\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-panel-2' },\n        })\n\n        // Close disclosure\n        await press(Keys.Enter)\n\n        // Verify it is closed again\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the disclosure with Space',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Open disclosure\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-panel-2' },\n        })\n      })\n    )\n\n    it(\n      'should not be possible to open the disclosure with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton disabled>Trigger</DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Try to open the disclosure\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the disclosure with Space when the disclosure is open',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Disclosure>\n            <DisclosureButton>Trigger</DisclosureButton>\n            <DisclosurePanel>Contents</DisclosurePanel>\n          </Disclosure>\n        `)\n\n        assertDisclosureButton({\n          state: DisclosureState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-disclosure-button-1' },\n        })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n        // Focus the button\n        getDisclosureButton()?.focus()\n\n        // Open disclosure\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertDisclosureButton({ state: DisclosureState.Visible })\n        assertDisclosurePanel({\n          state: DisclosureState.Visible,\n          attributes: { id: 'headlessui-disclosure-panel-2' },\n        })\n\n        // Close disclosure\n        await press(Keys.Space)\n\n        // Verify it is closed again\n        assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n        assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to open a disclosure on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Open disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is open\n      assertDisclosureButton({ state: DisclosureState.Visible })\n      assertDisclosurePanel({\n        state: DisclosureState.Visible,\n        attributes: { id: 'headlessui-disclosure-panel-2' },\n      })\n    })\n  )\n\n  it(\n    'should not be possible to open a disclosure on right click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Open disclosure\n      await click(getDisclosureButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open a disclosure on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton disabled>Trigger</DisclosureButton>\n          <DisclosurePanel>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Try to open the disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is still closed\n      assertDisclosureButton({\n        state: DisclosureState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-disclosure-button-1' },\n      })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a disclosure on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Trigger</DisclosureButton>\n          <DisclosurePanel>Contents</DisclosurePanel>\n        </Disclosure>\n      `)\n\n      // Open disclosure\n      await click(getDisclosureButton())\n\n      // Verify it is open\n      assertDisclosureButton({ state: DisclosureState.Visible })\n\n      // Click to close\n      await click(getDisclosureButton())\n\n      // Verify it is closed\n      assertDisclosureButton({ state: DisclosureState.InvisibleUnmounted })\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close the Disclosure by clicking on a DisclosureButton inside a DisclosurePanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Disclosure>\n          <DisclosureButton>Open</DisclosureButton>\n          <DisclosurePanel>\n            <DisclosureButton>Close</DisclosureButton>\n          </DisclosurePanel>\n        </Disclosure>\n      `)\n\n      // Open the disclosure\n      await click(getDisclosureButton())\n\n      let closeBtn = getByText('Close')\n\n      expect(closeBtn).not.toHaveAttribute('id')\n      expect(closeBtn).not.toHaveAttribute('aria-controls')\n      expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n      // The close button should close the disclosure\n      await click(closeBtn)\n\n      // Verify it is closed\n      assertDisclosurePanel({ state: DisclosureState.InvisibleUnmounted })\n\n      // Verify we restored the Open button\n      assertActiveElement(getDisclosureButton())\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/disclosure/disclosure.ts",
    "content": "// WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/\nimport {\n  computed,\n  defineComponent,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  watchEffect,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport { match } from '../../utils/match'\nimport { Features, render } from '../../utils/render'\n\nenum DisclosureStates {\n  Open,\n  Closed,\n}\n\ninterface StateDefinition {\n  // State\n  disclosureState: Ref<DisclosureStates>\n  panel: Ref<HTMLElement | null>\n  panelId: Ref<string | null>\n  button: Ref<HTMLButtonElement | null>\n  buttonId: Ref<string | null>\n\n  // State mutators\n  toggleDisclosure(): void\n  closeDisclosure(): void\n\n  // Exposed functions\n  close(focusableElement: HTMLElement | Ref<HTMLElement | null>): void\n}\n\nlet DisclosureContext = Symbol('DisclosureContext') as InjectionKey<StateDefinition>\n\nfunction useDisclosureContext(component: string) {\n  let context = inject(DisclosureContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Disclosure /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureContext)\n    throw err\n  }\n\n  return context\n}\n\nlet DisclosurePanelContext = Symbol('DisclosurePanelContext') as InjectionKey<Ref<string | null>>\nfunction useDisclosurePanelContext() {\n  return inject(DisclosurePanelContext, null)\n}\n\n// ---\n\nexport let Disclosure = defineComponent({\n  name: 'Disclosure',\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    defaultOpen: { type: [Boolean], default: false },\n  },\n  setup(props, { slots, attrs }) {\n    let disclosureState = ref<StateDefinition['disclosureState']['value']>(\n      props.defaultOpen ? DisclosureStates.Open : DisclosureStates.Closed\n    )\n    let panelRef = ref<StateDefinition['panel']['value']>(null)\n    let buttonRef = ref<StateDefinition['button']['value']>(null)\n\n    let api = {\n      buttonId: ref(`headlessui-disclosure-button-${useId()}`),\n      panelId: ref(`headlessui-disclosure-panel-${useId()}`),\n      disclosureState,\n      panel: panelRef,\n      button: buttonRef,\n      toggleDisclosure() {\n        disclosureState.value = match(disclosureState.value, {\n          [DisclosureStates.Open]: DisclosureStates.Closed,\n          [DisclosureStates.Closed]: DisclosureStates.Open,\n        })\n      },\n      closeDisclosure() {\n        if (disclosureState.value === DisclosureStates.Closed) return\n        disclosureState.value = DisclosureStates.Closed\n      },\n      close(focusableElement: HTMLElement | Ref<HTMLElement | null>) {\n        api.closeDisclosure()\n\n        let restoreElement = (() => {\n          if (!focusableElement) return dom(api.button)\n          if (focusableElement instanceof HTMLElement) return focusableElement\n          if (focusableElement.value instanceof HTMLElement) return dom(focusableElement)\n\n          return dom(api.button)\n        })()\n\n        restoreElement?.focus()\n      },\n    } as StateDefinition\n\n    provide(DisclosureContext, api)\n    useOpenClosedProvider(\n      computed(() => {\n        return match(disclosureState.value, {\n          [DisclosureStates.Open]: State.Open,\n          [DisclosureStates.Closed]: State.Closed,\n        })\n      })\n    )\n\n    return () => {\n      let { defaultOpen: _, ...theirProps } = props\n      let slot = { open: disclosureState.value === DisclosureStates.Open, close: api.close }\n      return render({\n        theirProps,\n        ourProps: {},\n        slot,\n        slots,\n        attrs,\n        name: 'Disclosure',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let DisclosureButton = defineComponent({\n  name: 'DisclosureButton',\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    disabled: { type: [Boolean], default: false },\n    id: { type: String, default: null },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useDisclosureContext('DisclosureButton')\n\n    let panelContext = useDisclosurePanelContext()\n    let isWithinPanel = computed(() =>\n      panelContext === null ? false : panelContext.value === api.panelId.value\n    )\n\n    onMounted(() => {\n      if (isWithinPanel.value) return\n      if (props.id !== null) {\n        api.buttonId.value = props.id\n      }\n    })\n    onUnmounted(() => {\n      if (isWithinPanel.value) return\n      api.buttonId.value = null\n    })\n\n    let internalButtonRef = ref<HTMLButtonElement | null>(null)\n\n    expose({ el: internalButtonRef, $el: internalButtonRef })\n\n    if (!isWithinPanel.value) {\n      watchEffect(() => {\n        api.button.value = internalButtonRef.value\n      })\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      internalButtonRef\n    )\n\n    function handleClick() {\n      if (props.disabled) return\n\n      if (isWithinPanel.value) {\n        api.toggleDisclosure()\n        dom(api.button)?.focus()\n      } else {\n        api.toggleDisclosure()\n      }\n    }\n    function handleKeyDown(event: KeyboardEvent) {\n      if (props.disabled) return\n\n      if (isWithinPanel.value) {\n        switch (event.key) {\n          case Keys.Space:\n          case Keys.Enter:\n            event.preventDefault()\n            event.stopPropagation()\n            api.toggleDisclosure()\n            dom(api.button)?.focus()\n            break\n        }\n      } else {\n        switch (event.key) {\n          case Keys.Space:\n          case Keys.Enter:\n            event.preventDefault()\n            event.stopPropagation()\n            api.toggleDisclosure()\n            break\n        }\n      }\n    }\n    function handleKeyUp(event: KeyboardEvent) {\n      switch (event.key) {\n        case Keys.Space:\n          // Required for firefox, event.preventDefault() in handleKeyDown for\n          // the Space key doesn't cancel the handleKeyUp, which in turn\n          // triggers a *click*.\n          event.preventDefault()\n          break\n      }\n    }\n\n    return () => {\n      let slot = { open: api.disclosureState.value === DisclosureStates.Open }\n      let { id, ...theirProps } = props\n      let ourProps = isWithinPanel.value\n        ? {\n            ref: internalButtonRef,\n            type: type.value,\n            onClick: handleClick,\n            onKeydown: handleKeyDown,\n          }\n        : {\n            id: api.buttonId.value ?? id,\n            ref: internalButtonRef,\n            type: type.value,\n            'aria-expanded': api.disclosureState.value === DisclosureStates.Open,\n            'aria-controls':\n              api.disclosureState.value === DisclosureStates.Open || dom(api.panel)\n                ? api.panelId.value\n                : undefined,\n            disabled: props.disabled ? true : undefined,\n            onClick: handleClick,\n            onKeydown: handleKeyDown,\n            onKeyup: handleKeyUp,\n          }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'DisclosureButton',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let DisclosurePanel = defineComponent({\n  name: 'DisclosurePanel',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    id: { type: String, default: null },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useDisclosureContext('DisclosurePanel')\n\n    onMounted(() => {\n      if (props.id !== null) {\n        api.panelId.value = props.id\n      }\n    })\n    onUnmounted(() => {\n      api.panelId.value = null\n    })\n\n    expose({ el: api.panel, $el: api.panel })\n\n    provide(DisclosurePanelContext, api.panelId)\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.disclosureState.value === DisclosureStates.Open\n    })\n\n    return () => {\n      let slot = { open: api.disclosureState.value === DisclosureStates.Open, close: api.close }\n      let { id, ...theirProps } = props\n      let ourProps = { id: api.panelId.value ?? id, ref: api.panel }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'DisclosurePanel',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts",
    "content": "import { onMounted, ref } from 'vue'\nimport { assertActiveElement, getByText } from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, click, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate } from '../../test-utils/vue-testing-library'\nimport { FocusTrap } from './focus-trap'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nconst renderTemplate = createRenderTemplate({\n  FocusTrap,\n})\n\nit('should focus the first focusable element inside the FocusTrap', async () => {\n  renderTemplate(html`\n    <FocusTrap>\n      <button>Trigger</button>\n    </FocusTrap>\n  `)\n\n  await nextFrame()\n\n  assertActiveElement(getByText('Trigger'))\n})\n\nit('should focus the autoFocus element inside the FocusTrap if that exists', async () => {\n  renderTemplate({\n    template: html`\n      <FocusTrap>\n        <input id=\"a\" type=\"text\" />\n        <input id=\"b\" type=\"text\" ref=\"autofocus\" />\n        <input id=\"c\" type=\"text\" />\n      </FocusTrap>\n    `,\n    setup() {\n      let autofocus = ref<HTMLElement | null>(null)\n      onMounted(() => {\n        autofocus.value?.focus?.()\n      })\n      return { autofocus }\n    },\n  })\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('b'))\n})\n\nit('should focus the initialFocus element inside the FocusTrap if that exists', async () => {\n  renderTemplate({\n    template: html`\n      <FocusTrap :initialFocus=\"initialFocusRef\">\n        <input id=\"a\" type=\"text\" />\n        <input id=\"b\" type=\"text\" />\n        <input id=\"c\" type=\"text\" ref=\"initialFocusRef\" />\n      </FocusTrap>\n    `,\n    setup() {\n      let initialFocusRef = ref(null)\n      return { initialFocusRef }\n    },\n  })\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('c'))\n})\n\nit('should focus the initialFocus element inside the FocusTrap even if another element has autoFocus', async () => {\n  renderTemplate({\n    template: html`\n      <FocusTrap :initialFocus=\"initialFocusRef\">\n        <input id=\"a\" type=\"text\" />\n        <input id=\"b\" type=\"text\" autofocus />\n        <input id=\"c\" type=\"text\" ref=\"initialFocusRef\" />\n      </FocusTrap>\n    `,\n    setup() {\n      let initialFocusRef = ref(null)\n      return { initialFocusRef }\n    },\n  })\n\n  await nextFrame()\n\n  assertActiveElement(document.getElementById('c'))\n})\n\nit('should warn when there is no focusable element inside the FocusTrap', async () => {\n  expect.assertions(1)\n  let spy = jest.spyOn(console, 'warn').mockImplementation(jest.fn())\n\n  renderTemplate(html`\n    <FocusTrap>\n      <span>Nothing to see here...</span>\n    </FocusTrap>\n  `)\n\n  await nextFrame()\n\n  expect(spy.mock.calls[0][0]).toBe('There are no focusable elements inside the <FocusTrap />')\n  spy.mockReset()\n})\n\nit(\n  'should not be possible to programmatically escape the focus trap',\n  suppressConsoleLogs(async () => {\n    renderTemplate({\n      template: html`\n        <div>\n          <input id=\"a\" autofocus />\n\n          <FocusTrap>\n            <input id=\"b\" />\n            <input id=\"c\" />\n            <input id=\"d\" />\n          </FocusTrap>\n        </div>\n      `,\n    })\n\n    await nextFrame()\n\n    let [a, b, c, d] = Array.from(document.querySelectorAll('input'))\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Try to move focus\n    a?.focus()\n\n    // Ensure that input-c is still the active element\n    assertActiveElement(c)\n\n    // Click on an element within the FocusTrap\n    await click(b)\n\n    // Ensure that input-b is the active element\n    assertActiveElement(b)\n\n    // Try to move focus again\n    a?.focus()\n\n    // Ensure that input-b is still the active element\n    assertActiveElement(b)\n\n    // Focus on an element within the FocusTrap\n    d?.focus()\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Try to move focus again\n    a?.focus()\n\n    // Ensure that input-d is still the active element\n    assertActiveElement(d)\n  })\n)\n\nit('should restore the previously focused element, before entering the FocusTrap, after the FocusTrap unmounts', async () => {\n  renderTemplate({\n    template: html`\n      <div>\n        <input id=\"item-1\" ref=\"autoFocusRef\" />\n        <button id=\"item-2\" @click=\"visible = true\">Open modal</button>\n\n        <FocusTrap v-if=\"visible\">\n          <button id=\"item-3\" @click=\"visible = false\">Close</button>\n        </FocusTrap>\n      </div>\n    `,\n    setup() {\n      let visible = ref(false)\n      let autoFocusRef = ref<HTMLElement | null>(null)\n      onMounted(() => {\n        autoFocusRef.value?.focus()\n      })\n      return { visible, autoFocusRef }\n    },\n  })\n\n  await nextFrame()\n\n  // The input should have focus by default because of the autoFocus prop\n  assertActiveElement(document.getElementById('item-1'))\n\n  // Open the modal\n  await click(document.getElementById('item-2')) // This will also focus this button\n\n  // Ensure that the first item inside the focus trap is focused\n  assertActiveElement(document.getElementById('item-3'))\n\n  // Close the modal\n  await click(document.getElementById('item-3'))\n\n  // Ensure that we restored focus correctly\n  assertActiveElement(document.getElementById('item-2'))\n})\n\nit('should stay in the FocusTrap when using `tab`, if there is only 1 focusable element', async () => {\n  renderTemplate({\n    template: html`\n      <div>\n        <button>Before</button>\n        <FocusTrap>\n          <button id=\"item-a\">Item A</button>\n        </FocusTrap>\n        <button>After</button>\n      </div>\n    `,\n  })\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should stay in the FocusTrap when using `shift+tab`, if there is only 1 focusable element', async () => {\n  renderTemplate({\n    template: html`\n      <div>\n        <button>Before</button>\n        <FocusTrap>\n          <button id=\"item-a\">Item A</button>\n        </FocusTrap>\n        <button>After</button>\n      </div>\n    `,\n  })\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous (loop around!)\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible to tab to the next focusable element within the focus trap', async () => {\n  renderTemplate(html`\n    <div>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\">Item C</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  `)\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-c'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible to shift+tab to the previous focusable element within the focus trap', async () => {\n  renderTemplate(html`\n    <div>\n      <button>Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\">Item C</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  `)\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Previous (loop around!)\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-c'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Previous\n  await press(shift(Keys.Tab))\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should skip the initial \"hidden\" elements within the focus trap', async () => {\n  renderTemplate(html`\n    <div>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\" style=\"display:none\">Item A</button>\n        <button id=\"item-b\" style=\"display:none\">Item B</button>\n        <button id=\"item-c\">Item C</button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  `)\n\n  await nextFrame()\n\n  // Item C should be focused because the FocusTrap had to skip the first 2\n  assertActiveElement(document.getElementById('item-c'))\n})\n\nit('should be possible skip \"hidden\" elements within the focus trap', async () => {\n  renderTemplate(html`\n    <div>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\" style=\"display:none\">Item C</button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  `)\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Notice that we skipped item-c\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-d'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit('should be possible skip disabled elements within the focus trap', async () => {\n  renderTemplate(html`\n    <div>\n      <button id=\"before\">Before</button>\n      <FocusTrap>\n        <button id=\"item-a\">Item A</button>\n        <button id=\"item-b\">Item B</button>\n        <button id=\"item-c\" disabled>Item C</button>\n        <button id=\"item-d\">Item D</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  `)\n\n  await nextFrame()\n\n  // Item A should be focused because the FocusTrap will focus the first item\n  assertActiveElement(document.getElementById('item-a'))\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-b'))\n\n  // Notice that we skipped item-c\n\n  // Next\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-d'))\n\n  // Loop around!\n  await press(Keys.Tab)\n  assertActiveElement(document.getElementById('item-a'))\n})\n\nit(\n  'should not be possible to escape the FocusTrap due to strange tabIndex usage',\n  suppressConsoleLogs(async () => {\n    renderTemplate(html`\n      <div>\n        <div :tabindex=\"-1\">\n          <input :tabindex=\"2\" id=\"a\" />\n          <input :tabindex=\"1\" id=\"b\" />\n        </div>\n\n        <FocusTrap>\n          <input :tabindex=\"1\" id=\"c\" />\n          <input id=\"d\" />\n        </FocusTrap>\n      </div>\n    `)\n\n    await nextFrame()\n\n    let [_a, _b, c, d] = Array.from(document.querySelectorAll('input'))\n\n    // First item in the FocusTrap should be the active one\n    assertActiveElement(c)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Tab to the next item\n    await press(Keys.Tab)\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Let's go the other way\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-d is the active element\n    assertActiveElement(d)\n\n    // Tab to the previous item\n    await press(shift(Keys.Tab))\n\n    // Ensure that input-c is the active element\n    assertActiveElement(c)\n  })\n)\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  onMounted,\n  onUnmounted,\n  ref,\n  watch,\n  watchEffect,\n  type PropType,\n  type Ref,\n} from 'vue'\nimport { useEventListener } from '../../hooks/use-event-listener'\nimport { Direction as TabDirection, useTabDirection } from '../../hooks/use-tab-direction'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { history } from '../../utils/active-element-history'\nimport { dom } from '../../utils/dom'\nimport { Focus, FocusResult, focusElement, focusIn } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { render } from '../../utils/render'\n\ntype Containers =\n  // Lazy resolved containers\n  | (() => Iterable<HTMLElement>)\n\n  // List of containers\n  | Ref<Set<Ref<HTMLElement | null>>>\n\nfunction resolveContainers(containers?: Containers): Set<HTMLElement> {\n  if (!containers) return new Set<HTMLElement>()\n  if (typeof containers === 'function') return new Set(containers())\n\n  let all = new Set<HTMLElement>()\n  for (let container of containers.value) {\n    let el = dom(container)\n    if (el instanceof HTMLElement) {\n      all.add(el)\n    }\n  }\n  return all\n}\n\nenum Features {\n  /** No features enabled for the focus trap. */\n  None = 1 << 0,\n\n  /** Ensure that we move focus initially into the container. */\n  InitialFocus = 1 << 1,\n\n  /** Ensure that pressing `Tab` and `Shift+Tab` is trapped within the container. */\n  TabLock = 1 << 2,\n\n  /** Ensure that programmatically moving focus outside of the container is disallowed. */\n  FocusLock = 1 << 3,\n\n  /** Ensure that we restore the focus when unmounting the focus trap. */\n  RestoreFocus = 1 << 4,\n\n  /** Enable all features. */\n  All = InitialFocus | TabLock | FocusLock | RestoreFocus,\n}\n\nexport let FocusTrap = Object.assign(\n  defineComponent({\n    name: 'FocusTrap',\n    props: {\n      as: { type: [Object, String], default: 'div' },\n      initialFocus: { type: Object as PropType<HTMLElement | null>, default: null },\n      features: { type: Number as PropType<Features>, default: Features.All },\n      containers: {\n        type: [Object, Function] as PropType<Containers>,\n        default: ref(new Set()),\n      },\n    },\n    inheritAttrs: false,\n    setup(props, { attrs, slots, expose }) {\n      let container = ref<HTMLElement | null>(null)\n\n      expose({ el: container, $el: container })\n\n      let ownerDocument = computed(() => getOwnerDocument(container))\n\n      let mounted = ref(false)\n      onMounted(() => (mounted.value = true))\n      onUnmounted(() => (mounted.value = false))\n\n      useRestoreFocus(\n        { ownerDocument },\n        computed(() => mounted.value && Boolean(props.features & Features.RestoreFocus))\n      )\n      let previousActiveElement = useInitialFocus(\n        { ownerDocument, container, initialFocus: computed(() => props.initialFocus) },\n        computed(() => mounted.value && Boolean(props.features & Features.InitialFocus))\n      )\n      useFocusLock(\n        {\n          ownerDocument,\n          container,\n          containers: props.containers,\n          previousActiveElement,\n        },\n        computed(() => mounted.value && Boolean(props.features & Features.FocusLock))\n      )\n\n      let direction = useTabDirection()\n      function handleFocus(e: FocusEvent) {\n        let el = dom(container) as HTMLElement\n        if (!el) return\n\n        // TODO: Cleanup once we are using real browser tests\n        let wrapper = process.env.NODE_ENV === 'test' ? microTask : (cb: Function) => cb()\n        wrapper(() => {\n          match(direction.value, {\n            [TabDirection.Forwards]: () => {\n              focusIn(el, Focus.First, { skipElements: [e.relatedTarget as HTMLElement] })\n            },\n            [TabDirection.Backwards]: () => {\n              focusIn(el, Focus.Last, { skipElements: [e.relatedTarget as HTMLElement] })\n            },\n          })\n        })\n      }\n\n      let recentlyUsedTabKey = ref(false)\n      function handleKeyDown(e: KeyboardEvent) {\n        if (e.key === 'Tab') {\n          recentlyUsedTabKey.value = true\n          requestAnimationFrame(() => {\n            recentlyUsedTabKey.value = false\n          })\n        }\n      }\n\n      function handleBlur(e: FocusEvent) {\n        if (!mounted.value) return\n        let allContainers = resolveContainers(props.containers)\n        if (dom(container) instanceof HTMLElement) allContainers.add(dom(container)!)\n\n        let relatedTarget = e.relatedTarget\n        if (!(relatedTarget instanceof HTMLElement)) return\n\n        // Known guards, leave them alone!\n        if (relatedTarget.dataset.headlessuiFocusGuard === 'true') {\n          return\n        }\n\n        // Blur is triggered due to focus on relatedTarget, and the relatedTarget is not inside any\n        // of the dialog containers. In other words, let's move focus back in!\n        if (!contains(allContainers, relatedTarget)) {\n          // Was the blur invoked via the keyboard? Redirect to the next in line.\n          if (recentlyUsedTabKey.value) {\n            focusIn(\n              dom(container) as HTMLElement,\n              match(direction.value, {\n                [TabDirection.Forwards]: () => Focus.Next,\n                [TabDirection.Backwards]: () => Focus.Previous,\n              }) | Focus.WrapAround,\n              { relativeTo: e.target as HTMLElement }\n            )\n          }\n\n          // It was invoked via something else (e.g.: click, programmatically, ...). Redirect to the\n          // previous active item in the FocusTrap\n          else if (e.target instanceof HTMLElement) {\n            focusElement(e.target)\n          }\n        }\n      }\n\n      return () => {\n        let slot = {}\n        let ourProps = { ref: container, onKeydown: handleKeyDown, onFocusout: handleBlur }\n        let { features, initialFocus, containers: _containers, ...theirProps } = props\n\n        return h(Fragment, [\n          Boolean(features & Features.TabLock) &&\n            h(Hidden, {\n              as: 'button',\n              type: 'button',\n              'data-headlessui-focus-guard': true,\n              onFocus: handleFocus,\n              features: HiddenFeatures.Focusable,\n            }),\n          render({\n            ourProps,\n            theirProps: { ...attrs, ...theirProps },\n            slot,\n            attrs,\n            slots,\n            name: 'FocusTrap',\n          }),\n          Boolean(features & Features.TabLock) &&\n            h(Hidden, {\n              as: 'button',\n              type: 'button',\n              'data-headlessui-focus-guard': true,\n              onFocus: handleFocus,\n              features: HiddenFeatures.Focusable,\n            }),\n        ])\n      }\n    },\n  }),\n  { features: Features }\n)\n\nfunction useRestoreElement(enabled: Ref<boolean>) {\n  let localHistory = ref<HTMLElement[]>(history.slice())\n\n  watch(\n    [enabled],\n    ([newEnabled], [oldEnabled]) => {\n      // We are disabling the restore element, so we need to clear it.\n      if (oldEnabled === true && newEnabled === false) {\n        // However, let's schedule it in a microTask, so that we can still read the value in the\n        // places where we are restoring the focus.\n        microTask(() => {\n          localHistory.value.splice(0)\n        })\n      }\n\n      // We are enabling the restore element, so we need to set it to the last \"focused\" element.\n      else if (oldEnabled === false && newEnabled === true) {\n        localHistory.value = history.slice()\n      }\n    },\n    { flush: 'post' }\n  )\n\n  // We want to return the last element that is still connected to the DOM, so we can restore the\n  // focus to it.\n  return () => {\n    return localHistory.value.find((x) => x != null && x.isConnected) ?? null\n  }\n}\n\nfunction useRestoreFocus(\n  { ownerDocument }: { ownerDocument: Ref<Document | null> },\n  enabled: Ref<boolean>\n) {\n  let getRestoreElement = useRestoreElement(enabled)\n\n  // Restore the focus to the previous element\n  onMounted(() => {\n    watchEffect(\n      () => {\n        if (enabled.value) return\n\n        if (ownerDocument.value?.activeElement === ownerDocument.value?.body) {\n          focusElement(getRestoreElement())\n        }\n      },\n      { flush: 'post' }\n    )\n  })\n\n  // Restore the focus when we unmount the component\n  onUnmounted(() => {\n    if (!enabled.value) return\n\n    focusElement(getRestoreElement())\n  })\n}\n\nfunction useInitialFocus(\n  {\n    ownerDocument,\n    container,\n    initialFocus,\n  }: {\n    ownerDocument: Ref<Document | null>\n    container: Ref<HTMLElement | null>\n    initialFocus?: Ref<HTMLElement | null>\n  },\n  enabled: Ref<boolean>\n) {\n  let previousActiveElement = ref<HTMLElement | null>(null)\n\n  let mounted = ref(false)\n  onMounted(() => (mounted.value = true))\n  onUnmounted(() => (mounted.value = false))\n\n  onMounted(() => {\n    watch(\n      // Handle initial focus\n      [container, initialFocus, enabled],\n      (newValues, prevValues) => {\n        if (newValues.every((value, idx) => prevValues?.[idx] === value)) return\n        if (!enabled.value) return\n\n        let containerElement = dom(container)\n        if (!containerElement) return\n\n        // Delaying the focus to the next microtask ensures that a few conditions are true:\n        // - The container is rendered\n        // - Transitions could be started\n        // If we don't do this, then focusing an element will immediately cancel any transitions. This\n        // is not ideal because transitions will look broken.\n        // There is an additional issue with doing this immediately. The FocusTrap is used inside a\n        // Dialog, the Dialog is rendered inside of a Portal and the Portal is rendered at the end of\n        // the `document.body`. This means that the moment we call focus, the browser immediately\n        // tries to focus the element, which will still be at the bottom resulting in the page to\n        // scroll down. Delaying this will prevent the page to scroll down entirely.\n        microTask(() => {\n          if (!mounted.value) {\n            return\n          }\n\n          let initialFocusElement = dom(initialFocus)\n\n          let activeElement = ownerDocument.value?.activeElement as HTMLElement\n\n          if (initialFocusElement) {\n            if (initialFocusElement === activeElement) {\n              previousActiveElement.value = activeElement\n              return // Initial focus ref is already the active element\n            }\n          } else if (containerElement!.contains(activeElement)) {\n            previousActiveElement.value = activeElement\n            return // Already focused within Dialog\n          }\n\n          // Try to focus the initialFocus ref\n          if (initialFocusElement) {\n            focusElement(initialFocusElement)\n          } else {\n            if (focusIn(containerElement!, Focus.First | Focus.NoScroll) === FocusResult.Error) {\n              console.warn('There are no focusable elements inside the <FocusTrap />')\n            }\n          }\n\n          previousActiveElement.value = ownerDocument.value?.activeElement as HTMLElement\n        })\n      },\n      { immediate: true, flush: 'post' }\n    )\n  })\n\n  return previousActiveElement\n}\n\nfunction useFocusLock(\n  {\n    ownerDocument,\n    container,\n    containers,\n    previousActiveElement,\n  }: {\n    ownerDocument: Ref<Document | null>\n    container: Ref<HTMLElement | null>\n    containers: Containers\n    previousActiveElement: Ref<HTMLElement | null>\n  },\n  enabled: Ref<boolean>\n) {\n  // Prevent programmatically escaping\n  useEventListener(\n    ownerDocument.value?.defaultView,\n    'focus',\n    (event) => {\n      if (!enabled.value) return\n\n      let allContainers = resolveContainers(containers)\n      if (dom(container) instanceof HTMLElement) allContainers.add(dom(container)!)\n\n      let previous = previousActiveElement.value\n      if (!previous) return\n\n      let toElement = event.target as HTMLElement | null\n\n      if (toElement && toElement instanceof HTMLElement) {\n        if (!contains(allContainers, toElement)) {\n          event.preventDefault()\n          event.stopPropagation()\n          focusElement(previous)\n        } else {\n          previousActiveElement.value = toElement\n          focusElement(toElement)\n        }\n      } else {\n        focusElement(previousActiveElement.value)\n      }\n    },\n    true\n  )\n}\n\nfunction contains(containers: Set<HTMLElement>, element: HTMLElement) {\n  for (let container of containers) {\n    if (container.contains(element)) return true\n  }\n\n  return false\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/label/__snapshots__/label.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to update a prop from the parent and it should reflect in the Label component 1`] = `\"<div><div aria-labelledby=\\\\\"headlessui-label-1\\\\\"><label data-count=\\\\\"0\\\\\" id=\\\\\"headlessui-label-1\\\\\">I am a label</label><button>+1</button></div></div>\"`;\n\nexports[`should be possible to update a prop from the parent and it should reflect in the Label component 2`] = `\"<div><div aria-labelledby=\\\\\"headlessui-label-1\\\\\"><label data-count=\\\\\"1\\\\\" id=\\\\\"headlessui-label-1\\\\\">I am a label</label><button>+1</button></div></div>\"`;\n\nexports[`should be possible to use useLabels and a single Label, and have them linked 1`] = `\"<div><div aria-labelledby=\\\\\"headlessui-label-1\\\\\"><label id=\\\\\"headlessui-label-1\\\\\">I am a label</label><span>Contents</span></div></div>\"`;\n\nexports[`should be possible to use useLabels and multiple Label components, and have them linked 1`] = `\"<div><div aria-labelledby=\\\\\"headlessui-label-1 headlessui-label-2\\\\\"><label id=\\\\\"headlessui-label-1\\\\\">I am a label</label><span>Contents</span><label id=\\\\\"headlessui-label-2\\\\\">I am also a label</label></div></div>\"`;\n\nexports[`should be possible to use useLabels without using a Label 1`] = `\"<div><div>No label</div></div>\"`;\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/label/label.test.ts",
    "content": "import { defineComponent, h, nextTick, ref } from 'vue'\nimport { getByText } from '../../test-utils/accessibility-assertions'\nimport { click } from '../../test-utils/interactions'\nimport { render } from '../../test-utils/vue-testing-library'\nimport { Label, useLabels } from './label'\n\nfunction format(input: Element | null | string) {\n  if (input === null) throw new Error('input is null')\n  let contents = (typeof input === 'string' ? input : (input as HTMLElement).outerHTML).trim()\n  return contents\n}\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nit('should be possible to use useLabels without using a Label', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Label },\n      setup() {\n        let labelledby = useLabels()\n\n        return () => h('div', [h('div', { 'aria-labelledby': labelledby.value }, ['No label'])])\n      },\n    })\n  )\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to use useLabels and a single Label, and have them linked', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Label },\n      setup() {\n        let labelledby = useLabels()\n\n        return () =>\n          h('div', [\n            h('div', { 'aria-labelledby': labelledby.value }, [\n              h(Label, () => 'I am a label'),\n              h('span', 'Contents'),\n            ]),\n          ])\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to use useLabels and multiple Label components, and have them linked', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Label },\n      setup() {\n        let labelledby = useLabels()\n\n        return () =>\n          h('div', [\n            h('div', { 'aria-labelledby': labelledby.value }, [\n              h(Label, () => 'I am a label'),\n              h('span', 'Contents'),\n              h(Label, () => 'I am also a label'),\n            ]),\n          ])\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n\nit('should be possible to update a prop from the parent and it should reflect in the Label component', async () => {\n  let { container } = render(\n    defineComponent({\n      components: { Label },\n      setup() {\n        let count = ref(0)\n        let labelledby = useLabels({ props: { 'data-count': count } })\n\n        return () =>\n          h('div', [\n            h('div', { 'aria-labelledby': labelledby.value }, [\n              h(Label, () => 'I am a label'),\n              h('button', { onClick: () => count.value++ }, '+1'),\n            ]),\n          ])\n      },\n    })\n  )\n\n  await new Promise<void>(nextTick)\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n\n  await click(getByText('+1'))\n\n  expect(format(container.firstElementChild)).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/label/label.ts",
    "content": "import {\n  computed,\n  defineComponent,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  unref,\n  type ComputedRef,\n  type InjectionKey,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport { render } from '../../utils/render'\n\n// ---\n\nlet LabelContext = Symbol('LabelContext') as InjectionKey<{\n  register(value: string): () => void\n  slot: Record<string, unknown>\n  name: string\n  props: Record<string, unknown>\n}>\n\nfunction useLabelContext() {\n  let context = inject(LabelContext, null)\n  if (context === null) {\n    let err = new Error('You used a <Label /> component, but it is not inside a parent.')\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useLabelContext)\n    throw err\n  }\n  return context\n}\n\nexport function useLabels({\n  slot = {},\n  name = 'Label',\n  props = {},\n}: {\n  slot?: Record<string, unknown>\n  name?: string\n  props?: Record<string, unknown>\n} = {}): ComputedRef<string | undefined> {\n  let labelIds = ref<string[]>([])\n  function register(value: string) {\n    labelIds.value.push(value)\n\n    return () => {\n      let idx = labelIds.value.indexOf(value)\n      if (idx === -1) return\n      labelIds.value.splice(idx, 1)\n    }\n  }\n\n  provide(LabelContext, { register, slot, name, props })\n\n  // The actual id's as string or undefined.\n  return computed(() => (labelIds.value.length > 0 ? labelIds.value.join(' ') : undefined))\n}\n\n// ---\n\nexport let Label = defineComponent({\n  name: 'Label',\n  props: {\n    as: { type: [Object, String], default: 'label' },\n    passive: { type: [Boolean], default: false },\n    id: { type: String, default: () => `headlessui-label-${useId()}` },\n  },\n  setup(myProps, { slots, attrs }) {\n    let context = useLabelContext()\n\n    onMounted(() => onUnmounted(context.register(myProps.id)))\n\n    return () => {\n      let { name = 'Label', slot = {}, props = {} } = context\n      let { id, passive, ...theirProps } = myProps\n      let ourProps = {\n        ...Object.entries(props).reduce(\n          (acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),\n          {}\n        ),\n        id,\n      }\n\n      if (passive) {\n        // @ts-expect-error props are dynamic via context, some components will provide an onClick\n        // then we can delete it.\n        delete ourProps['onClick']\n\n        // @ts-expect-error props are dynamic via context, some components will provide an htmlFor\n        // then we can delete it.\n        delete ourProps['htmlFor']\n\n        // @ts-expect-error props are dynamic via context, some components will provide an onClick\n        // then we can delete it.\n        delete theirProps['onClick']\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name,\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/listbox/listbox.test.tsx",
    "content": "import { defineComponent, h, nextTick, reactive, ref, watch } from 'vue'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  ListboxMode,\n  ListboxState,\n  assertActiveElement,\n  assertActiveListboxOption,\n  assertListbox,\n  assertListboxButton,\n  assertListboxButtonLinkedWithListbox,\n  assertListboxButtonLinkedWithListboxLabel,\n  assertListboxLabel,\n  assertListboxOption,\n  assertNoActiveListboxOption,\n  assertNoSelectedListboxOption,\n  getByText,\n  getListbox,\n  getListboxButton,\n  getListboxButtons,\n  getListboxLabel,\n  getListboxOptions,\n  getListboxes,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport {\n  Keys,\n  MouseButton,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from './listbox'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nconst renderTemplate = createRenderTemplate({\n  Listbox,\n  ListboxLabel,\n  ListboxButton,\n  ListboxOptions,\n  ListboxOption,\n})\n\ndescribe('safeguards', () => {\n  it.each([\n    ['ListboxButton', ListboxButton],\n    ['ListboxLabel', ListboxLabel],\n    ['ListboxOptions', ListboxOptions],\n    ['ListboxOption', ListboxOption],\n  ])(\n    'should error when we are using a <%s /> without a parent <Listbox />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <Listbox /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Listbox without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('Listbox', () => {\n    it(\n      'should be possible to render a Listbox using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" v-slot=\"{ open }\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions v-show=\"open\">\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to disable a Listbox',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" disabled>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await press(Keys.Enter, getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not crash in multiple mode',\n      suppressConsoleLogs(async () => {\n        let options = [\n          { id: 1, name: 'Alice' },\n          { id: 2, name: 'Bob' },\n          { id: 3, name: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <Listbox multiple name=\"abc\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption\n                  v-for=\"option in options\"\n                  :key=\"option.id\"\n                  :value=\"option\"\n                  v-slot=\"data\"\n                  >{{ JSON.stringify(data) }}</ListboxOption\n                >\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => {\n            let value = ref(options[1])\n            return { options, value }\n          },\n        })\n\n        await click(getListboxButton())\n        let [alice, bob, charlie] = getListboxOptions()\n\n        await click(alice)\n        await click(bob)\n        await click(charlie)\n      })\n    )\n\n    describe('Equality', () => {\n      let options = [\n        { id: 1, name: 'Alice' },\n        { id: 2, name: 'Bob' },\n        { id: 3, name: 'Charlie' },\n      ]\n\n      it(\n        'should use object equality by default',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ListboxOption\n                  >\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref(options[1])\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a field',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" by=\"id\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ListboxOption\n                  >\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to compare objects by a comparator function',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" :by=\"compare\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption\n                    v-for=\"option in options\"\n                    :key=\"option.id\"\n                    :value=\"option\"\n                    v-slot=\"data\"\n                    >{{ JSON.stringify(data) }}</ListboxOption\n                  >\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value, compare: (a: any, z: any) => a.id === z.id }\n            },\n          })\n\n          await click(getListboxButton())\n\n          let bob = getListboxOptions()[1]\n          expect(bob).toHaveTextContent(\n            JSON.stringify({ active: true, selected: true, disabled: false })\n          )\n        })\n      )\n\n      it(\n        'should be possible to use the by prop (as a string) with a null initial value',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" by=\"id\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ListboxOption>\n                  <ListboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ListboxOption>\n                  <ListboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ListboxOption>\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref(null)\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[1])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      // TODO: Does this test prove anything useful?\n      it(\n        'should be possible to use the by prop (as a string) with a null listbox option',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" by=\"id\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption :value=\"null\" disabled>Please select an option</ListboxOption>\n                  <ListboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ListboxOption>\n                  <ListboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ListboxOption>\n                  <ListboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ListboxOption>\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref(null)\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n          let [disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'true')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[3])\n          await click(getListboxButton())\n          ;[disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'false')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[disabled, alice, bob, charlie] = getListboxOptions()\n          expect(disabled).toHaveAttribute('aria-selected', 'false')\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (single mode)',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" by=\"id\">\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ListboxOption>\n                  <ListboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ListboxOption>\n                  <ListboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ListboxOption>\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref({ id: 2, name: 'Bob' })\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n\n          await click(getListboxOptions()[2])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'false')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[1])\n          await click(getListboxButton())\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n\n      it(\n        'should be possible to use completely new objects while rendering (multiple mode)',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\" by=\"id\" multiple>\n                <ListboxButton>Trigger</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ListboxOption>\n                  <ListboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ListboxOption>\n                  <ListboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ListboxOption>\n                </ListboxOptions>\n              </Listbox>\n            `,\n            setup: () => {\n              let value = ref([{ id: 2, name: 'Bob' }])\n              return { options, value }\n            },\n          })\n\n          await click(getListboxButton())\n\n          await click(getListboxOptions()[2])\n          let [alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'true')\n\n          await click(getListboxOptions()[2])\n          ;[alice, bob, charlie] = getListboxOptions()\n          expect(alice).toHaveAttribute('aria-selected', 'false')\n          expect(bob).toHaveAttribute('aria-selected', 'true')\n          expect(charlie).toHaveAttribute('aria-selected', 'false')\n        })\n      )\n    })\n\n    it(\n      'null should be a valid value for the Listbox',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" by=\"id\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption :value=\"{ id: 1, name: 'alice' }\">alice</ListboxOption>\n                <ListboxOption :value=\"{ id: 2, name: 'bob' }\">bob</ListboxOption>\n                <ListboxOption :value=\"{ id: 3, name: 'charlie' }\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: null }),\n        })\n\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n  })\n\n  describe('ListboxLabel', () => {\n    it(\n      'should be possible to render a ListboxLabel using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxLabel v-slot=\"data\">{{JSON.stringify(data)}}</ListboxLabel>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-2' },\n        })\n        assertListboxLabel({\n          attributes: { id: 'headlessui-listbox-label-1' },\n          textContent: JSON.stringify({ open: false, disabled: false }),\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxLabel({\n          attributes: { id: 'headlessui-listbox-label-1' },\n          textContent: JSON.stringify({ open: true, disabled: false }),\n        })\n        assertListbox({ state: ListboxState.Visible })\n        assertListboxButtonLinkedWithListboxLabel()\n      })\n    )\n\n    it(\n      'should be possible to render a ListboxLabel using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxLabel as=\"p\" v-slot=\"data\">{{JSON.stringify(data)}}</ListboxLabel>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxLabel({\n          attributes: { id: 'headlessui-listbox-label-1' },\n          textContent: JSON.stringify({ open: false, disabled: false }),\n          tag: 'p',\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n        assertListboxLabel({\n          attributes: { id: 'headlessui-listbox-label-1' },\n          textContent: JSON.stringify({ open: true, disabled: false }),\n          tag: 'p',\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n  })\n\n  describe('ListboxButton', () => {\n    it(\n      'should be possible to render a ListboxButton using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton v-slot=\"data\">{{JSON.stringify(data)}}</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n          textContent: JSON.stringify({ open: false, disabled: false, value: null }),\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-button-1' },\n          textContent: JSON.stringify({ open: true, disabled: false, value: null }),\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a ListboxButton using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton as=\"div\" role=\"button\" v-slot=\"data\"\n                >{{JSON.stringify(data)}}</ListboxButton\n              >\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n          textContent: JSON.stringify({ open: false, disabled: false, value: null }),\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-button-1' },\n          textContent: JSON.stringify({ open: true, disabled: false, value: null }),\n        })\n        assertListbox({ state: ListboxState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a ListboxButton and a ListboxLabel and see them linked together',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxLabel>Label</ListboxLabel>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        await new Promise(requestAnimationFrame)\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-2' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n        assertListboxButtonLinkedWithListboxLabel()\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getListboxButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton type=\"submit\"> Trigger </ListboxButton>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getListboxButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\">\n                <ListboxButton :as=\"CustomButton\"> Trigger </ListboxButton>\n              </Listbox>\n            `,\n            setup: () => ({\n              value: ref(null),\n              CustomButton: defineComponent({\n                setup: (props) => () => h('button', { ...props }),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getListboxButton()).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton as=\"div\"> Trigger </ListboxButton>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        expect(getListboxButton()).not.toHaveAttribute('type')\n      })\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Listbox v-model=\"value\">\n                <ListboxButton :as=\"CustomButton\"> Trigger </ListboxButton>\n              </Listbox>\n            `,\n            setup: () => ({\n              value: ref(null),\n              CustomButton: defineComponent({\n                setup: (props) => () => h('div', props),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getListboxButton()).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n\n  describe('ListboxOptions', () => {\n    it(\n      'should be possible to render ListboxOptions using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions v-slot=\"data\">\n                <ListboxOption value=\"a\">{{JSON.stringify(data)}}</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({\n          state: ListboxState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertActiveElement(getListbox())\n      })\n    )\n\n    it('should be possible to always render the ListboxOptions if we provide it a `static` prop', () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions static>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Let's verify that the Listbox is already there\n      expect(getListbox()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the ListboxOptions', async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions :unmount=\"false\">\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertListbox({ state: ListboxState.InvisibleHidden })\n\n      // Let's open the Listbox, to see if it is not hidden anymore\n      await click(getListboxButton())\n\n      assertListbox({ state: ListboxState.Visible })\n    })\n  })\n\n  describe('ListboxOption', () => {\n    it(\n      'should be possible to render a ListboxOption using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\" v-slot=\"data\">{{JSON.stringify(data)}}</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        await click(getListboxButton())\n\n        assertListboxButton({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({\n          state: ListboxState.Visible,\n          textContent: JSON.stringify({ active: false, selected: false, disabled: false }),\n        })\n      })\n    )\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    let props = reactive({ hide: false })\n\n    renderTemplate({\n      template: html`\n        <Listbox v-model=\"value\">\n          <ListboxButton>Trigger</ListboxButton>\n          <ListboxOptions>\n            <ListboxOption value=\"a\">Option 1</ListboxOption>\n            <ListboxOption v-if=\"!hide\" value=\"b\">Option 2</ListboxOption>\n            <ListboxOption value=\"c\">Option 3</ListboxOption>\n          </ListboxOptions>\n        </Listbox>\n      `,\n      setup() {\n        return {\n          value: ref(null),\n          get hide() {\n            return props.hide\n          },\n        }\n      },\n    })\n\n    // Open the Listbox\n    await click(getByText('Trigger'))\n\n    props.hide = true\n    await nextFrame()\n\n    props.hide = false\n    await nextFrame()\n\n    assertListbox({ state: ListboxState.Visible })\n\n    let options = getListboxOptions()\n\n    // Focus the first option\n    await press(Keys.ArrowDown)\n\n    // Verify that the first listbox option is active\n    assertActiveListboxOption(options[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second listbox option is active\n    assertActiveListboxOption(options[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third listbox option is active\n    assertActiveListboxOption(options[2])\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Listbox name=\"assignee\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">Alice</ListboxOption>\n                <ListboxOption value=\"bob\">Bob</ListboxOption>\n                <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose charlie\n      await click(getListboxOptions()[2])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should expose the value via the render prop', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Listbox name=\"assignee\" v-slot=\"{ value }\">\n              <div data-testid=\"value\">{{value}}</div>\n              <ListboxButton v-slot=\"{ value }\">\n                Trigger\n                <div data-testid=\"value-2\">{{value}}</div>\n              </ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">Alice</ListboxOption>\n                <ListboxOption value=\"bob\">Bob</ListboxOption>\n                <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n      expect(document.querySelector('[data-testid=\"value\"]')).toHaveTextContent('alice')\n      expect(document.querySelector('[data-testid=\"value-2\"]')).toHaveTextContent('alice')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose charlie\n      await click(getListboxOptions()[2])\n      expect(document.querySelector('[data-testid=\"value\"]')).toHaveTextContent('charlie')\n      expect(document.querySelector('[data-testid=\"value-2\"]')).toHaveTextContent('charlie')\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Charlie should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Listbox name=\"assignee\" defaultValue=\"bob\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">Alice</ListboxOption>\n                <ListboxOption value=\"bob\">Bob</ListboxOption>\n                <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Alice should be submitted\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n    })\n\n    it(\n      'should be possible to reset to the default value if the form is reset',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <Listbox name=\"assignee\" defaultValue=\"bob\">\n                <ListboxButton v-slot=\"{ value }\">{{ value ?? 'Trigger' }}</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption value=\"alice\">Alice</ListboxOption>\n                  <ListboxOption value=\"bob\">Bob</ListboxOption>\n                  <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n                </ListboxOptions>\n              </Listbox>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Choose alice\n        await click(getListboxOptions()[0])\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // The listbox should be reset to bob\n        expect(getListboxButton()).toHaveTextContent('bob')\n\n        // Open listbox\n        await click(getListboxButton())\n        assertActiveListboxOption(getListboxOptions()[1])\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset (using objects)',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <Listbox name=\"assignee\" :defaultValue=\"{ id: 2, name: 'bob', label: 'Bob' }\" by=\"id\">\n                <ListboxButton v-slot=\"{ value }\">{{ value ?? 'Trigger' }}</ListboxButton>\n                <ListboxOptions>\n                  <ListboxOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                    {{ person.label }}\n                  </ListboxOption>\n                <ListboxOptions>\n              </Listbox>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            data,\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Choose alice\n        await click(getListboxOptions()[0])\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // The listbox should be reset to bob\n        expect(getListboxButton()).toHaveTextContent('bob')\n\n        // Open listbox\n        await click(getListboxButton())\n        assertActiveListboxOption(getListboxOptions()[1])\n      })\n    )\n\n    it('should be possible to reset to the default value in multiple mode', async () => {\n      let data = ['alice', 'bob', 'charlie']\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Listbox name=\"assignee\" :defaultValue=\"['bob']\" multiple>\n              <ListboxButton v-slot=\"{ value }\">{{ value.join(', ') || 'Trigger' }}</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption v-for=\"person in data\" :key=\"person\" :value=\"person\">\n                  {{ person }}\n                </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n            <button id=\"submit\">submit</button>\n            <button type=\"reset\" id=\"reset\">reset</button>\n          </form>\n        `,\n        setup: () => ({\n          data,\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // Bob is the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n\n      await click(document.getElementById('reset'))\n      await click(document.getElementById('submit'))\n\n      // Bob is still the defaultValue\n      expect(handleSubmission).toHaveBeenLastCalledWith({\n        'assignee[0]': 'bob',\n      })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <Listbox name=\"assignee\" @update:modelValue=\"handleChange\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">Alice</ListboxOption>\n              <ListboxOption value=\"bob\">Bob</ListboxOption>\n              <ListboxOption value=\"charlie\">Charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ handleChange }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose alice\n      await click(getListboxOptions()[0])\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Choose bob\n      await click(getListboxOptions()[1])\n\n      // Change handler should have been called twice\n      expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n      expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n    })\n  })\n\n  it(\n    'should be possible to use a custom component using the `as` prop without crashing',\n    suppressConsoleLogs(async () => {\n      let CustomComponent = defineComponent({\n        template: html`<button><slot /></button>`,\n      })\n\n      renderTemplate({\n        template: html`\n          <Listbox name=\"assignee\">\n            <ListboxButton />\n            <ListboxOptions>\n              <ListboxOption :as=\"CustomComponent\" value=\"alice\">Alice</RadioGroupOption>\n              <ListboxOption :as=\"CustomComponent\" value=\"bob\">Bob</RadioGroupOption>\n              <ListboxOption :as=\"CustomComponent\" value=\"charlie\">Charlie</RadioGroupOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ CustomComponent }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n    })\n  )\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to swap the Listbox option with a button for example',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption as=\"button\" value=\"a\"> Option A </ListboxOption>\n              <ListboxOption as=\"button\" value=\"b\"> Option B </ListboxOption>\n              <ListboxOption as=\"button\" value=\"c\"> Option C </ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open Listbox\n      await click(getListboxButton())\n\n      // Verify options are buttons now\n      getListboxOptions().forEach((option) => assertListboxOption(option, { tag: 'button' }))\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  let OpenClosedWrite = defineComponent({\n    props: { open: { type: Boolean } },\n    setup(props, { slots }) {\n      useOpenClosedProvider(ref(props.open ? State.Open : State.Closed))\n      return () => slots.default?.()\n    },\n  })\n\n  let OpenClosedRead = defineComponent({\n    emits: ['read'],\n    setup(_, { slots, emit }) {\n      let state = useOpenClosed()\n      watch([state], ([value]) => emit('read', value))\n      return () => slots.default?.()\n    },\n  })\n\n  it(\n    'should always open the ListboxOptions because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Listbox>\n            <ListboxButton>Trigger</ListboxButton>\n            <OpenClosedWrite :open=\"true\">\n              <ListboxOptions v-slot=\"data\"> {{JSON.stringify(data)}} </ListboxOptions>\n            </OpenClosedWrite>\n          </Listbox>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Listbox is visible\n      assertListbox({ state: ListboxState.Visible })\n\n      // Let's try and open the Listbox\n      await click(getListboxButton())\n\n      // Verify the Listbox is still visible\n      assertListbox({ state: ListboxState.Visible })\n    })\n  )\n\n  it(\n    'should always close the ListboxOptions because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Listbox>\n            <ListboxButton>Trigger</ListboxButton>\n            <OpenClosedWrite :open=\"false\">\n              <ListboxOptions v-slot=\"data\"> {{JSON.stringify(data)}} </ListboxOptions>\n            </OpenClosedWrite>\n          </Listbox>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Listbox is hidden\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Let's try and open the Listbox\n      await click(getListboxButton())\n\n      // Verify the Listbox is still hidden\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to read the OpenClosed state',\n    suppressConsoleLogs(async () => {\n      let readFn = jest.fn()\n      renderTemplate({\n        components: { OpenClosedRead },\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <OpenClosedRead @read=\"readFn\">\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n              </ListboxOptions>\n            </OpenClosedRead>\n          </Listbox>\n        `,\n        setup() {\n          return { value: ref(null), readFn }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Listbox is hidden\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Let's toggle the Listbox 3 times\n      await click(getListboxButton())\n      await click(getListboxButton())\n      await click(getListboxButton())\n\n      // Verify the Listbox is visible\n      assertListbox({ state: ListboxState.Visible })\n\n      expect(readFn).toHaveBeenCalledTimes(3)\n      expect(readFn).toHaveBeenNthCalledWith(1, State.Open)\n      expect(readFn).toHaveBeenNthCalledWith(2, State.Closed)\n      expect(readFn).toHaveBeenNthCalledWith(3, State.Open)\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the listbox with Enter',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option, { selected: false }))\n\n        // Verify that the first listbox option is active\n        assertActiveListboxOption(options[0])\n        assertNoSelectedListboxOption()\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" disabled>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Try to open the listbox\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with Enter, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref('b') }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with Enter, and focus the selected option (when using the `hidden` render strategy)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions :unmount=\"false\">\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref('b') }),\n        })\n\n        await new Promise<void>(nextTick)\n\n        assertListboxButton({\n          state: ListboxState.InvisibleHidden,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleHidden })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        let options = getListboxOptions()\n\n        // Hover over Option A\n        await mouseMove(options[0])\n\n        // Verify that Option A is active\n        assertActiveListboxOption(options[0])\n\n        // Verify that Option B is still selected\n        assertListboxOption(options[1], { selected: true })\n\n        // Close/Hide the listbox\n        await press(Keys.Escape)\n\n        // Re-open the listbox\n        await click(getListboxButton())\n\n        // Verify we have listbox options\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with Enter, and focus the selected option (with a list of objects)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption v-for=\"option in options\" key=\"option.id\" :value=\"option\"\n                  >{{ option.name }}</ListboxOption\n                >\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => {\n            let options = [\n              { id: 'a', name: 'Option A' },\n              { id: 'b', name: 'Option B' },\n              { id: 'c', name: 'Option C' },\n            ]\n            let value = ref(options[1])\n\n            return { value, options }\n          },\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions />\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Enter',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Enter (jump over multiple disabled ones)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon Enter key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to close the listbox with Enter when there is no active listboxoption',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Close listbox\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n      })\n    )\n\n    it(\n      'should be possible to close the listbox with Enter and choose the active listbox option',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup() {\n            let value = ref(null)\n            watch([value], () => handleChange(value.value))\n            return { value }\n          },\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Activate the first listbox option\n        let options = getListboxOptions()\n        await mouseMove(options[0])\n\n        // Choose option, and close listbox\n        await press(Keys.Enter)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify we got the change event\n        expect(handleChange).toHaveBeenCalledTimes(1)\n        expect(handleChange).toHaveBeenCalledWith('a')\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n\n        // Open listbox again\n        await click(getListboxButton())\n\n        // Verify the active option is the previously selected one\n        assertActiveListboxOption(getListboxOptions()[0])\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the listbox with Space',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" disabled>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Try to open the listbox\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with Space, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref('b') }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions />\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Space',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should focus the first non disabled listbox option when opening with Space (jump over multiple disabled ones)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        let options = getListboxOptions()\n\n        // Verify that the first non-disabled listbox option is active\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon Space key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to close the listbox with Space and choose the active listbox option',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup() {\n            let value = ref(null)\n            watch([value], () => handleChange(value.value))\n            return { value }\n          },\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n\n        // Activate the first listbox option\n        let options = getListboxOptions()\n        await mouseMove(options[0])\n\n        // Choose option, and close listbox\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify we got the change event\n        expect(handleChange).toHaveBeenCalledTimes(1)\n        expect(handleChange).toHaveBeenCalledWith('a')\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n\n        // Open listbox again\n        await click(getListboxButton())\n\n        // Verify the active option is the previously selected one\n        assertActiveListboxOption(getListboxOptions()[0])\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it(\n      'should be possible to close an open listbox with Escape',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Close listbox\n        await press(Keys.Escape)\n\n        // Verify it is closed\n        assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getListboxButton())\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should focus trap when we use Tab',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // Try to tab\n        await press(Keys.Tab)\n\n        // Verify it is still open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n      })\n    )\n\n    it(\n      'should focus trap when we use Shift+Tab',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // Try to Shift+Tab\n        await press(shift(Keys.Tab))\n\n        // Verify it is still open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n      })\n    )\n  })\n\n  describe('`ArrowDown` key', () => {\n    it(\n      'should be possible to open the listbox with ArrowDown',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n\n        // Verify that the first listbox option is active\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with ArrowDown when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" disabled>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Try to open the listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is still closed\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with ArrowDown, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref('b') }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions />\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowDown)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n\n        // We should NOT be able to go down again (because last option). Current implementation won't go around.\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options and skip the first disabled one',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down once\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowDown to navigate the listbox options and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n      })\n    )\n  })\n\n  describe('`ArrowRight` key', () => {\n    it(\n      'should be possible to use ArrowRight to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" horizontal>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go right once\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go right again\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[2])\n\n        // We should NOT be able to go right again (because last option). Current implementation won't go around.\n        await press(Keys.ArrowRight)\n        assertActiveListboxOption(options[2])\n      })\n    )\n  })\n\n  describe('`ArrowUp` key', () => {\n    it(\n      'should be possible to open the listbox with ArrowUp and the last option should be active',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n\n        // ! ALERT: The LAST option should now be active\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should not be possible to open the listbox with ArrowUp and the last option should be active when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" disabled>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Try to open the listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is still closed\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to open the listbox with ArrowUp, and focus the selected option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref('b') }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n        // Verify that the second listbox option is active (because it is already selected)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should have no active listbox option when there are no listbox options at all',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions />\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n        assertListbox({ state: ListboxState.Visible })\n        assertActiveElement(getListbox())\n\n        assertNoActiveListboxOption()\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the listbox options and jump to the first non-disabled one',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should not be possible to navigate up or down if there is only a single non-disabled option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go up (because those are disabled)\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go down (because this is the last option)\n        await press(Keys.ArrowDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use ArrowUp to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is open\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go down once\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go down again\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[0])\n\n        // We should NOT be able to go up again (because first option). Current implementation won't go around.\n        await press(Keys.ArrowUp)\n        assertActiveListboxOption(options[0])\n      })\n    )\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it(\n      'should be possible to use ArrowLeft to navigate the listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\" horizontal>\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        assertListboxButton({\n          state: ListboxState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-listbox-button-1' },\n        })\n        assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        // Verify it is visible\n        assertListboxButton({ state: ListboxState.Visible })\n        assertListbox({\n          state: ListboxState.Visible,\n          attributes: { id: 'headlessui-listbox-options-2' },\n          orientation: 'horizontal',\n        })\n        assertActiveElement(getListbox())\n        assertListboxButtonLinkedWithListbox()\n\n        // Verify we have listbox options\n        let options = getListboxOptions()\n        expect(options).toHaveLength(3)\n        options.forEach((option) => assertListboxOption(option))\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go left once\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go left again\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[0])\n\n        // We should NOT be able to go left again (because first option). Current implementation won't go around.\n        await press(Keys.ArrowLeft)\n        assertActiveListboxOption(options[0])\n      })\n    )\n  })\n\n  describe('`End` key', () => {\n    it(\n      'should be possible to use the End key to go to the last listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await press(Keys.End)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the last non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last non-disabled option\n        await press(Keys.End)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to use the End key to go to the first listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon End key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.End)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`PageDown` key', () => {\n    it(\n      'should be possible to use the PageDown key to go to the last listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await press(Keys.PageDown)\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the last non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.Enter)\n\n        let options = getListboxOptions()\n\n        // We should be on the first option\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last non-disabled option\n        await press(Keys.PageDown)\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to use the PageDown key to go to the first listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon PageDown key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageDown)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`Home` key', () => {\n    it(\n      'should be possible to use the Home key to go to the first listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the first option\n        await press(Keys.Home)\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the first non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n                <ListboxOption value=\"d\">Option D</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let options = getListboxOptions()\n\n        // We should be on the first non-disabled option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the Home key to go to the last listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption value=\"d\">Option D</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon Home key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.Home)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`PageUp` key', () => {\n    it(\n      'should be possible to use the PageUp key to go to the first listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">Option A</ListboxOption>\n                <ListboxOption value=\"b\">Option B</ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the first option\n        await press(Keys.PageUp)\n        assertActiveListboxOption(options[0])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the first non disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption value=\"c\">Option C</ListboxOption>\n                <ListboxOption value=\"d\">Option D</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the first non-disabled option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to use the PageUp key to go to the last listbox option if that is the only non-disabled listbox option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption value=\"d\">Option D</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        let options = getListboxOptions()\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should have no active listbox option upon PageUp key press, when there are no non-disabled listbox options',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption disabled value=\"a\"> Option A </ListboxOption>\n                <ListboxOption disabled value=\"b\"> Option B </ListboxOption>\n                <ListboxOption disabled value=\"c\"> Option C </ListboxOption>\n                <ListboxOption disabled value=\"d\"> Option D </ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        // We opened via click, we don't have an active option\n        assertNoActiveListboxOption()\n\n        // We should not be able to go to the end\n        await press(Keys.PageUp)\n\n        assertNoActiveListboxOption()\n      })\n    )\n  })\n\n  describe('`Any` key aka search', () => {\n    it(\n      'should be possible to type a full word that has a perfect match',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption value=\"bob\">bob</ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // We should be able to go to the second option\n        await type(word('bob'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('alice'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('charlie'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to type a partial of a word',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption value=\"bob\">bob</ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the second option\n        await type(word('bo'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('ali'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('char'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to type words with spaces',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">value a</ListboxOption>\n                <ListboxOption value=\"b\">value b</ListboxOption>\n                <ListboxOption value=\"c\">value c</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should be able to go to the second option\n        await type(word('value b'))\n        assertActiveListboxOption(options[1])\n\n        // We should be able to go to the first option\n        await type(word('value a'))\n        assertActiveListboxOption(options[0])\n\n        // We should be able to go to the last option\n        await type(word('value c'))\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should not be possible to search for a disabled option',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // We should not be able to go to the disabled option\n        await type(word('bo'))\n\n        // We should still be on the last option\n        assertActiveListboxOption(options[2])\n      })\n    )\n\n    it(\n      'should be possible to search for a word (case insensitive)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption value=\"bob\">bob</ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Focus the button\n        getListboxButton()?.focus()\n\n        // Open listbox\n        await press(Keys.ArrowUp)\n\n        let options = getListboxOptions()\n\n        // We should be on the last option\n        assertActiveListboxOption(options[2])\n\n        // Search for bob in a different casing\n        await type(word('BO'))\n\n        // We should be on `bob`\n        assertActiveListboxOption(options[1])\n      })\n    )\n\n    it(\n      'should be possible to search for the next occurence',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">alice</ListboxOption>\n                <ListboxOption value=\"b\">bob</ListboxOption>\n                <ListboxOption value=\"c\">charlie</ListboxOption>\n                <ListboxOption value=\"b\">bob</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // Search for bob\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for bob again\n        await type(word('b'))\n\n        // We should be on the second `bob`\n        assertActiveListboxOption(options[3])\n      })\n    )\n\n    it(\n      'should stay on the same item while keystrokes still match',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"a\">alice</ListboxOption>\n                <ListboxOption value=\"b\">bob</ListboxOption>\n                <ListboxOption value=\"c\">charlie</ListboxOption>\n                <ListboxOption value=\"b\">bob</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          `,\n          setup: () => ({ value: ref(null) }),\n        })\n\n        // Open listbox\n        await click(getListboxButton())\n\n        let options = getListboxOptions()\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"b\" in \"bob\"\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"b\" in \"bob\" again\n        await type(word('b'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"bo\" in \"bob\"\n        await type(word('bo'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"bo\" in \"bob\" again\n        await type(word('bo'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n\n        // ---\n\n        // Reset: Go to first option\n        await press(Keys.Home)\n\n        // Search for \"bob\" in \"bob\"\n        await type(word('bob'))\n\n        // We should be on the first `bob`\n        assertActiveListboxOption(options[1])\n\n        // Search for \"bob\" in \"bob\" again\n        await type(word('bob'))\n\n        // We should be on the next `bob`\n        assertActiveListboxOption(options[3])\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should focus the ListboxButton when we click the ListboxLabel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxLabel>Label</ListboxLabel>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getListboxLabel())\n\n      // Ensure that the actual button is focused instead\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should not focus the ListboxButton when we right click the ListboxLabel',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxLabel>Label</ListboxLabel>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Ensure the button is not focused yet\n      assertActiveElement(document.body)\n\n      // Focus the label\n      await click(getListboxLabel(), MouseButton.Right)\n\n      // Ensure that the body is still active\n      assertActiveElement(document.body)\n    })\n  )\n\n  it(\n    'should be possible to open the listbox on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is open\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({\n        state: ListboxState.Visible,\n        attributes: { id: 'headlessui-listbox-options-2' },\n      })\n      assertActiveElement(getListbox())\n      assertListboxButtonLinkedWithListbox()\n\n      // Verify we have listbox options\n      let options = getListboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option) => assertListboxOption(option))\n    })\n  )\n\n  it(\n    'should not be possible to open the listbox on right click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Try to open the menu\n      await click(getListboxButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open the listbox on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" disabled>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Try to open the listbox\n      await click(getListboxButton())\n\n      // Verify it is still closed\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to open the listbox on click, and focus the selected option',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref('b') }),\n      })\n\n      assertListboxButton({\n        state: ListboxState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-listbox-button-1' },\n      })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is open\n      assertListboxButton({ state: ListboxState.Visible })\n      assertListbox({\n        state: ListboxState.Visible,\n        attributes: { id: 'headlessui-listbox-options-2' },\n      })\n      assertActiveElement(getListbox())\n      assertListboxButtonLinkedWithListbox()\n\n      // Verify we have listbox options\n      let options = getListboxOptions()\n      expect(options).toHaveLength(3)\n      options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))\n\n      // Verify that the second listbox option is active (because it is already selected)\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be possible to close a listbox on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"a\">Option A</ListboxOption>\n              <ListboxOption value=\"b\">Option B</ListboxOption>\n              <ListboxOption value=\"c\">Option C</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify it is open\n      assertListboxButton({ state: ListboxState.Visible })\n\n      // Click to close\n      await click(getListboxButton())\n\n      // Verify it is closed\n      assertListboxButton({ state: ListboxState.InvisibleUnmounted })\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be a no-op when we click outside of a closed listbox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Verify that the window is closed\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Click something that is not related to the listbox\n      await click(document.body)\n\n      // Should still be closed\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox which should close the listbox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      // Click something that is not related to the listbox\n      await click(document.body)\n\n      // Should be closed now\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox on another listbox button which should close the current listbox and open the new listbox',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <div>\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption value=\"bob\">bob</ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n\n            <Listbox v-model=\"value\">\n              <ListboxButton>Trigger</ListboxButton>\n              <ListboxOptions>\n                <ListboxOption value=\"alice\">alice</ListboxOption>\n                <ListboxOption value=\"bob\">bob</ListboxOption>\n                <ListboxOption value=\"charlie\">charlie</ListboxOption>\n              </ListboxOptions>\n            </Listbox>\n          </div>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      let [button1, button2] = getListboxButtons()\n\n      // Click the first menu button\n      await click(button1)\n      expect(getListboxes()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the first button\n      assertListboxButtonLinkedWithListbox(button1, getListbox())\n\n      // Click the second menu button\n      await click(button2)\n\n      expect(getListboxes()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the second button\n      assertListboxButtonLinkedWithListbox(button2, getListbox())\n    })\n  )\n\n  it(\n    'should be possible to click outside of the listbox which should close the listbox (even if we press the listbox button)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      // Click the listbox button again\n      await click(getListboxButton())\n\n      // Should be closed now\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n    })\n  )\n\n  it(\n    'should be possible to hover an option and make it active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveListboxOption(options[0])\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveListboxOption(options[2])\n    })\n  )\n\n  it(\n    'should make a listbox option active when you move the mouse over it',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the listbox option is already active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      await mouseMove(options[1])\n\n      // Nothing should be changed\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should be a no-op when we move the mouse and the listbox option is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      await mouseMove(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should not be possible to hover an option that is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n\n      // We should not have an active option now\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave an option and make it inactive',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref('bob') }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // We should be able to go to the second option\n      await mouseMove(options[1])\n      assertActiveListboxOption(options[1])\n\n      await mouseLeave(options[1])\n      assertNoActiveListboxOption()\n\n      // We should be able to go to the first option\n      await mouseMove(options[0])\n      assertActiveListboxOption(options[0])\n\n      await mouseLeave(options[0])\n      assertNoActiveListboxOption()\n\n      // We should be able to go to the last option\n      await mouseMove(options[2])\n      assertActiveListboxOption(options[2])\n\n      await mouseLeave(options[2])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to mouse leave a disabled option and be a no-op',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      let options = getListboxOptions()\n\n      // Try to hover over option 1, which is disabled\n      await mouseMove(options[1])\n      assertNoActiveListboxOption()\n\n      await mouseLeave(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible to click a listbox option, which closes the listbox',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup() {\n          let value = ref(null)\n          watch([value], () => handleChange(value.value))\n          return { value }\n        },\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertListbox({ state: ListboxState.InvisibleUnmounted })\n      expect(handleChange).toHaveBeenCalledTimes(1)\n      expect(handleChange).toHaveBeenCalledWith('bob')\n\n      // Verify the button is focused again\n      assertActiveElement(getListboxButton())\n\n      // Open listbox again\n      await click(getListboxButton())\n\n      // Verify the active option is the previously selected one\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n  )\n\n  it(\n    'should be possible to click a disabled listbox option, which is a no-op',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup() {\n          let value = ref(null)\n          watch([value], () => handleChange(value.value))\n          return { value }\n        },\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should be able to click the first option\n      await click(options[1])\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n      expect(handleChange).toHaveBeenCalledTimes(0)\n\n      // Close the listbox\n      await click(getListboxButton())\n\n      // Open listbox again\n      await click(getListboxButton())\n\n      // Verify the active option is non existing\n      assertNoActiveListboxOption()\n    })\n  )\n\n  it(\n    'should be possible focus a listbox option, so that it becomes active',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // Verify that nothing is active yet\n      assertNoActiveListboxOption()\n\n      // We should be able to focus the first option\n      await focus(options[1])\n      assertActiveListboxOption(options[1])\n    })\n  )\n\n  it(\n    'should not be possible to focus a listbox option which is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption disabled value=\"bob\"> bob </ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(null) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n      assertActiveElement(getListbox())\n\n      let options = getListboxOptions()\n\n      // We should not be able to focus the first option\n      await focus(options[1])\n      assertNoActiveListboxOption()\n    })\n  )\n})\n\ndescribe('Multi-select', () => {\n  it(\n    'should be possible to pass multiple values to the Listbox component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" multiple>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify that we have an open listbox with multiple mode\n      assertListbox({ state: ListboxState.Visible, mode: ListboxMode.Multiple })\n\n      // Verify that we have multiple selected listbox options\n      let options = getListboxOptions()\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should make the first selected option the active item',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" multiple>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n\n      // Verify that bob is the active option\n      assertActiveListboxOption(getListboxOptions()[1])\n    })\n  )\n\n  it(\n    'should keep the listbox open when selecting an item via the keyboard',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" multiple>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n\n      // Verify that bob is the active option\n      await click(getListboxOptions()[0])\n\n      // Verify that the listbox is still open\n      assertListbox({ state: ListboxState.Visible })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" multiple>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"alice\">alice</ListboxOption>\n              <ListboxOption value=\"bob\">bob</ListboxOption>\n              <ListboxOption value=\"charlie\">charlie</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => ({ value: ref(['bob', 'charlie']) }),\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n\n      let options = getListboxOptions()\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: false })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n    })\n  )\n\n  it(\n    'should toggle the selected state of an option when clicking on it (using objects instead of primitives)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <Listbox v-model=\"value\" multiple>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption v-for=\"person in people\" :value=\"person\"\n                >{{ person.name }}</ListboxOption\n              >\n            </ListboxOptions>\n          </Listbox>\n        `,\n        setup: () => {\n          let people = [\n            { id: 1, name: 'alice' },\n            { id: 2, name: 'bob' },\n            { id: 3, name: 'charlie' },\n          ]\n\n          let value = ref([people[1], people[2]])\n          return { people, value }\n        },\n      })\n\n      // Open listbox\n      await click(getListboxButton())\n      assertListbox({ state: ListboxState.Visible })\n\n      let options = getListboxOptions()\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: false })\n      assertListboxOption(options[2], { selected: true })\n\n      // Click on bob again\n      await click(getListboxOptions()[1])\n\n      assertListboxOption(options[0], { selected: false })\n      assertListboxOption(options[1], { selected: true })\n      assertListboxOption(options[2], { selected: true })\n    })\n  )\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <div>\n          <Listbox form=\"my-form\" v-model=\"value\" name=\"delivery\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"pickup\">Pickup</ListboxOption>\n              <ListboxOption value=\"home-delivery\">Home delivery</ListboxOption>\n              <ListboxOption value=\"dine-in\">Dine in</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n          <form id=\"my-form\" @submit=\"handleSubmit\">\n            <button>Submit</button>\n          </form>\n        </div>\n      `,\n      setup: () => {\n        let value = ref(null)\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should be possible to submit a form with a value', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <Listbox v-model=\"value\" name=\"delivery\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"pickup\">Pickup</ListboxOption>\n              <ListboxOption value=\"home-delivery\">Home delivery</ListboxOption>\n              <ListboxOption value=\"dine-in\">Dine in</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let value = ref(null)\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Open listbox again\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should not submit the data if the Listbox is disabled', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <Listbox v-model=\"value\" name=\"delivery\" disabled>\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption value=\"pickup\">Pickup</ListboxOption>\n              <ListboxOption value=\"home-delivery\">Home delivery</ListboxOption>\n              <ListboxOption value=\"dine-in\">Dine in</ListboxOption>\n            </ListboxOptions>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let value = ref('home-delivery')\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it('should be possible to submit a form with a complex value object', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <Listbox v-model=\"value\" name=\"delivery\">\n            <ListboxButton>Trigger</ListboxButton>\n            <ListboxOptions>\n              <ListboxOption v-for=\"option in options\" :key=\"option.id\" :value=\"option\"\n                >{{option.label}}</ListboxOption\n              >\n            </ListboxOptions>\n          </Listbox>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let options = ref([\n          {\n            id: 1,\n            value: 'pickup',\n            label: 'Pickup',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 2,\n            value: 'home-delivery',\n            label: 'Home delivery',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 3,\n            value: 'dine-in',\n            label: 'Dine in',\n            extra: { info: 'Some extra info' },\n          },\n        ])\n        let value = ref(options.value[0])\n\n        return {\n          value,\n          options,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '2'],\n      ['delivery[value]', 'home-delivery'],\n      ['delivery[label]', 'Home delivery'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Open listbox\n    await click(getListboxButton())\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/listbox/listbox.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  nextTick,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  toRaw,\n  watch,\n  watchEffect,\n  type ComputedRef,\n  type InjectionKey,\n  type PropType,\n  type Ref,\n  type UnwrapNestedRefs,\n} from 'vue'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useId } from '../../hooks/use-id'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useTextValue } from '../../hooks/use-text-value'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport { Keys } from '../../keyboard'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport { dom } from '../../utils/dom'\nimport { FocusableMode, isFocusableElement, sortByDomNode } from '../../utils/focus-management'\nimport { objectToFormEntries } from '../../utils/form'\nimport { match } from '../../utils/match'\nimport { Features, compact, omit, render } from '../../utils/render'\n\nfunction defaultComparator<T>(a: T, z: T): boolean {\n  return a === z\n}\n\nenum ListboxStates {\n  Open,\n  Closed,\n}\n\nenum ValueMode {\n  Single,\n  Multi,\n}\n\nenum ActivationTrigger {\n  Pointer,\n  Other,\n}\n\nfunction nextFrame(cb: () => void) {\n  requestAnimationFrame(() => requestAnimationFrame(cb))\n}\n\ntype ListboxOptionData = {\n  textValue: string\n  disabled: boolean\n  value: unknown\n  domRef: Ref<HTMLElement | null>\n}\n\ntype StateDefinition = {\n  // State\n  listboxState: Ref<ListboxStates>\n  value: ComputedRef<unknown>\n  orientation: Ref<'vertical' | 'horizontal'>\n\n  mode: ComputedRef<ValueMode>\n\n  compare: (a: unknown, z: unknown) => boolean\n\n  labelRef: Ref<HTMLLabelElement | null>\n  buttonRef: Ref<HTMLButtonElement | null>\n  optionsRef: Ref<HTMLDivElement | null>\n\n  disabled: Ref<boolean>\n  options: Ref<{ id: string; dataRef: ComputedRef<ListboxOptionData> }[]>\n  searchQuery: Ref<string>\n  activeOptionIndex: Ref<number | null>\n  activationTrigger: Ref<ActivationTrigger>\n\n  // State mutators\n  closeListbox(): void\n  openListbox(): void\n  goToOption(focus: Focus, id?: string, trigger?: ActivationTrigger): void\n  search(value: string): void\n  clearSearch(): void\n  registerOption(id: string, dataRef: ComputedRef<ListboxOptionData>): void\n  unregisterOption(id: string): void\n  select(value: unknown): void\n}\n\nlet ListboxContext = Symbol('ListboxContext') as InjectionKey<StateDefinition>\n\nfunction useListboxContext(component: string) {\n  let context = inject(ListboxContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Listbox /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext)\n    throw err\n  }\n\n  return context\n}\n\n// ---\n\nexport let Listbox = defineComponent({\n  name: 'Listbox',\n  emits: { 'update:modelValue': (_value: any) => true },\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    disabled: { type: [Boolean], default: false },\n    by: { type: [String, Function], default: () => defaultComparator },\n    horizontal: { type: [Boolean], default: false },\n    modelValue: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n      default: undefined,\n    },\n    defaultValue: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n      default: undefined,\n    },\n    form: { type: String, optional: true },\n    name: { type: String, optional: true },\n    multiple: { type: [Boolean], default: false },\n  },\n  inheritAttrs: false,\n  setup(props, { slots, attrs, emit }) {\n    let listboxState = ref<StateDefinition['listboxState']['value']>(ListboxStates.Closed)\n    let labelRef = ref<StateDefinition['labelRef']['value']>(null)\n    let buttonRef = ref<StateDefinition['buttonRef']['value']>(null)\n    let optionsRef = ref<StateDefinition['optionsRef']['value']>(null)\n    let options = ref<StateDefinition['options']['value']>([])\n    let searchQuery = ref<StateDefinition['searchQuery']['value']>('')\n    let activeOptionIndex = ref<StateDefinition['activeOptionIndex']['value']>(null)\n    let activationTrigger = ref<StateDefinition['activationTrigger']['value']>(\n      ActivationTrigger.Other\n    )\n\n    function adjustOrderedState(\n      adjustment: (\n        options: UnwrapNestedRefs<StateDefinition['options']['value']>\n      ) => UnwrapNestedRefs<StateDefinition['options']['value']> = (i) => i\n    ) {\n      let currentActiveOption =\n        activeOptionIndex.value !== null ? options.value[activeOptionIndex.value] : null\n\n      let sortedOptions = sortByDomNode(adjustment(options.value.slice()), (option) =>\n        dom(option.dataRef.domRef)\n      )\n\n      // If we inserted an option before the current active option then the active option index\n      // would be wrong. To fix this, we will re-lookup the correct index.\n      let adjustedActiveOptionIndex = currentActiveOption\n        ? sortedOptions.indexOf(currentActiveOption)\n        : null\n\n      // Reset to `null` in case the currentActiveOption was removed.\n      if (adjustedActiveOptionIndex === -1) {\n        adjustedActiveOptionIndex = null\n      }\n\n      return {\n        options: sortedOptions,\n        activeOptionIndex: adjustedActiveOptionIndex,\n      }\n    }\n\n    let mode = computed(() => (props.multiple ? ValueMode.Multi : ValueMode.Single))\n\n    let [directValue, theirOnChange] = useControllable(\n      computed(() => props.modelValue),\n      (value: unknown) => emit('update:modelValue', value),\n      computed(() => props.defaultValue)\n    )\n\n    let value = computed(() =>\n      directValue.value === undefined\n        ? match(mode.value, {\n            [ValueMode.Multi]: [],\n            [ValueMode.Single]: undefined,\n          })\n        : directValue.value\n    )\n\n    let api = {\n      listboxState,\n      value,\n      mode,\n      compare(a: any, z: any) {\n        if (typeof props.by === 'string') {\n          let property = props.by as unknown as any\n          return a?.[property] === z?.[property]\n        }\n        return props.by(a, z)\n      },\n      orientation: computed(() => (props.horizontal ? 'horizontal' : 'vertical')),\n      labelRef,\n      buttonRef,\n      optionsRef,\n      disabled: computed(() => props.disabled),\n      options,\n      searchQuery,\n      activeOptionIndex,\n      activationTrigger,\n      closeListbox() {\n        if (props.disabled) return\n        if (listboxState.value === ListboxStates.Closed) return\n        listboxState.value = ListboxStates.Closed\n        activeOptionIndex.value = null\n      },\n      openListbox() {\n        if (props.disabled) return\n        if (listboxState.value === ListboxStates.Open) return\n        listboxState.value = ListboxStates.Open\n      },\n      goToOption(focus: Focus, id?: string, trigger?: ActivationTrigger) {\n        if (props.disabled) return\n        if (listboxState.value === ListboxStates.Closed) return\n\n        let adjustedState = adjustOrderedState()\n        let nextActiveOptionIndex = calculateActiveIndex(\n          focus === Focus.Specific\n            ? { focus: Focus.Specific, id: id! }\n            : { focus: focus as Exclude<Focus, Focus.Specific> },\n          {\n            resolveItems: () => adjustedState.options,\n            resolveActiveIndex: () => adjustedState.activeOptionIndex,\n            resolveId: (option) => option.id,\n            resolveDisabled: (option) => option.dataRef.disabled,\n          }\n        )\n\n        searchQuery.value = ''\n        activeOptionIndex.value = nextActiveOptionIndex\n        activationTrigger.value = trigger ?? ActivationTrigger.Other\n        options.value = adjustedState.options\n      },\n      search(value: string) {\n        if (props.disabled) return\n        if (listboxState.value === ListboxStates.Closed) return\n\n        let wasAlreadySearching = searchQuery.value !== ''\n        let offset = wasAlreadySearching ? 0 : 1\n\n        searchQuery.value += value.toLowerCase()\n\n        let reOrderedOptions =\n          activeOptionIndex.value !== null\n            ? options.value\n                .slice(activeOptionIndex.value + offset)\n                .concat(options.value.slice(0, activeOptionIndex.value + offset))\n            : options.value\n\n        let matchingOption = reOrderedOptions.find(\n          (option) =>\n            option.dataRef.textValue.startsWith(searchQuery.value) && !option.dataRef.disabled\n        )\n\n        let matchIdx = matchingOption ? options.value.indexOf(matchingOption) : -1\n        if (matchIdx === -1 || matchIdx === activeOptionIndex.value) return\n\n        activeOptionIndex.value = matchIdx\n        activationTrigger.value = ActivationTrigger.Other\n      },\n      clearSearch() {\n        if (props.disabled) return\n        if (listboxState.value === ListboxStates.Closed) return\n        if (searchQuery.value === '') return\n\n        searchQuery.value = ''\n      },\n      registerOption(id: string, dataRef: ListboxOptionData) {\n        let adjustedState = adjustOrderedState((options) => {\n          return [...options, { id, dataRef }]\n        })\n\n        options.value = adjustedState.options\n        activeOptionIndex.value = adjustedState.activeOptionIndex\n      },\n      unregisterOption(id: string) {\n        let adjustedState = adjustOrderedState((options) => {\n          let idx = options.findIndex((a) => a.id === id)\n          if (idx !== -1) options.splice(idx, 1)\n          return options\n        })\n\n        options.value = adjustedState.options\n        activeOptionIndex.value = adjustedState.activeOptionIndex\n        activationTrigger.value = ActivationTrigger.Other\n      },\n      theirOnChange(value: unknown) {\n        if (props.disabled) return\n        theirOnChange(value)\n      },\n      select(value: unknown) {\n        if (props.disabled) return\n        theirOnChange(\n          match(mode.value, {\n            [ValueMode.Single]: () => value,\n            [ValueMode.Multi]: () => {\n              let copy = toRaw(api.value.value as unknown[]).slice()\n              let raw = toRaw(value)\n\n              let idx = copy.findIndex((value) => api.compare(raw, toRaw(value)))\n              if (idx === -1) {\n                copy.push(raw)\n              } else {\n                copy.splice(idx, 1)\n              }\n\n              return copy\n            },\n          })\n        )\n      },\n    }\n\n    // Handle outside click\n    useOutsideClick(\n      [buttonRef, optionsRef],\n      (event, target) => {\n        api.closeListbox()\n\n        if (!isFocusableElement(target, FocusableMode.Loose)) {\n          event.preventDefault()\n          dom(buttonRef)?.focus()\n        }\n      },\n      computed(() => listboxState.value === ListboxStates.Open)\n    )\n\n    // @ts-expect-error Types of property 'dataRef' are incompatible.\n    provide(ListboxContext, api)\n    useOpenClosedProvider(\n      computed(() =>\n        match(listboxState.value, {\n          [ListboxStates.Open]: State.Open,\n          [ListboxStates.Closed]: State.Closed,\n        })\n      )\n    )\n\n    let form = computed(() => dom(buttonRef)?.closest('form'))\n    onMounted(() => {\n      watch(\n        [form],\n        () => {\n          if (!form.value) return\n          if (props.defaultValue === undefined) return\n\n          function handle() {\n            api.theirOnChange(props.defaultValue)\n          }\n\n          form.value.addEventListener('reset', handle)\n\n          return () => {\n            form.value?.removeEventListener('reset', handle)\n          }\n        },\n        { immediate: true }\n      )\n    })\n\n    return () => {\n      let { name, modelValue, disabled, form, ...theirProps } = props\n\n      let slot = { open: listboxState.value === ListboxStates.Open, disabled, value: value.value }\n\n      return h(Fragment, [\n        ...(name != null && value.value != null\n          ? objectToFormEntries({ [name]: value.value }).map(([name, value]) =>\n              h(\n                Hidden,\n                compact({\n                  features: HiddenFeatures.Hidden,\n                  key: name,\n                  as: 'input',\n                  type: 'hidden',\n                  hidden: true,\n                  readOnly: true,\n                  form,\n                  disabled,\n                  name,\n                  value,\n                })\n              )\n            )\n          : []),\n        render({\n          ourProps: {},\n          theirProps: {\n            ...attrs,\n            ...omit(theirProps, [\n              'defaultValue',\n              'onUpdate:modelValue',\n              'horizontal',\n              'multiple',\n              'by',\n            ]),\n          },\n          slot,\n          slots,\n          attrs,\n          name: 'Listbox',\n        }),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let ListboxLabel = defineComponent({\n  name: 'ListboxLabel',\n  props: {\n    as: { type: [Object, String], default: 'label' },\n    id: { type: String, default: () => `headlessui-listbox-label-${useId()}` },\n  },\n  setup(props, { attrs, slots }) {\n    let api = useListboxContext('ListboxLabel')\n\n    function handleClick() {\n      dom(api.buttonRef)?.focus({ preventScroll: true })\n    }\n\n    return () => {\n      let slot = {\n        open: api.listboxState.value === ListboxStates.Open,\n        disabled: api.disabled.value,\n      }\n      let { id, ...theirProps } = props\n      let ourProps = { id, ref: api.labelRef, onClick: handleClick }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ListboxLabel',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let ListboxButton = defineComponent({\n  name: 'ListboxButton',\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    id: { type: String, default: () => `headlessui-listbox-button-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useListboxContext('ListboxButton')\n\n    expose({ el: api.buttonRef, $el: api.buttonRef })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/#keyboard-interaction-13\n\n        case Keys.Space:\n        case Keys.Enter:\n        case Keys.ArrowDown:\n          event.preventDefault()\n          api.openListbox()\n          nextTick(() => {\n            dom(api.optionsRef)?.focus({ preventScroll: true })\n            if (!api.value.value) api.goToOption(Focus.First)\n          })\n          break\n\n        case Keys.ArrowUp:\n          event.preventDefault()\n          api.openListbox()\n          nextTick(() => {\n            dom(api.optionsRef)?.focus({ preventScroll: true })\n            if (!api.value.value) api.goToOption(Focus.Last)\n          })\n          break\n      }\n    }\n\n    function handleKeyUp(event: KeyboardEvent) {\n      switch (event.key) {\n        case Keys.Space:\n          // Required for firefox, event.preventDefault() in handleKeyDown for\n          // the Space key doesn't cancel the handleKeyUp, which in turn\n          // triggers a *click*.\n          event.preventDefault()\n          break\n      }\n    }\n\n    function handleClick(event: MouseEvent) {\n      if (api.disabled.value) return\n      if (api.listboxState.value === ListboxStates.Open) {\n        api.closeListbox()\n        nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n      } else {\n        event.preventDefault()\n        api.openListbox()\n        nextFrame(() => dom(api.optionsRef)?.focus({ preventScroll: true }))\n      }\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      api.buttonRef\n    )\n\n    return () => {\n      let slot = {\n        open: api.listboxState.value === ListboxStates.Open,\n        disabled: api.disabled.value,\n        value: api.value.value,\n      }\n\n      let { id, ...theirProps } = props\n      let ourProps = {\n        ref: api.buttonRef,\n        id,\n        type: type.value,\n        'aria-haspopup': 'listbox',\n        'aria-controls': dom(api.optionsRef)?.id,\n        'aria-expanded': api.listboxState.value === ListboxStates.Open,\n        'aria-labelledby': api.labelRef.value ? [dom(api.labelRef)?.id, id].join(' ') : undefined,\n        disabled: api.disabled.value === true ? true : undefined,\n        onKeydown: handleKeyDown,\n        onKeyup: handleKeyUp,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ListboxButton',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let ListboxOptions = defineComponent({\n  name: 'ListboxOptions',\n  props: {\n    as: { type: [Object, String], default: 'ul' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    id: { type: String, default: () => `headlessui-listbox-options-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useListboxContext('ListboxOptions')\n    let searchDebounce = ref<ReturnType<typeof setTimeout> | null>(null)\n\n    expose({ el: api.optionsRef, $el: api.optionsRef })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      if (searchDebounce.value) clearTimeout(searchDebounce.value)\n\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n        // @ts-expect-error Fallthrough is expected here\n        case Keys.Space:\n          if (api.searchQuery.value !== '') {\n            event.preventDefault()\n            event.stopPropagation()\n            return api.search(event.key)\n          }\n        // When in type ahead mode, fallthrough\n        case Keys.Enter:\n          event.preventDefault()\n          event.stopPropagation()\n          if (api.activeOptionIndex.value !== null) {\n            let activeOption = api.options.value[api.activeOptionIndex.value]\n            api.select(activeOption.dataRef.value)\n          }\n          if (api.mode.value === ValueMode.Single) {\n            api.closeListbox()\n            nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n          }\n          break\n\n        case match(api.orientation.value, {\n          vertical: Keys.ArrowDown,\n          horizontal: Keys.ArrowRight,\n        }):\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.Next)\n\n        case match(api.orientation.value, { vertical: Keys.ArrowUp, horizontal: Keys.ArrowLeft }):\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.Previous)\n\n        case Keys.Home:\n        case Keys.PageUp:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.First)\n\n        case Keys.End:\n        case Keys.PageDown:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToOption(Focus.Last)\n\n        case Keys.Escape:\n          event.preventDefault()\n          event.stopPropagation()\n          api.closeListbox()\n          nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n          break\n\n        case Keys.Tab:\n          event.preventDefault()\n          event.stopPropagation()\n          break\n\n        default:\n          if (event.key.length === 1) {\n            api.search(event.key)\n            searchDebounce.value = setTimeout(() => api.clearSearch(), 350)\n          }\n          break\n      }\n    }\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.listboxState.value === ListboxStates.Open\n    })\n\n    return () => {\n      let slot = { open: api.listboxState.value === ListboxStates.Open }\n      let { id, ...theirProps } = props\n      let ourProps = {\n        'aria-activedescendant':\n          api.activeOptionIndex.value === null\n            ? undefined\n            : api.options.value[api.activeOptionIndex.value]?.id,\n        'aria-multiselectable': api.mode.value === ValueMode.Multi ? true : undefined,\n        'aria-labelledby': dom(api.buttonRef)?.id,\n        'aria-orientation': api.orientation.value,\n        id,\n        onKeydown: handleKeyDown,\n        role: 'listbox',\n        tabIndex: 0,\n        ref: api.optionsRef,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'ListboxOptions',\n      })\n    }\n  },\n})\n\nexport let ListboxOption = defineComponent({\n  name: 'ListboxOption',\n  props: {\n    as: { type: [Object, String], default: 'li' },\n    value: {\n      type: [Object, String, Number, Boolean] as PropType<\n        object | string | number | boolean | null\n      >,\n    },\n    disabled: { type: Boolean, default: false },\n    id: { type: String, default: () => `headlessui-listbox.option-${useId()}` },\n  },\n  setup(props, { slots, attrs, expose }) {\n    let api = useListboxContext('ListboxOption')\n    let internalOptionRef = ref<HTMLElement | null>(null)\n\n    expose({ el: internalOptionRef, $el: internalOptionRef })\n\n    let active = computed(() => {\n      return api.activeOptionIndex.value !== null\n        ? api.options.value[api.activeOptionIndex.value].id === props.id\n        : false\n    })\n\n    let selected = computed(() =>\n      match(api.mode.value, {\n        [ValueMode.Single]: () => api.compare(toRaw(api.value.value), toRaw(props.value)),\n        [ValueMode.Multi]: () =>\n          (toRaw(api.value.value) as unknown[]).some((value) =>\n            api.compare(toRaw(value), toRaw(props.value))\n          ),\n      })\n    )\n    let isFirstSelected = computed(() => {\n      return match(api.mode.value, {\n        [ValueMode.Multi]: () => {\n          let currentValues = toRaw(api.value.value) as unknown[]\n\n          return (\n            api.options.value.find((option) =>\n              currentValues.some((value) => api.compare(toRaw(value), toRaw(option.dataRef.value)))\n            )?.id === props.id\n          )\n        },\n        [ValueMode.Single]: () => selected.value,\n      })\n    })\n\n    let getTextValue = useTextValue(internalOptionRef)\n    let dataRef = computed<ListboxOptionData>(() => ({\n      disabled: props.disabled,\n      value: props.value,\n      get textValue() {\n        return getTextValue()\n      },\n      domRef: internalOptionRef,\n    }))\n\n    onMounted(() => api.registerOption(props.id, dataRef))\n    onUnmounted(() => api.unregisterOption(props.id))\n\n    onMounted(() => {\n      watch(\n        [api.listboxState, selected],\n        () => {\n          if (api.listboxState.value !== ListboxStates.Open) return\n          if (!selected.value) return\n\n          match(api.mode.value, {\n            [ValueMode.Multi]: () => {\n              if (isFirstSelected.value) api.goToOption(Focus.Specific, props.id)\n            },\n            [ValueMode.Single]: () => {\n              api.goToOption(Focus.Specific, props.id)\n            },\n          })\n        },\n        { immediate: true }\n      )\n    })\n\n    watchEffect(() => {\n      if (api.listboxState.value !== ListboxStates.Open) return\n      if (!active.value) return\n      if (api.activationTrigger.value === ActivationTrigger.Pointer) return\n      nextTick(() => dom(internalOptionRef)?.scrollIntoView?.({ block: 'nearest' }))\n    })\n\n    function handleClick(event: MouseEvent) {\n      if (props.disabled) return event.preventDefault()\n      api.select(props.value)\n      if (api.mode.value === ValueMode.Single) {\n        api.closeListbox()\n        nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n      }\n    }\n\n    function handleFocus() {\n      if (props.disabled) return api.goToOption(Focus.Nothing)\n      api.goToOption(Focus.Specific, props.id)\n    }\n\n    let pointer = useTrackedPointer()\n\n    function handleEnter(evt: PointerEvent) {\n      pointer.update(evt)\n    }\n\n    function handleMove(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled) return\n      if (active.value) return\n      api.goToOption(Focus.Specific, props.id, ActivationTrigger.Pointer)\n    }\n\n    function handleLeave(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled) return\n      if (!active.value) return\n      api.goToOption(Focus.Nothing)\n    }\n\n    return () => {\n      let { disabled } = props\n      let slot = { active: active.value, selected: selected.value, disabled }\n      let { id, value: _value, disabled: _disabled, ...theirProps } = props\n      let ourProps = {\n        id,\n        ref: internalOptionRef,\n        role: 'option',\n        tabIndex: disabled === true ? undefined : -1,\n        'aria-disabled': disabled === true ? true : undefined,\n        // According to the WAI-ARIA best practices, we should use aria-checked for\n        // multi-select,but Voice-Over disagrees. So we use aria-checked instead for\n        // both single and multi-select.\n        'aria-selected': selected.value,\n        disabled: undefined, // Never forward the `disabled` prop\n        onClick: handleClick,\n        onFocus: handleFocus,\n        onPointerenter: handleEnter,\n        onMouseenter: handleEnter,\n        onPointermove: handleMove,\n        onMousemove: handleMove,\n        onPointerleave: handleLeave,\n        onMouseleave: handleLeave,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'ListboxOption',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/menu/menu.test.tsx",
    "content": "import { defineComponent, h, nextTick, reactive, ref, watch } from 'vue'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  MenuState,\n  assertActiveElement,\n  assertMenu,\n  assertMenuButton,\n  assertMenuButtonLinkedWithMenu,\n  assertMenuItem,\n  assertMenuLinkedWithMenuItem,\n  assertNoActiveMenuItem,\n  getByText,\n  getMenu,\n  getMenuButton,\n  getMenuButtons,\n  getMenuItems,\n  getMenus,\n} from '../../test-utils/accessibility-assertions'\nimport { jsx } from '../../test-utils/html'\nimport {\n  Keys,\n  MouseButton,\n  click,\n  focus,\n  mouseLeave,\n  mouseMove,\n  press,\n  shift,\n  type,\n  word,\n} from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { TransitionChild } from '../transitions/transition'\nimport { Menu, MenuButton, MenuItem, MenuItems } from './menu'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nconst renderTemplate = createRenderTemplate({ Menu, MenuButton, MenuItems, MenuItem })\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['MenuButton', MenuButton],\n    ['MenuItems', MenuItems],\n    ['MenuItem', MenuItem],\n  ])(\n    'should error when we are using a <%s /> without a parent <Menu />',\n    suppressConsoleLogs((name, component) => {\n      expect(() => render(component)).toThrow(`<${name} /> is missing a parent <Menu /> component.`)\n    })\n  )\n\n  it('should be possible to render a Menu without crashing', () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">Item A</MenuItem>\n          <MenuItem as=\"a\">Item B</MenuItem>\n          <MenuItem as=\"a\">Item C</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    assertMenuButton({\n      state: MenuState.InvisibleUnmounted,\n      attributes: { id: 'headlessui-menu-button-1' },\n    })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n  })\n})\n\ndescribe('Rendering', () => {\n  describe('Menu', () => {\n    it('should not crash when rendering no children at all', () => {\n      renderTemplate(jsx`\n        <Menu></Menu>\n      `)\n    })\n\n    it('should be possible to render a Menu using a default render prop', async () => {\n      renderTemplate(jsx`\n        <Menu v-slot=\"{ open }\">\n          <MenuButton>Trigger {{ open ? \"visible\" : \"hidden\" }}</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n        textContent: 'Trigger hidden',\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n        textContent: 'Trigger visible',\n      })\n      assertMenu({ state: MenuState.Visible })\n    })\n\n    it('should be possible to render a Menu using a template `as` prop', async () => {\n      renderTemplate(jsx`\n        <Menu as=\"template\">\n          <div>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </div>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible })\n    })\n\n    it(\n      'should yell when we render a Menu using a template `as` prop (default) that contains multiple children (if we passthrough props)',\n      suppressConsoleLogs(() => {\n        expect.hasAssertions()\n\n        renderTemplate({\n          template: jsx`\n              <Menu class=\"relative\">\n                <MenuButton>Trigger</MenuButton>\n                <MenuItems>\n                  <MenuItem as=\"a\">Item A</MenuItem>\n                  <MenuItem as=\"a\">Item B</MenuItem>\n                  <MenuItem as=\"a\">Item C</MenuItem>\n                </MenuItems>\n              </Menu>\n            `,\n          errorCaptured(err) {\n            expect(err as Error).toEqual(\n              new Error(\n                [\n                  'Passing props on \"template\"!',\n                  '',\n                  'The current component <Menu /> is rendering a \"template\".',\n                  'However we need to passthrough the following props:',\n                  '  - class',\n                  '',\n                  'You can apply a few solutions:',\n                  '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n                  '  - Render a single element as the child so that we can forward the props onto that element.',\n                ].join('\\n')\n              )\n            )\n\n            return false\n          },\n        })\n      })\n    )\n\n    it('should be possible to manually close the Menu using the exposed close function', async () => {\n      renderTemplate({\n        template: jsx`\n          <Menu v-slot=\"{ close }\">\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem>\n                <button @click.prevent=\"close\">Close</button>\n              </MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n      })\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenu({ state: MenuState.Visible })\n\n      await click(getByText('Close'))\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  })\n\n  describe('MenuButton', () => {\n    it('should be possible to render a MenuButton using a default render prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton v-slot=\"{ open }\">\n            Trigger {{ open ? \"visible\" : \"hidden\" }}\n          </MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n        textContent: 'Trigger hidden',\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n        textContent: 'Trigger visible',\n      })\n      assertMenu({ state: MenuState.Visible })\n    })\n\n    it('should be possible to render a MenuButton using a template `as` prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton as=\"template\" v-slot=\"{ open }\">\n            <button :data-open=\"open\">Trigger</button>\n          </MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1', 'data-open': 'false' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1', 'data-open': 'true' },\n      })\n      assertMenu({ state: MenuState.Visible })\n    })\n\n    it('should be possible to render a MenuButton using a template `as` prop and a custom element', async () => {\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton as=\"template\" v-slot=\"{ open }\">\n              <MyCustomButton :data-open=\"open\">Options</MyCustomButton>\n            </MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n        components: {\n          MyCustomButton: defineComponent({\n            setup(_, { slots }) {\n              return () => {\n                return h('button', slots.default?.())\n              }\n            },\n          }),\n        },\n      })\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1', 'data-open': 'false' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1', 'data-open': 'true' },\n      })\n      assertMenu({ state: MenuState.Visible })\n    })\n\n    it(\n      'should yell when we render a MenuButton using a template `as` prop that contains multiple children',\n      suppressConsoleLogs(() => {\n        expect.hasAssertions()\n\n        renderTemplate({\n          template: jsx`\n            <Menu>\n              <MenuButton as=\"template\">\n                <span>Trigger</span>\n                <svg />\n              </MenuButton>\n              <MenuItems>\n                <MenuItem as=\"a\">Item A</MenuItem>\n                <MenuItem as=\"a\">Item B</MenuItem>\n                <MenuItem as=\"a\">Item C</MenuItem>\n              </MenuItems>\n            </Menu>\n          `,\n          errorCaptured(err) {\n            expect(err as Error).toEqual(\n              new Error(\n                [\n                  'Passing props on \"template\"!',\n                  '',\n                  'The current component <MenuButton /> is rendering a \"template\".',\n                  'However we need to passthrough the following props:',\n                  '  - aria-controls',\n                  '  - aria-expanded',\n                  '  - aria-haspopup',\n                  '  - disabled',\n                  '  - id',\n                  '  - onClick',\n                  '  - onKeydown',\n                  '  - onKeyup',\n                  '  - ref',\n                  '  - type',\n                  '',\n                  'You can apply a few solutions:',\n                  '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n                  '  - Render a single element as the child so that we can forward the props onto that element.',\n                ].join('\\n')\n              )\n            )\n\n            return false\n          },\n        })\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        renderTemplate(\n          jsx`\n            <Menu>\n              <MenuButton>Trigger</MenuButton>\n            </Menu>\n          `\n        )\n\n        expect(getMenuButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        renderTemplate(\n          jsx`\n            <Menu>\n              <MenuButton type=\"submit\">\n                Trigger\n              </MenuButton>\n            </Menu>\n          `\n        )\n\n        expect(getMenuButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: jsx`\n              <Menu>\n                <MenuButton :as=\"CustomButton\">\n                  Trigger\n                </MenuButton>\n              </Menu>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('button', { ...props }),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getMenuButton()).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        renderTemplate(\n          jsx`\n            <Menu>\n              <MenuButton as=\"div\">\n                Trigger\n              </MenuButton>\n            </Menu>\n          `\n        )\n\n        expect(getMenuButton()).not.toHaveAttribute('type')\n      })\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: jsx`\n              <Menu>\n                <MenuButton :as=\"CustomButton\">\n                  Trigger\n                </MenuButton>\n              </Menu>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('div', props),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getMenuButton()).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n\n  describe('MenuItems', () => {\n    it('should be possible to render MenuItems using a default render prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems v-slot=\"{ open }\">\n            <span>{{ open ? \"visible\" : \"hidden\" }}</span>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible })\n      expect(getMenu()?.firstChild?.textContent).toBe('visible')\n    })\n\n    it('should be possible to render MenuItems using a template `as` prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems as=\"template\" v-slot=\"{ open }\">\n            <div :data-open=\"open\">\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </div>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible, attributes: { 'data-open': 'true' } })\n    })\n\n    it('should yell when we render MenuItems using a template `as` prop that contains multiple children', async () => {\n      expect.assertions(1)\n\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems as=\"template\">\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n        errorCaptured(err) {\n          expect(err as Error).toEqual(\n            new Error(\n              [\n                'Passing props on \"template\"!',\n                '',\n                'The current component <MenuItems /> is rendering a \"template\".',\n                'However we need to passthrough the following props:',\n                '  - aria-activedescendant',\n                '  - aria-labelledby',\n                '  - id',\n                '  - onKeydown',\n                '  - onKeyup',\n                '  - ref',\n                '  - role',\n                '  - tabIndex',\n                '',\n                'You can apply a few solutions:',\n                '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n                '  - Render a single element as the child so that we can forward the props onto that element.',\n              ].join('\\n')\n            )\n          )\n\n          return false\n        },\n      })\n\n      await click(getMenuButton())\n    })\n\n    it('should be possible to always render the MenuItems if we provide it a `static` prop', () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems static>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Let's verify that the Menu is already there\n      expect(getMenu()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the MenuItems', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems :unmount=\"false\">\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertMenu({ state: MenuState.InvisibleHidden })\n\n      // Let's open the Menu, to see if it is not hidden anymore\n      await click(getMenuButton())\n\n      assertMenu({ state: MenuState.Visible })\n    })\n  })\n\n  describe('MenuItem', () => {\n    it('should be possible to render MenuItem using a default render prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem v-slot=\"{ active, disabled }\">\n              <span>Item A - {{ JSON.stringify({ active, disabled }) }}</span>\n            </MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible })\n      expect(getMenuItems()[0]?.textContent).toBe(\n        `Item A - ${JSON.stringify({ active: false, disabled: false })}`\n      )\n    })\n\n    it('should be possible to render a MenuItem using a template `as` prop', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"template\" v-slot=\"{ active, disabled }\">\n              <a :data-active=\"active\" :data-disabled=\"disabled\">Item A</a>\n            </MenuItem>\n            <MenuItem as=\"template\" v-slot=\"{ active, disabled }\">\n              <a :data-active=\"active\" :data-disabled=\"disabled\">Item B</a>\n            </MenuItem>\n            <MenuItem disabled as=\"template\" v-slot=\"{ active, disabled }\">\n              <a :data-active=\"active\" :data-disabled=\"disabled\">Item C</a>\n            </MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      getMenuButton()?.focus()\n\n      await press(Keys.Enter)\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible })\n      assertMenuItem(getMenuItems()[0], {\n        tag: 'a',\n        attributes: { 'data-active': 'true', 'data-disabled': 'false' },\n      })\n      assertMenuItem(getMenuItems()[1], {\n        tag: 'a',\n        attributes: { 'data-active': 'false', 'data-disabled': 'false' },\n      })\n      assertMenuItem(getMenuItems()[2], {\n        tag: 'a',\n        attributes: { 'data-active': 'false', 'data-disabled': 'true' },\n      })\n    })\n\n    it('should not override an explicit disabled prop on MenuItems child', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem v-slot=\"{ active, disabled }\">\n              <button :data-active=\"active\" :disabled=\"disabled\">Item A</button>\n            </MenuItem>\n            <MenuItem v-slot=\"{ active, disabled }\">\n              <button :data-active=\"active\" :disabled=\"disabled\">Item B</button>\n            </MenuItem>\n            <MenuItem disabled v-slot=\"{ active, disabled }\">\n              <button :data-active=\"active\" :disabled=\"disabled\">Item C</button>\n            </MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      getMenuButton()?.focus()\n\n      await press(Keys.Enter)\n\n      assertMenuButton({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.Visible })\n      assertMenuItem(getMenuItems()[0], {\n        tag: 'button',\n        attributes: { 'data-active': 'true' },\n      })\n      assertMenuItem(getMenuItems()[1], {\n        tag: 'button',\n        attributes: { 'data-active': 'false' },\n      })\n      assertMenuItem(getMenuItems()[2], {\n        tag: 'button',\n        attributes: { 'data-active': 'false', disabled: '' },\n      })\n    })\n\n    it('should yell when we render a MenuItem using a template `as` prop that contains multiple children', async () => {\n      expect.hasAssertions()\n\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"template\">\n                <span>Item A</span>\n                <svg />\n              </MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n        errorCaptured(err) {\n          expect(err as Error).toEqual(\n            new Error(\n              [\n                'Passing props on \"template\"!',\n                '',\n                'The current component <MenuItem /> is rendering a \"template\".',\n                'However we need to passthrough the following props:',\n                '  - aria-disabled',\n                '  - id',\n                '  - onClick',\n                '  - onFocus',\n                '  - onMouseenter',\n                '  - onMouseleave',\n                '  - onMousemove',\n                '  - onPointerenter',\n                '  - onPointerleave',\n                '  - onPointermove',\n                '  - ref',\n                '  - role',\n                '  - tabIndex',\n                '',\n                'You can apply a few solutions:',\n                '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n                '  - Render a single element as the child so that we can forward the props onto that element.',\n              ].join('\\n')\n            )\n          )\n\n          return false\n        },\n      })\n\n      await click(getMenuButton())\n    })\n\n    it('should be possible to manually close the Menu using the exposed close function', async () => {\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem v-slot=\"{ close }\">\n                <button @click.prevent=\"close\">Close</button>\n              </MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n      })\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      await click(getMenuButton())\n\n      assertMenu({ state: MenuState.Visible })\n\n      await click(getByText('Close'))\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    let props = reactive({ hide: false })\n\n    renderTemplate({\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"button\">Item 1</MenuItem>\n            <MenuItem v-if=\"!hide\" as=\"button\">Item 2</MenuItem>\n            <MenuItem as=\"button\">Item 3</MenuItem>\n          </MenuItems>\n        </Menu>\n      `,\n      setup() {\n        return {\n          get hide() {\n            return props.hide\n          },\n        }\n      },\n    })\n\n    // Open the Menu\n    await click(getByText('Trigger'))\n\n    props.hide = true\n    await nextFrame()\n\n    props.hide = false\n    await nextFrame()\n\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // Focus the first item\n    await press(Keys.ArrowDown)\n\n    // Verify that the first menu item is active\n    assertMenuLinkedWithMenuItem(items[0])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the second menu item is active\n    assertMenuLinkedWithMenuItem(items[1])\n\n    await press(Keys.ArrowDown)\n\n    // Verify that the third menu item is active\n    assertMenuLinkedWithMenuItem(items[2])\n  })\n\n  it(\n    'should be possible to use a custom component using the `as` prop without crashing',\n    suppressConsoleLogs(async () => {\n      let CustomComponent = defineComponent({\n        template: `<button><slot /></button>`,\n      })\n\n      renderTemplate({\n        template: `\n          <Menu>\n            <MenuButton />\n            <MenuOptions>\n              <MenuOption :as=\"CustomComponent\">Alice</RadioGroupOption>\n              <MenuOption :as=\"CustomComponent\">Bob</RadioGroupOption>\n              <MenuOption :as=\"CustomComponent\">Charlie</RadioGroupOption>\n            </MenuOptions>\n          </Menu>\n        `,\n        setup: () => ({ CustomComponent }),\n      })\n\n      // Open menu\n      await click(getMenuButton())\n    })\n  )\n})\n\ndescribe('Rendering composition', () => {\n  it(\n    'should be possible to swap the menu item with a button for example',\n    suppressConsoleLogs(async () => {\n      let MyButton = defineComponent({\n        setup(props) {\n          return () => h('button', { 'data-my-custom-button': true, ...props })\n        },\n      })\n\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem :as=\"MyButton\">Item A</MenuItem>\n              <MenuItem :as=\"MyButton\">Item B</MenuItem>\n              <MenuItem :as=\"MyButton\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n        setup: () => ({ MyButton }),\n      })\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify items are buttons now\n      let items = getMenuItems()\n      items.forEach((item) =>\n        assertMenuItem(item, { tag: 'button', attributes: { 'data-my-custom-button': 'true' } })\n      )\n    })\n  )\n\n  it(\n    'should mark all the elements between Menu.Items and Menu.Item with role none',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <div class=\"outer\">\n              <MenuItems>\n                <div class=\"py-1 inner\">\n                  <MenuItem as=\"button\">Item A</MenuItem>\n                  <MenuItem as=\"button\">Item B</MenuItem>\n                </div>\n                <div class=\"py-1 inner\">\n                  <MenuItem as=\"button\">Item C</MenuItem>\n                  <MenuItem>\n                    <div>\n                      <div class=\"outer\">Item D</div>\n                    </div>\n                  </MenuItem>\n                </div>\n                <div class=\"py-1 inner\">\n                  <form class=\"inner\">\n                    <MenuItem as=\"button\">Item E</MenuItem>\n                  </form>\n                </div>\n              </MenuItems>\n            </div>\n          </Menu>\n        `,\n      })\n\n      // Open menu\n      await click(getMenuButton())\n\n      expect.hasAssertions()\n\n      document.querySelectorAll('.outer').forEach((element) => {\n        expect(element).not.toHaveAttribute('role', 'none')\n      })\n\n      document.querySelectorAll('.inner').forEach((element) => {\n        expect(element).toHaveAttribute('role', 'none')\n      })\n    })\n  )\n})\n\ndescribe('Composition', () => {\n  let OpenClosedWrite = defineComponent({\n    props: { open: { type: Boolean } },\n    setup(props, { slots }) {\n      useOpenClosedProvider(ref(props.open ? State.Open : State.Closed))\n      return () => slots.default?.()\n    },\n  })\n\n  let OpenClosedRead = defineComponent({\n    emits: ['read'],\n    setup(_, { slots, emit }) {\n      let state = useOpenClosed()\n      watch([state], ([value]) => emit('read', value))\n      return () => slots.default?.()\n    },\n  })\n\n  it(\n    'should always open the MenuItems because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <OpenClosedWrite :open=\"true\">\n              <MenuItems v-slot=\"data\">\n                {{JSON.stringify(data)}}\n              </MenuItems>\n            </OpenClosedWrite>\n          </Menu>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Menu is visible\n      assertMenu({ state: MenuState.Visible })\n\n      // Let's try and open the Menu\n      await click(getMenuButton())\n\n      // Verify the Menu is still visible\n      assertMenu({ state: MenuState.Visible })\n    })\n  )\n\n  it(\n    'should always close the MenuItems because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <OpenClosedWrite :open=\"false\">\n              <MenuItems v-slot=\"data\">\n                {{JSON.stringify(data)}}\n              </MenuItems>\n            </OpenClosedWrite>\n          </Menu>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Menu is hidden\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Let's try and open the Menu\n      await click(getMenuButton())\n\n      // Verify the Menu is still hidden\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to read the OpenClosed state',\n    suppressConsoleLogs(async () => {\n      let readFn = jest.fn()\n      renderTemplate({\n        components: { OpenClosedRead },\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <OpenClosedRead @read=\"readFn\">\n              <MenuItems></MenuItems>\n            </OpenClosedRead>\n          </Menu>\n        `,\n        setup() {\n          return { readFn }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Menu is hidden\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Let's toggle the Menu 3 times\n      await click(getMenuButton())\n      await click(getMenuButton())\n      await click(getMenuButton())\n\n      // Verify the Menu is visible\n      assertMenu({ state: MenuState.Visible })\n\n      expect(readFn).toHaveBeenCalledTimes(3)\n      expect(readFn).toHaveBeenNthCalledWith(1, State.Open)\n      expect(readFn).toHaveBeenNthCalledWith(2, State.Closed)\n      expect(readFn).toHaveBeenNthCalledWith(3, State.Open)\n    })\n  )\n\n  it('should be possible to render a TransitionChild that inherits state from the Menu', async () => {\n    let readFn = jest.fn()\n    renderTemplate({\n      components: { TransitionChild },\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <TransitionChild\n            as=\"template\"\n            @beforeEnter=\"readFn('enter')\"\n            @beforeLeave=\"readFn('leave')\"\n          >\n            <MenuItems>\n              <MenuItem as=\"button\">I am a button</MenuItem>\n            </MenuItems>\n          </TransitionChild>\n        </Menu>\n      `,\n      setup() {\n        return { readFn }\n      },\n    })\n\n    // Verify the Menu is hidden\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Let's toggle the Menu\n    await click(getMenuButton())\n\n    // Verify that our transition fired\n    expect(readFn).toHaveBeenCalledTimes(1)\n    expect(readFn).toHaveBeenNthCalledWith(1, 'enter')\n\n    // Verify the Menu is visible\n    assertMenu({ state: MenuState.Visible })\n\n    // Let's toggle the Menu\n    await click(getMenuButton())\n\n    // Verify that our transition fired\n    expect(readFn).toHaveBeenCalledTimes(2)\n    expect(readFn).toHaveBeenNthCalledWith(2, 'leave')\n\n    // Wait for the transitions to finish\n    await nextFrame()\n\n    // Verify the Menu is hidden\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it('should be possible to open the menu with Enter', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n\n      // Verify that the first menu item is active\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should not be possible to open the menu with Enter when the button is disabled', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton disabled>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Try to open the menu\n      await press(Keys.Enter)\n\n      // Verify it is still closed\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n\n    it('should have no active menu item when there are no menu items at all', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems />\n        </Menu>\n      `)\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n      assertMenu({ state: MenuState.Visible })\n\n      assertNoActiveMenuItem()\n    })\n\n    it('should focus the first non disabled menu item when opening with Enter', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // Verify that the first non-disabled menu item is active\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n\n    it('should focus the first non disabled menu item when opening with Enter (jump over multiple disabled ones)', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // Verify that the first non-disabled menu item is active\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should have no active menu item upon Enter key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      assertNoActiveMenuItem()\n    })\n\n    it('should be possible to close the menu with Enter when there is no active menuitem', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n\n      // Close menu\n      await press(Keys.Enter)\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n\n    it('should be possible to close the menu with Enter and invoke the active menu item', async () => {\n      let clickHandler = jest.fn()\n      renderTemplate({\n        template: jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\" @click=\"clickHandler\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `,\n        setup: () => ({ clickHandler }),\n      })\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Open menu\n      await click(getMenuButton())\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n\n      // Activate the first menu item\n      let items = getMenuItems()\n      await mouseMove(items[0])\n\n      // Close menu, and invoke the item\n      await press(Keys.Enter)\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n\n      // Verify the \"click\" went through on the `a` tag\n      expect(clickHandler).toHaveBeenCalled()\n    })\n  })\n\n  it('should be possible to use a button as a menu item and invoke it upon Enter', async () => {\n    let clickHandler = jest.fn()\n\n    renderTemplate({\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"button\" @click=\"clickHandler\">\n              Item B\n            </MenuItem>\n            <MenuItem>\n              <button @click=\"clickHandler\">Item C</button>\n            </MenuItem>\n          </MenuItems>\n        </Menu>\n      `,\n      setup: () => ({ clickHandler }),\n    })\n\n    assertMenuButton({\n      state: MenuState.InvisibleUnmounted,\n      attributes: { id: 'headlessui-menu-button-1' },\n    })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Open menu\n    await click(getMenuButton())\n\n    // Verify it is open\n    assertMenuButton({ state: MenuState.Visible })\n\n    // Activate the second menu item\n    let items = getMenuItems()\n    await mouseMove(items[1])\n\n    // Close menu, and invoke the item\n    await press(Keys.Enter)\n\n    // Verify it is closed\n    assertMenuButton({ state: MenuState.InvisibleUnmounted })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Verify the button got \"clicked\"\n    expect(clickHandler).toHaveBeenCalledTimes(1)\n\n    // Verify the button is focused again\n    assertActiveElement(getMenuButton())\n\n    // Click the menu button again\n    await click(getMenuButton())\n\n    // Activate the last menu item\n    await mouseMove(getMenuItems()[2])\n\n    // Close menu, and invoke the item\n    await press(Keys.Enter)\n\n    // Verify the button got \"clicked\"\n    expect(clickHandler).toHaveBeenCalledTimes(2)\n\n    // Verify the button is focused again\n    assertActiveElement(getMenuButton())\n  })\n\n  describe('`Space` key', () => {\n    it('should be possible to open the menu with Space', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should not be possible to open the menu with Space when the button is disabled', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton disabled>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Try to open the menu\n      await press(Keys.Space)\n\n      // Verify it is still closed\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n\n    it('should have no active menu item when there are no menu items at all', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems />\n        </Menu>\n      `)\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n      assertMenu({ state: MenuState.Visible })\n\n      assertNoActiveMenuItem()\n    })\n\n    it('should focus the first non disabled menu item when opening with Space', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n\n      let items = getMenuItems()\n\n      // Verify that the first non-disabled menu item is active\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n\n    it('should focus the first non disabled menu item when opening with Space (jump over multiple disabled ones)', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n\n      let items = getMenuItems()\n\n      // Verify that the first non-disabled menu item is active\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should have no active menu item upon Space key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n\n      assertNoActiveMenuItem()\n    })\n\n    it(\n      'should be possible to close the menu with Space when there is no active menuitem',\n      suppressConsoleLogs(async () => {\n        renderTemplate(jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">Item A</MenuItem>\n              <MenuItem as=\"a\">Item B</MenuItem>\n              <MenuItem as=\"a\">Item C</MenuItem>\n            </MenuItems>\n          </Menu>\n        `)\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-menu-button-1' },\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open Menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Close Menu\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n\n    it(\n      'should be possible to close the menu with Space and invoke the active menu item',\n      suppressConsoleLogs(async () => {\n        let clickHandler = jest.fn()\n        renderTemplate({\n          template: jsx`\n            <Menu>\n              <MenuButton>Trigger</MenuButton>\n              <MenuItems>\n                <MenuItem as=\"a\" @click=\"clickHandler\">Item A</MenuItem>\n                <MenuItem as=\"a\">Item B</MenuItem>\n                <MenuItem as=\"a\">Item C</MenuItem>\n              </MenuItems>\n            </Menu>\n          `,\n          setup: () => ({ clickHandler }),\n        })\n\n        assertMenuButton({\n          state: MenuState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-menu-button-1' },\n        })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Open menu\n        await click(getMenuButton())\n\n        // Verify it is open\n        assertMenuButton({ state: MenuState.Visible })\n\n        // Activate the first menu item\n        let items = getMenuItems()\n        await mouseMove(items[0])\n\n        // Close menu, and invoke the item\n        await press(Keys.Space)\n\n        // Verify it is closed\n        assertMenuButton({ state: MenuState.InvisibleUnmounted })\n        assertMenu({ state: MenuState.InvisibleUnmounted })\n\n        // Verify the \"click\" went through on the `a` tag\n        expect(clickHandler).toHaveBeenCalled()\n\n        // Verify the button is focused again\n        assertActiveElement(getMenuButton())\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it('should be possible to close an open menu with Escape', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Space)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Close menu\n      await press(Keys.Escape)\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  })\n\n  describe('`Tab` key', () => {\n    it('should not focus trap when we use Tab', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // Try to tab\n      await press(Keys.Tab)\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n\n    it('should not focus trap when we use Shift+Tab', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // Try to Shift+Tab\n      await press(shift(Keys.Tab))\n\n      // Verify it is closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n  })\n\n  describe('`ArrowDown` key', () => {\n    it('should be possible to open the menu with ArrowDown', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowDown)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n\n      // Verify that the first menu item is active\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should not be possible to open the menu with ArrowDown when the button is disabled', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton disabled>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Try to open the menu\n      await press(Keys.ArrowDown)\n\n      // Verify it is still closed\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n    })\n\n    it('should have no active menu item when there are no menu items at all', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems />\n        </Menu>\n      `)\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowDown)\n      assertMenu({ state: MenuState.Visible })\n\n      assertNoActiveMenuItem()\n    })\n\n    it('should be possible to use ArrowDown to navigate the menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go down once\n      await press(Keys.ArrowDown)\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go down again\n      await press(Keys.ArrowDown)\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should NOT be able to go down again (because last item). Current implementation won't go around.\n      await press(Keys.ArrowDown)\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use ArrowDown to navigate the menu items and skip the first disabled one', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go down once\n      await press(Keys.ArrowDown)\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use ArrowDown to navigate the menu items and jump to the first non-disabled one', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n  })\n\n  describe('`ArrowUp` key', () => {\n    it('should be possible to open the menu with ArrowUp and the last item should be active', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n\n      // ! ALERT: The LAST item should now be active\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should have no active menu item when there are no menu items at all', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems />\n        </Menu>\n      `)\n\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n      assertMenu({ state: MenuState.Visible })\n\n      assertNoActiveMenuItem()\n    })\n\n    it('should be possible to use ArrowUp to navigate the menu items and jump to the first non-disabled one', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should not be possible to navigate up or down if there is only a single non-disabled item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should not be able to go up (because those are disabled)\n      await press(Keys.ArrowUp)\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should not be able to go down (because this is the last item)\n      await press(Keys.ArrowDown)\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use ArrowUp to navigate the menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      // Verify it is open\n      assertMenuButton({ state: MenuState.Visible })\n      assertMenu({\n        state: MenuState.Visible,\n        attributes: { id: 'headlessui-menu-items-2' },\n      })\n      assertMenuButtonLinkedWithMenu()\n\n      // Verify we have menu items\n      let items = getMenuItems()\n      expect(items).toHaveLength(3)\n      items.forEach((item) => assertMenuItem(item))\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should be able to go down once\n      await press(Keys.ArrowUp)\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go down again\n      await press(Keys.ArrowUp)\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should NOT be able to go up again (because first item). Current implementation won't go around.\n      await press(Keys.ArrowUp)\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n  })\n\n  describe('`End` key', () => {\n    it('should be possible to use the End key to go to the last menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // We should be on the first item\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last item\n      await press(Keys.End)\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use the End key to go to the last non disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // We should be on the first item\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last non-disabled item\n      await press(Keys.End)\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n\n    it('should be possible to use the End key to go to the first menu item if that is the only non-disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.End)\n\n      let items = getMenuItems()\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should have no active menu item upon End key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.End)\n\n      assertNoActiveMenuItem()\n    })\n  })\n\n  describe('`PageDown` key', () => {\n    it('should be possible to use the PageDown key to go to the last menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // We should be on the first item\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last item\n      await press(Keys.PageDown)\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use the PageDown key to go to the last non disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.Enter)\n\n      let items = getMenuItems()\n\n      // We should be on the first item\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last non-disabled item\n      await press(Keys.PageDown)\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n\n    it('should be possible to use the PageDown key to go to the first menu item if that is the only non-disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.PageDown)\n\n      let items = getMenuItems()\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should have no active menu item upon PageDown key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.PageDown)\n\n      assertNoActiveMenuItem()\n    })\n  })\n\n  describe('`Home` key', () => {\n    it('should be possible to use the Home key to go to the first menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      let items = getMenuItems()\n\n      // We should be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should be able to go to the first item\n      await press(Keys.Home)\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should be possible to use the Home key to go to the first non disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n            <MenuItem as=\"a\">Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.Home)\n\n      let items = getMenuItems()\n\n      // We should be on the first non-disabled item\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use the Home key to go to the last menu item if that is the only non-disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\">Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.Home)\n\n      let items = getMenuItems()\n      assertMenuLinkedWithMenuItem(items[3])\n    })\n\n    it('should have no active menu item upon Home key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.Home)\n\n      assertNoActiveMenuItem()\n    })\n  })\n\n  describe('`PageUp` key', () => {\n    it('should be possible to use the PageUp key to go to the first menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      let items = getMenuItems()\n\n      // We should be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should be able to go to the first item\n      await press(Keys.PageUp)\n      assertMenuLinkedWithMenuItem(items[0])\n    })\n\n    it('should be possible to use the PageUp key to go to the first non disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n            <MenuItem as=\"a\">Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.PageUp)\n\n      let items = getMenuItems()\n\n      // We should be on the first non-disabled item\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to use the PageUp key to go to the last menu item if that is the only non-disabled menu item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\">Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.PageUp)\n\n      let items = getMenuItems()\n      assertMenuLinkedWithMenuItem(items[3])\n    })\n\n    it('should have no active menu item upon PageUp key press, when there are no non-disabled menu items', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\" disabled>Item A</MenuItem>\n            <MenuItem as=\"a\" disabled>Item B</MenuItem>\n            <MenuItem as=\"a\" disabled>Item C</MenuItem>\n            <MenuItem as=\"a\" disabled>Item D</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      // We opened via click, we don't have an active item\n      assertNoActiveMenuItem()\n\n      // We should not be able to go to the end\n      await press(Keys.PageUp)\n\n      assertNoActiveMenuItem()\n    })\n  })\n\n  describe('`Any` key aka search', () => {\n    it('should be possible to type a full word that has a perfect match', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\">bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // We should be able to go to the second item\n      await type(word('bob'))\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go to the first item\n      await type(word('alice'))\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last item\n      await type(word('charlie'))\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to type a partial of a word', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\">bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      let items = getMenuItems()\n\n      // We should be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should be able to go to the second item\n      await type(word('bo'))\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // We should be able to go to the first item\n      await type(word('ali'))\n      assertMenuLinkedWithMenuItem(items[0])\n\n      // We should be able to go to the last item\n      await type(word('char'))\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it(\n      'should be possible to type words with spaces',\n      suppressConsoleLogs(async () => {\n        renderTemplate(jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">value a</MenuItem>\n              <MenuItem as=\"a\">value b</MenuItem>\n              <MenuItem as=\"a\">value c</MenuItem>\n            </MenuItems>\n          </Menu>\n        `)\n\n        // Focus the button\n        getMenuButton()?.focus()\n\n        // Open menu\n        await press(Keys.ArrowUp)\n\n        let items = getMenuItems()\n\n        // We should be on the last item\n        assertMenuLinkedWithMenuItem(items[2])\n\n        // We should be able to go to the second item\n        await type(word('value b'))\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // We should be able to go to the first item\n        await type(word('value a'))\n        assertMenuLinkedWithMenuItem(items[0])\n\n        // We should be able to go to the last item\n        await type(word('value c'))\n        assertMenuLinkedWithMenuItem(items[2])\n      })\n    )\n\n    it('should not be possible to search for a disabled item', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\" disabled>bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      let items = getMenuItems()\n\n      // We should be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // We should not be able to go to the disabled item\n      await type(word('bo'))\n\n      // We should still be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n    })\n\n    it('should be possible to search for a word (case insensitive)', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\">bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Focus the button\n      getMenuButton()?.focus()\n\n      // Open menu\n      await press(Keys.ArrowUp)\n\n      let items = getMenuItems()\n\n      // We should be on the last item\n      assertMenuLinkedWithMenuItem(items[2])\n\n      // Search for bob in a different casing\n      await type(word('BO'))\n\n      // We should be on `bob`\n      assertMenuLinkedWithMenuItem(items[1])\n    })\n\n    it('should be possible to search for the next occurrence', async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\">bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n            <MenuItem as=\"a\">bob</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n\n      let items = getMenuItems()\n\n      // Search for bob\n      await type(word('b'))\n\n      // We should be on the first `bob`\n      assertMenuLinkedWithMenuItem(items[1])\n\n      // Search for bob again\n      await type(word('b'))\n\n      // We should be on the second `bob`\n      assertMenuLinkedWithMenuItem(items[3])\n    })\n\n    it(\n      'should stay on the same item while keystrokes still match',\n      suppressConsoleLogs(async () => {\n        renderTemplate(jsx`\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">alice</MenuItem>\n              <MenuItem as=\"a\">bob</MenuItem>\n              <MenuItem as=\"a\">charlie</MenuItem>\n              <MenuItem as=\"a\">bob</MenuItem>\n            </MenuItems>\n          </Menu>\n        `)\n\n        // Open menu\n        await click(getMenuButton())\n\n        let items = getMenuItems()\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"b\" in \"bob\"\n        await type(word('b'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"b\" in \"bob\" again\n        await type(word('b'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"bo\" in \"bob\"\n        await type(word('bo'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"bo\" in \"bob\" again\n        await type(word('bo'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n\n        // ---\n\n        // Reset: Go to first item\n        await press(Keys.Home)\n\n        // Search for \"bob\" in \"bob\"\n        await type(word('bob'))\n\n        // We should be on the first `bob`\n        assertMenuLinkedWithMenuItem(items[1])\n\n        // Search for \"bob\" in \"bob\" again\n        await type(word('bob'))\n\n        // We should be on the next `bob`\n        assertMenuLinkedWithMenuItem(items[3])\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it('should be possible to open a menu on click', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">Item A</MenuItem>\n          <MenuItem as=\"a\">Item B</MenuItem>\n          <MenuItem as=\"a\">Item C</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    assertMenuButton({\n      state: MenuState.InvisibleUnmounted,\n      attributes: { id: 'headlessui-menu-button-1' },\n    })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Open menu\n    await click(getMenuButton())\n\n    // Verify it is open\n    assertMenuButton({ state: MenuState.Visible })\n    assertMenu({\n      state: MenuState.Visible,\n      attributes: { id: 'headlessui-menu-items-2' },\n    })\n    assertMenuButtonLinkedWithMenu()\n\n    // Verify we have menu items\n    let items = getMenuItems()\n    expect(items).toHaveLength(3)\n    items.forEach((item) => assertMenuItem(item))\n  })\n\n  it(\n    'should not be possible to open a menu on right click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">Item A</MenuItem>\n            <MenuItem as=\"a\">Item B</MenuItem>\n            <MenuItem as=\"a\">Item C</MenuItem>\n          </MenuItems>\n        </Menu>\n      `)\n\n      assertMenuButton({\n        state: MenuState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-menu-button-1' },\n      })\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Try to open the menu\n      await click(getMenuButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertMenuButton({ state: MenuState.InvisibleUnmounted })\n    })\n  )\n\n  it('should not be possible to open a menu on click when the button is disabled', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton disabled>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">Item A</MenuItem>\n          <MenuItem as=\"a\">Item B</MenuItem>\n          <MenuItem as=\"a\">Item C</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    assertMenuButton({\n      state: MenuState.InvisibleUnmounted,\n      attributes: { id: 'headlessui-menu-button-1' },\n    })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Try to open the menu\n    await click(getMenuButton())\n\n    // Verify it is still closed\n    assertMenuButton({\n      state: MenuState.InvisibleUnmounted,\n      attributes: { id: 'headlessui-menu-button-1' },\n    })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n  })\n\n  it('should be possible to close a menu on click', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">Item A</MenuItem>\n          <MenuItem as=\"a\">Item B</MenuItem>\n          <MenuItem as=\"a\">Item C</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    // Verify it is open\n    assertMenuButton({ state: MenuState.Visible })\n\n    // Click to close\n    await click(getMenuButton())\n\n    // Verify it is closed\n    assertMenuButton({ state: MenuState.InvisibleUnmounted })\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n  })\n\n  it('should be a no-op when we click outside of a closed menu', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Verify that the window is closed\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Click something that is not related to the menu\n    await click(document.body)\n\n    // Should still be closed\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n  })\n\n  it('should be possible to click outside of the menu which should close the menu', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    // Click something that is not related to the menu\n    await click(document.body)\n\n    // Should be closed now\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Verify the button is focused again\n    assertActiveElement(getMenuButton())\n  })\n\n  it('should be possible to click outside of the menu which should close the menu (even if we press the menu button)', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    // Click the menu button again\n    await click(getMenuButton())\n\n    // Should be closed now\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Verify the button is focused again\n    assertActiveElement(getMenuButton())\n  })\n\n  it(\n    'should be possible to click outside of the menu on another menu button which should close the current menu and open the new menu',\n    suppressConsoleLogs(async () => {\n      renderTemplate(jsx`\n        <div>\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">alice</MenuItem>\n              <MenuItem as=\"a\">bob</MenuItem>\n              <MenuItem as=\"a\">charlie</MenuItem>\n            </MenuItems>\n          </Menu>\n\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <MenuItem as=\"a\">alice</MenuItem>\n              <MenuItem as=\"a\">bob</MenuItem>\n              <MenuItem as=\"a\">charlie</MenuItem>\n            </MenuItems>\n          </Menu>\n        </div>\n      `)\n\n      let [button1, button2] = getMenuButtons()\n\n      // Click the first menu button\n      await click(button1)\n      expect(getMenus()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the first button\n      assertMenuButtonLinkedWithMenu(button1, getMenu())\n\n      // Click the second menu button\n      await click(button2)\n\n      expect(getMenus()).toHaveLength(1) // Only 1 menu should be visible\n\n      // Ensure the open menu is linked to the second button\n      assertMenuButtonLinkedWithMenu(button2, getMenu())\n    })\n  )\n\n  // TODO: This test doesn't work — and it would be more suited for browser testing anyway\n  it.skip(\n    'should be possible to click outside of the menu into an iframe and which should close the menu',\n    suppressConsoleLogs(async () => {\n      renderTemplate(`\n        <div>\n          <Menu>\n            <MenuButton>Trigger</MenuButton>\n            <MenuItems>\n              <menuitem as=\"a\">alice</menuitem>\n              <menuitem as=\"a\">bob</menuitem>\n              <menuitem as=\"a\">charlie</menuitem>\n            </MenuItems>\n          </Menu>\n          <iframe :srcdoc=\"'<button>Trigger</button>'\" frameborder=\"0\" width=\"300\" height=\"300\"></iframe>\n        </div>\n      `)\n\n      // Open menu\n      await click(getMenuButton())\n      assertMenu({ state: MenuState.Visible })\n\n      // Click the input element in the iframe\n      await click(document.querySelector('iframe')?.contentDocument!.querySelector('button')!)\n\n      // Should be closed now\n      assertMenu({ state: MenuState.InvisibleUnmounted })\n\n      // Verify the button is focused again\n      assertActiveElement(getMenuButton())\n    })\n  )\n\n  it('should be possible to hover an item and make it active', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n    // We should be able to go to the second item\n    await mouseMove(items[1])\n    assertMenuLinkedWithMenuItem(items[1])\n\n    // We should be able to go to the first item\n    await mouseMove(items[0])\n    assertMenuLinkedWithMenuItem(items[0])\n\n    // We should be able to go to the last item\n    await mouseMove(items[2])\n    assertMenuLinkedWithMenuItem(items[2])\n  })\n\n  it('should make a menu item active when you move the mouse over it', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n    // We should be able to go to the second item\n    await mouseMove(items[1])\n    assertMenuLinkedWithMenuItem(items[1])\n  })\n\n  it('should be a no-op when we move the mouse and the menu item is already active', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n\n    // We should be able to go to the second item\n    await mouseMove(items[1])\n    assertMenuLinkedWithMenuItem(items[1])\n\n    await mouseMove(items[1])\n\n    // Nothing should be changed\n    assertMenuLinkedWithMenuItem(items[1])\n  })\n\n  it('should be a no-op when we move the mouse and the menu item is disabled', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\" disabled>bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n\n    await mouseMove(items[1])\n    assertNoActiveMenuItem()\n  })\n\n  it('should not be possible to hover an item that is disabled', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\" disabled>bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n\n    // Try to hover over item 1, which is disabled\n    await mouseMove(items[1])\n\n    // We should not have an active item now\n    assertNoActiveMenuItem()\n  })\n\n  it('should be possible to mouse leave an item and make it inactive', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n\n    // We should be able to go to the second item\n    await mouseMove(items[1])\n    assertMenuLinkedWithMenuItem(items[1])\n\n    await mouseLeave(items[1])\n    assertNoActiveMenuItem()\n\n    // We should be able to go to the first item\n    await mouseMove(items[0])\n    assertMenuLinkedWithMenuItem(items[0])\n\n    await mouseLeave(items[0])\n    assertNoActiveMenuItem()\n\n    // We should be able to go to the last item\n    await mouseMove(items[2])\n    assertMenuLinkedWithMenuItem(items[2])\n\n    await mouseLeave(items[2])\n    assertNoActiveMenuItem()\n  })\n\n  it('should be possible to mouse leave a disabled item and be a no-op', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\" disabled>bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n\n    let items = getMenuItems()\n\n    // Try to hover over item 1, which is disabled\n    await mouseMove(items[1])\n    assertNoActiveMenuItem()\n\n    await mouseLeave(items[1])\n    assertNoActiveMenuItem()\n  })\n\n  it('should be possible to click a menu item, which closes the menu', async () => {\n    let clickHandler = jest.fn()\n    renderTemplate({\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"a\" @click=\"clickHandler\">bob</MenuItem>\n            <MenuItem as=\"a\">charlie</MenuItem>\n          </MenuItems>\n        </Menu>\n      `,\n      setup: () => ({ clickHandler }),\n    })\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // We should be able to click the first item\n    await click(items[1])\n\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n    expect(clickHandler).toHaveBeenCalled()\n  })\n\n  it('should be possible to click a menu item, which closes the menu and invokes the @click handler', async () => {\n    let clickHandler = jest.fn()\n    renderTemplate({\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"a\">alice</MenuItem>\n            <MenuItem as=\"button\" @click=\"clickHandler\">bob</MenuItem>\n            <MenuItem>\n              <button @click=\"clickHandler\">charlie</button>\n            </MenuItem>\n          </MenuItems>\n        </Menu>\n      `,\n      setup: () => ({ clickHandler }),\n    })\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    // We should be able to click the first item\n    await click(getMenuItems()[1])\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Verify the callback has been called\n    expect(clickHandler).toHaveBeenCalledTimes(1)\n\n    // Let's re-open the window for now\n    await click(getMenuButton())\n\n    // Click the last item, which should close and invoke the handler\n    await click(getMenuItems()[2])\n    assertMenu({ state: MenuState.InvisibleUnmounted })\n\n    // Verify the callback has been called\n    expect(clickHandler).toHaveBeenCalledTimes(2)\n  })\n\n  it('should be possible to click a disabled menu item, which is a no-op', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\" disabled>bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // We should be able to click the first item\n    await click(items[1])\n    assertMenu({ state: MenuState.Visible })\n  })\n\n  it('should be possible focus a menu item, so that it becomes active', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\">bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // Verify that nothing is active yet\n    assertNoActiveMenuItem()\n\n    // We should be able to focus the first item\n    await focus(items[1])\n    assertMenuLinkedWithMenuItem(items[1])\n  })\n\n  it('should not be possible to focus a menu item which is disabled', async () => {\n    renderTemplate(jsx`\n      <Menu>\n        <MenuButton>Trigger</MenuButton>\n        <MenuItems>\n          <MenuItem as=\"a\">alice</MenuItem>\n          <MenuItem as=\"a\" disabled>bob</MenuItem>\n          <MenuItem as=\"a\">charlie</MenuItem>\n        </MenuItems>\n      </Menu>\n    `)\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    // We should not be able to focus the first item\n    await focus(items[1])\n    assertNoActiveMenuItem()\n  })\n\n  it('should not be possible to activate a disabled item', async () => {\n    let clickHandler = jest.fn()\n\n    renderTemplate({\n      template: jsx`\n        <Menu>\n          <MenuButton>Trigger</MenuButton>\n          <MenuItems>\n            <MenuItem as=\"button\" @click=\"clickHandler\">alice</MenuItem>\n            <MenuItem as=\"button\" @click=\"clickHandler\" disabled>\n              bob\n            </MenuItem>\n            <MenuItem disabled>\n              <button @click=\"clickHandler\">charlie</button>\n            </MenuItem>\n          </MenuItems>\n        </Menu>\n      `,\n      setup: () => ({ clickHandler }),\n    })\n\n    // Open menu\n    await click(getMenuButton())\n    assertMenu({ state: MenuState.Visible })\n\n    let items = getMenuItems()\n\n    await focus(items[0])\n    await click(items[1])\n    expect(clickHandler).not.toHaveBeenCalled()\n\n    // Activate the last item\n    await click(getMenuItems()[2])\n    expect(clickHandler).not.toHaveBeenCalled()\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/menu/menu.ts",
    "content": "import {\n  computed,\n  defineComponent,\n  inject,\n  nextTick,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  watchEffect,\n  type ComputedRef,\n  type InjectionKey,\n  type Ref,\n  type UnwrapNestedRefs,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useTextValue } from '../../hooks/use-text-value'\nimport { useTrackedPointer } from '../../hooks/use-tracked-pointer'\nimport { useTreeWalker } from '../../hooks/use-tree-walker'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport { Keys } from '../../keyboard'\nimport { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'\nimport { dom } from '../../utils/dom'\nimport {\n  Focus as FocusManagementFocus,\n  FocusableMode,\n  focusFrom,\n  isFocusableElement,\n  restoreFocusIfNecessary,\n  sortByDomNode,\n} from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { Features, render } from '../../utils/render'\n\nenum MenuStates {\n  Open,\n  Closed,\n}\n\nenum ActivationTrigger {\n  Pointer,\n  Other,\n}\n\nfunction nextFrame(cb: () => void) {\n  requestAnimationFrame(() => requestAnimationFrame(cb))\n}\n\ntype MenuItemData = {\n  textValue: string\n  disabled: boolean\n  domRef: Ref<HTMLElement | null>\n}\ntype StateDefinition = {\n  // State\n  menuState: Ref<MenuStates>\n  buttonRef: Ref<HTMLButtonElement | null>\n  itemsRef: Ref<HTMLDivElement | null>\n  items: Ref<{ id: string; dataRef: ComputedRef<MenuItemData> }[]>\n  searchQuery: Ref<string>\n  activeItemIndex: Ref<number | null>\n  activationTrigger: Ref<ActivationTrigger>\n\n  // State mutators\n  closeMenu(): void\n  openMenu(): void\n  goToItem(focus: Focus, id?: string, trigger?: ActivationTrigger): void\n  search(value: string): void\n  clearSearch(): void\n  registerItem(id: string, dataRef: ComputedRef<MenuItemData>): void\n  unregisterItem(id: string): void\n}\n\nlet MenuContext = Symbol('MenuContext') as InjectionKey<StateDefinition>\n\nfunction useMenuContext(component: string) {\n  let context = inject(MenuContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <Menu /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext)\n    throw err\n  }\n\n  return context\n}\n\nexport let Menu = defineComponent({\n  name: 'Menu',\n  props: { as: { type: [Object, String], default: 'template' } },\n  setup(props, { slots, attrs }) {\n    let menuState = ref<StateDefinition['menuState']['value']>(MenuStates.Closed)\n    let buttonRef = ref<StateDefinition['buttonRef']['value']>(null)\n    let itemsRef = ref<StateDefinition['itemsRef']['value']>(null)\n    let items = ref<StateDefinition['items']['value']>([])\n    let searchQuery = ref<StateDefinition['searchQuery']['value']>('')\n    let activeItemIndex = ref<StateDefinition['activeItemIndex']['value']>(null)\n    let activationTrigger = ref<StateDefinition['activationTrigger']['value']>(\n      ActivationTrigger.Other\n    )\n\n    function adjustOrderedState(\n      adjustment: (\n        items: UnwrapNestedRefs<StateDefinition['items']['value']>\n      ) => UnwrapNestedRefs<StateDefinition['items']['value']> = (i) => i\n    ) {\n      let currentActiveItem =\n        activeItemIndex.value !== null ? items.value[activeItemIndex.value] : null\n\n      let sortedItems = sortByDomNode(adjustment(items.value.slice()), (item) =>\n        dom(item.dataRef.domRef)\n      )\n\n      // If we inserted an item before the current active item then the active item index\n      // would be wrong. To fix this, we will re-lookup the correct index.\n      let adjustedActiveItemIndex = currentActiveItem\n        ? sortedItems.indexOf(currentActiveItem)\n        : null\n\n      // Reset to `null` in case the currentActiveItem was removed.\n      if (adjustedActiveItemIndex === -1) {\n        adjustedActiveItemIndex = null\n      }\n\n      return {\n        items: sortedItems,\n        activeItemIndex: adjustedActiveItemIndex,\n      }\n    }\n\n    let api = {\n      menuState,\n      buttonRef,\n      itemsRef,\n      items,\n      searchQuery,\n      activeItemIndex,\n      activationTrigger,\n      closeMenu: () => {\n        menuState.value = MenuStates.Closed\n        activeItemIndex.value = null\n      },\n      openMenu: () => (menuState.value = MenuStates.Open),\n      goToItem(focus: Focus, id?: string, trigger?: ActivationTrigger) {\n        let adjustedState = adjustOrderedState()\n        let nextActiveItemIndex = calculateActiveIndex(\n          focus === Focus.Specific\n            ? { focus: Focus.Specific, id: id! }\n            : { focus: focus as Exclude<Focus, Focus.Specific> },\n          {\n            resolveItems: () => adjustedState.items,\n            resolveActiveIndex: () => adjustedState.activeItemIndex,\n            resolveId: (item) => item.id,\n            resolveDisabled: (item) => item.dataRef.disabled,\n          }\n        )\n\n        searchQuery.value = ''\n        activeItemIndex.value = nextActiveItemIndex\n        activationTrigger.value = trigger ?? ActivationTrigger.Other\n        items.value = adjustedState.items\n      },\n      search(value: string) {\n        let wasAlreadySearching = searchQuery.value !== ''\n        let offset = wasAlreadySearching ? 0 : 1\n        searchQuery.value += value.toLowerCase()\n\n        let reOrderedItems =\n          activeItemIndex.value !== null\n            ? items.value\n                .slice(activeItemIndex.value + offset)\n                .concat(items.value.slice(0, activeItemIndex.value + offset))\n            : items.value\n\n        let matchingItem = reOrderedItems.find(\n          (item) => item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled\n        )\n\n        let matchIdx = matchingItem ? items.value.indexOf(matchingItem) : -1\n        if (matchIdx === -1 || matchIdx === activeItemIndex.value) return\n\n        activeItemIndex.value = matchIdx\n        activationTrigger.value = ActivationTrigger.Other\n      },\n      clearSearch() {\n        searchQuery.value = ''\n      },\n      registerItem(id: string, dataRef: MenuItemData) {\n        let adjustedState = adjustOrderedState((items) => {\n          return [...items, { id, dataRef }]\n        })\n\n        items.value = adjustedState.items\n        activeItemIndex.value = adjustedState.activeItemIndex\n        activationTrigger.value = ActivationTrigger.Other\n      },\n      unregisterItem(id: string) {\n        let adjustedState = adjustOrderedState((items) => {\n          let idx = items.findIndex((a) => a.id === id)\n          if (idx !== -1) items.splice(idx, 1)\n          return items\n        })\n\n        items.value = adjustedState.items\n        activeItemIndex.value = adjustedState.activeItemIndex\n        activationTrigger.value = ActivationTrigger.Other\n      },\n    }\n\n    // Handle outside click\n    useOutsideClick(\n      [buttonRef, itemsRef],\n      (event, target) => {\n        api.closeMenu()\n\n        if (!isFocusableElement(target, FocusableMode.Loose)) {\n          event.preventDefault()\n          dom(buttonRef)?.focus()\n        }\n      },\n      computed(() => menuState.value === MenuStates.Open)\n    )\n\n    // @ts-expect-error Types of property 'dataRef' are incompatible.\n    provide(MenuContext, api)\n\n    useOpenClosedProvider(\n      computed(() =>\n        match(menuState.value, {\n          [MenuStates.Open]: State.Open,\n          [MenuStates.Closed]: State.Closed,\n        })\n      )\n    )\n\n    return () => {\n      let slot = { open: menuState.value === MenuStates.Open, close: api.closeMenu }\n      return render({ ourProps: {}, theirProps: props, slot, slots, attrs, name: 'Menu' })\n    }\n  },\n})\n\nexport let MenuButton = defineComponent({\n  name: 'MenuButton',\n  props: {\n    disabled: { type: Boolean, default: false },\n    as: { type: [Object, String], default: 'button' },\n    id: { type: String, default: () => `headlessui-menu-button-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useMenuContext('MenuButton')\n\n    expose({ el: api.buttonRef, $el: api.buttonRef })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/#keyboard-interaction-13\n\n        case Keys.Space:\n        case Keys.Enter:\n        case Keys.ArrowDown:\n          event.preventDefault()\n          event.stopPropagation()\n          api.openMenu()\n          nextTick(() => {\n            dom(api.itemsRef)?.focus({ preventScroll: true })\n            api.goToItem(Focus.First)\n          })\n          break\n\n        case Keys.ArrowUp:\n          event.preventDefault()\n          event.stopPropagation()\n          api.openMenu()\n          nextTick(() => {\n            dom(api.itemsRef)?.focus({ preventScroll: true })\n            api.goToItem(Focus.Last)\n          })\n          break\n      }\n    }\n\n    function handleKeyUp(event: KeyboardEvent) {\n      switch (event.key) {\n        case Keys.Space:\n          // Required for firefox, event.preventDefault() in handleKeyDown for\n          // the Space key doesn't cancel the handleKeyUp, which in turn\n          // triggers a *click*.\n          event.preventDefault()\n          break\n      }\n    }\n\n    function handleClick(event: MouseEvent) {\n      if (props.disabled) return\n      if (api.menuState.value === MenuStates.Open) {\n        api.closeMenu()\n        nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n      } else {\n        event.preventDefault()\n        api.openMenu()\n        nextFrame(() => dom(api.itemsRef)?.focus({ preventScroll: true }))\n      }\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      api.buttonRef\n    )\n\n    return () => {\n      let slot = { open: api.menuState.value === MenuStates.Open }\n\n      let { id, ...theirProps } = props\n      let ourProps = {\n        ref: api.buttonRef,\n        id,\n        type: type.value,\n        'aria-haspopup': 'menu',\n        'aria-controls': dom(api.itemsRef)?.id,\n        'aria-expanded': api.menuState.value === MenuStates.Open,\n        onKeydown: handleKeyDown,\n        onKeyup: handleKeyUp,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'MenuButton',\n      })\n    }\n  },\n})\n\nexport let MenuItems = defineComponent({\n  name: 'MenuItems',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    id: { type: String, default: () => `headlessui-menu-items-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useMenuContext('MenuItems')\n    let searchDebounce = ref<ReturnType<typeof setTimeout> | null>(null)\n\n    expose({ el: api.itemsRef, $el: api.itemsRef })\n\n    useTreeWalker({\n      container: computed(() => dom(api.itemsRef)),\n      enabled: computed(() => api.menuState.value === MenuStates.Open),\n      accept(node) {\n        if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT\n        if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP\n        return NodeFilter.FILTER_ACCEPT\n      },\n      walk(node) {\n        node.setAttribute('role', 'none')\n      },\n    })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      if (searchDebounce.value) clearTimeout(searchDebounce.value)\n\n      switch (event.key) {\n        // Ref: https://www.w3.org/WAI/ARIA/apg/patterns/menu/#keyboard-interaction-12\n\n        // @ts-expect-error Fallthrough is expected here\n        case Keys.Space:\n          if (api.searchQuery.value !== '') {\n            event.preventDefault()\n            event.stopPropagation()\n            return api.search(event.key)\n          }\n        // When in type ahead mode, fallthrough\n        case Keys.Enter:\n          event.preventDefault()\n          event.stopPropagation()\n          if (api.activeItemIndex.value !== null) {\n            let activeItem = api.items.value[api.activeItemIndex.value]\n            let _activeItem = activeItem as unknown as UnwrapNestedRefs<typeof activeItem>\n            dom(_activeItem.dataRef.domRef)?.click()\n          }\n          api.closeMenu()\n          restoreFocusIfNecessary(dom(api.buttonRef))\n          break\n\n        case Keys.ArrowDown:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToItem(Focus.Next)\n\n        case Keys.ArrowUp:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToItem(Focus.Previous)\n\n        case Keys.Home:\n        case Keys.PageUp:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToItem(Focus.First)\n\n        case Keys.End:\n        case Keys.PageDown:\n          event.preventDefault()\n          event.stopPropagation()\n          return api.goToItem(Focus.Last)\n\n        case Keys.Escape:\n          event.preventDefault()\n          event.stopPropagation()\n          api.closeMenu()\n          nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))\n          break\n\n        case Keys.Tab:\n          event.preventDefault()\n          event.stopPropagation()\n          api.closeMenu()\n          nextTick(() =>\n            focusFrom(\n              dom(api.buttonRef),\n              event.shiftKey ? FocusManagementFocus.Previous : FocusManagementFocus.Next\n            )\n          )\n          break\n\n        default:\n          if (event.key.length === 1) {\n            api.search(event.key)\n            searchDebounce.value = setTimeout(() => api.clearSearch(), 350)\n          }\n          break\n      }\n    }\n\n    function handleKeyUp(event: KeyboardEvent) {\n      switch (event.key) {\n        case Keys.Space:\n          // Required for firefox, event.preventDefault() in handleKeyDown for\n          // the Space key doesn't cancel the handleKeyUp, which in turn\n          // triggers a *click*.\n          event.preventDefault()\n          break\n      }\n    }\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.menuState.value === MenuStates.Open\n    })\n\n    return () => {\n      let slot = { open: api.menuState.value === MenuStates.Open }\n      let { id, ...theirProps } = props\n      let ourProps = {\n        'aria-activedescendant':\n          api.activeItemIndex.value === null\n            ? undefined\n            : api.items.value[api.activeItemIndex.value]?.id,\n        'aria-labelledby': dom(api.buttonRef)?.id,\n        id,\n        onKeydown: handleKeyDown,\n        onKeyup: handleKeyUp,\n        role: 'menu',\n        tabIndex: 0,\n        ref: api.itemsRef,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'MenuItems',\n      })\n    }\n  },\n})\n\nexport let MenuItem = defineComponent({\n  name: 'MenuItem',\n  inheritAttrs: false,\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    disabled: { type: Boolean, default: false },\n    id: { type: String, default: () => `headlessui-menu-item-${useId()}` },\n  },\n  setup(props, { slots, attrs, expose }) {\n    let api = useMenuContext('MenuItem')\n    let internalItemRef = ref<HTMLElement | null>(null)\n\n    expose({ el: internalItemRef, $el: internalItemRef })\n\n    let active = computed(() => {\n      return api.activeItemIndex.value !== null\n        ? api.items.value[api.activeItemIndex.value].id === props.id\n        : false\n    })\n\n    let getTextValue = useTextValue(internalItemRef)\n    let dataRef = computed<MenuItemData>(() => ({\n      disabled: props.disabled,\n      get textValue() {\n        return getTextValue()\n      },\n      domRef: internalItemRef,\n    }))\n\n    onMounted(() => api.registerItem(props.id, dataRef))\n    onUnmounted(() => api.unregisterItem(props.id))\n\n    watchEffect(() => {\n      if (api.menuState.value !== MenuStates.Open) return\n      if (!active.value) return\n      if (api.activationTrigger.value === ActivationTrigger.Pointer) return\n      nextTick(() => dom(internalItemRef)?.scrollIntoView?.({ block: 'nearest' }))\n    })\n\n    function handleClick(event: MouseEvent) {\n      if (props.disabled) return event.preventDefault()\n      api.closeMenu()\n      restoreFocusIfNecessary(dom(api.buttonRef))\n    }\n\n    function handleFocus() {\n      if (props.disabled) return api.goToItem(Focus.Nothing)\n      api.goToItem(Focus.Specific, props.id)\n    }\n\n    let pointer = useTrackedPointer()\n\n    function handleEnter(evt: PointerEvent) {\n      pointer.update(evt)\n    }\n\n    function handleMove(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled) return\n      if (active.value) return\n      api.goToItem(Focus.Specific, props.id, ActivationTrigger.Pointer)\n    }\n\n    function handleLeave(evt: PointerEvent) {\n      if (!pointer.wasMoved(evt)) return\n      if (props.disabled) return\n      if (!active.value) return\n      api.goToItem(Focus.Nothing)\n    }\n\n    return () => {\n      let { id, disabled, ...theirProps } = props\n      let slot = { active: active.value, disabled, close: api.closeMenu }\n      let ourProps = {\n        id,\n        ref: internalItemRef,\n        role: 'menuitem',\n        tabIndex: disabled === true ? undefined : -1,\n        'aria-disabled': disabled === true ? true : undefined,\n        onClick: handleClick,\n        onFocus: handleFocus,\n        onPointerenter: handleEnter,\n        onMouseenter: handleEnter,\n        onPointermove: handleMove,\n        onMousemove: handleMove,\n        onPointerleave: handleLeave,\n        onMouseleave: handleLeave,\n      }\n\n      return render({\n        ourProps,\n        theirProps: { ...attrs, ...theirProps },\n        slot,\n        attrs,\n        slots,\n        name: 'MenuItem',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/popover/popover.test.ts",
    "content": "import { defineComponent, h, nextTick, onMounted, ref, watch } from 'vue'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\n\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport {\n  PopoverState,\n  assertActiveElement,\n  assertContainsActiveElement,\n  assertPopoverButton,\n  assertPopoverPanel,\n  getByText,\n  getPopoverButton,\n  getPopoverOverlay,\n  getPopoverPanel,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, MouseButton, click, focus, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { Portal } from '../portal/portal'\nimport { Popover, PopoverButton, PopoverGroup, PopoverOverlay, PopoverPanel } from './popover'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({\n  Popover,\n  PopoverGroup,\n  PopoverButton,\n  PopoverPanel,\n  PopoverOverlay,\n  Portal,\n})\n\ndescribe('Safe guards', () => {\n  it.each([\n    ['PopoverButton', PopoverButton],\n    ['PopoverPanel', PopoverPanel],\n    ['PopoverOverlay', PopoverOverlay],\n  ])(\n    'should error when we are using a <%s /> without a parent <Popover />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <Popover /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a Popover without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n})\n\ndescribe('Rendering', () => {\n  describe('PopoverGroup', () => {\n    it(\n      'should be possible to render a PopoverGroup with multiple Popover components',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton data-trigger=\"1\">Trigger 1</PopoverButton>\n              <PopoverPanel data-panel=\"1\">Panel 1</PopoverPanel>\n            </Popover>\n            <Popover>\n              <PopoverButton data-trigger=\"2\">Trigger 2</PopoverButton>\n              <PopoverPanel data-panel=\"2\">Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        function getTrigger(number: number) {\n          return document.querySelector(`[data-trigger=\"${number}\"]`)! as HTMLElement\n        }\n\n        function getPanel(number: number) {\n          return document.querySelector(`[data-panel=\"${number}\"]`)! as HTMLElement\n        }\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(2))\n\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getPanel(1))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getPanel(2))\n\n        await click(getTrigger(1))\n\n        assertPopoverButton({ state: PopoverState.Visible }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(2))\n\n        assertPopoverPanel({ state: PopoverState.Visible }, getPanel(1))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getPanel(2))\n\n        await click(getTrigger(2))\n\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.Visible }, getTrigger(2))\n\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getPanel(1))\n        assertPopoverPanel({ state: PopoverState.Visible }, getPanel(2))\n      })\n    )\n  })\n\n  describe('Popover', () => {\n    it(\n      'should be possible to render a Popover using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover v-slot=\"{ open }\">\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Panel is: {{open ? 'open' : 'closed'}}</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.Visible, textContent: 'Panel is: open' })\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover v-slot=\"{ close }\">\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>\n              <button @click=\"close()\">Close me</button>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the Popover.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button id=\"test\">restorable</button>\n            <Popover v-slot=\"{ close }\">\n              <PopoverButton>Trigger</PopoverButton>\n              <PopoverPanel>\n                <button @click=\"close(document.getElementById('test'))\">Close me</button>\n              </PopoverPanel>\n            </Popover>\n          `,\n          setup: () => ({ document }),\n        })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a ref',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button ref=\"elementRef\">restorable</button>\n            <Popover v-slot=\"{ close }\">\n              <PopoverButton>Trigger</PopoverButton>\n              <PopoverPanel> <button @click=\"close(elementRef)\">Close me</button>} </PopoverPanel>\n            </Popover>\n          `,\n          setup: () => ({ elementRef: ref() }),\n        })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n  })\n\n  describe('PopoverButton', () => {\n    it(\n      'should be possible to render a PopoverButton using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton v-slot=\"slot\">{{JSON.stringify(slot)}}</PopoverButton>\n            <PopoverPanel></PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n          textContent: JSON.stringify({ open: false }),\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-button-1' },\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n\n    it(\n      'should be possible to render a PopoverButton using a render prop and an `as` prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton as=\"div\" role=\"button\" v-slot=\"slot\">\n              {{JSON.stringify(slot)}}\n            </PopoverButton>\n            <PopoverPanel />\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n          textContent: JSON.stringify({ open: false }),\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-button-1' },\n          textContent: JSON.stringify({ open: true }),\n        })\n        assertPopoverPanel({ state: PopoverState.Visible })\n      })\n    )\n\n    describe('`type` attribute', () => {\n      it('should set the `type` to \"button\" by default', async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n          </Popover>\n        `)\n\n        expect(getPopoverButton()).toHaveAttribute('type', 'button')\n      })\n\n      it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton type=\"submit\"> Trigger </PopoverButton>\n          </Popover>\n        `)\n\n        expect(getPopoverButton()).toHaveAttribute('type', 'submit')\n      })\n\n      it(\n        'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Popover>\n                <PopoverButton :as=\"CustomButton\"> Trigger </PopoverButton>\n              </Popover>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('button', { ...props }),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getPopoverButton()).toHaveAttribute('type', 'button')\n        })\n      )\n\n      it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton as=\"div\"> Trigger </PopoverButton>\n          </Popover>\n        `)\n\n        expect(getPopoverButton()).not.toHaveAttribute('type')\n      })\n\n      it(\n        'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n        suppressConsoleLogs(async () => {\n          renderTemplate({\n            template: html`\n              <Popover>\n                <PopoverButton :as=\"CustomButton\"> Trigger </PopoverButton>\n              </Popover>\n            `,\n            setup: () => ({\n              CustomButton: defineComponent({\n                setup: (props) => () => h('div', props),\n              }),\n            }),\n          })\n\n          await new Promise(requestAnimationFrame)\n\n          expect(getPopoverButton()).not.toHaveAttribute('type')\n        })\n      )\n    })\n  })\n\n  describe('PopoverPanel', () => {\n    it(\n      'should be possible to render PopoverPanel using a render prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel v-slot=\"slot\">{{JSON.stringify(slot)}}</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        await click(getPopoverButton())\n\n        assertPopoverButton({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          textContent: JSON.stringify({ open: true }),\n        })\n      })\n    )\n\n    it('should be possible to always render the PopoverPanel if we provide it a `static` prop', () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel static>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      // Let's verify that the Popover is already there\n      expect(getPopoverPanel()).not.toBe(null)\n    })\n\n    it('should be possible to use a different render strategy for the PopoverPanel', async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel :unmount=\"false\">Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      getPopoverButton()?.focus()\n\n      // TODO: Can we improve this?\n      await new Promise<void>(nextTick)\n\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n      assertPopoverPanel({ state: PopoverState.InvisibleHidden })\n\n      // Let's open the Popover, to see if it is not hidden anymore\n      await click(getPopoverButton())\n\n      assertPopoverButton({ state: PopoverState.Visible })\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Let's re-click the Popover, to see if it is hidden again\n      await click(getPopoverButton())\n\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n      assertPopoverPanel({ state: PopoverState.InvisibleHidden })\n    })\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (very first link)',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel focus>\n              <a href=\"/\">Link 1</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n      })\n    )\n\n    it(\n      'should close the Popover, when PopoverPanel has the focus prop and you focus the open button',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel focus>\n              <a href=\"/\">Link 1</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n\n        // Focus the button again\n        getPopoverButton()?.focus()\n        await new Promise<void>(nextTick)\n\n        // Ensure the Popover is closed again\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (skip hidden link)',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel focus>\n              <a href=\"/\" style=\"display:none\"> Link 1 </a>\n              <a href=\"/\">Link 2</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n\n    it(\n      'should be possible to move the focus inside the panel to the first focusable element (very first link) when the hidden render strategy is used',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel focus :unmount=\"false\">\n              <a href=\"/\">Link 1</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the active element is within the Panel\n        assertContainsActiveElement(getPopoverPanel())\n        assertActiveElement(getByText('Link 1'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel v-slot=\"{ close }\">\n              <button @click=\"close()\">Close me</button>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the Popover.Button got the restored focus\n        assertActiveElement(getByText('Trigger'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a specific element',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button id=\"test\">restorable</button>\n            <Popover>\n              <PopoverButton>Trigger</PopoverButton>\n              <PopoverPanel v-slot=\"{ close }\">\n                <button @click=\"close(document.getElementById('test'))\">Close me</button>\n              </PopoverPanel>\n            </Popover>\n          `,\n          setup: () => ({ document }),\n        })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n\n    it(\n      'should expose a close function that closes the popover and restores to a ref',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <button ref=\"elementRef\">restorable</button>\n            <Popover>\n              <PopoverButton>Trigger</PopoverButton>\n              <PopoverPanel v-slot=\"{ close }\">\n                <button @click=\"close(elementRef)\">Close me</button>}\n              </PopoverPanel>\n            </Popover>\n          `,\n          setup: () => ({ elementRef: ref() }),\n        })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Ensure the button is focused\n        assertActiveElement(getPopoverButton())\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure we can click the close button\n        await click(getByText('Close me'))\n\n        // Ensure the popover is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Ensure the restorable button got the restored focus\n        assertActiveElement(getByText('restorable'))\n      })\n    )\n  })\n})\n\ndescribe('Composition', () => {\n  let OpenClosedWrite = defineComponent({\n    props: { open: { type: Boolean } },\n    setup(props, { slots }) {\n      useOpenClosedProvider(ref(props.open ? State.Open : State.Closed))\n      return () => slots.default?.()\n    },\n  })\n\n  let OpenClosedRead = defineComponent({\n    emits: ['read'],\n    setup(_, { slots, emit }) {\n      let state = useOpenClosed()\n      watch([state], ([value]) => emit('read', value))\n      return () => slots.default?.()\n    },\n  })\n\n  it(\n    'should always open the PopoverPanel because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <OpenClosedWrite :open=\"true\">\n              <PopoverPanel v-slot=\"data\"> {{JSON.stringify(data)}} </PopoverPanel>\n            </OpenClosedWrite>\n          </Popover>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Popover is visible\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Let's try and open the Popover\n      await click(getPopoverButton())\n\n      // Verify the Popover is still visible\n      assertPopoverPanel({ state: PopoverState.Visible })\n    })\n  )\n\n  it(\n    'should always close the PopoverPanel because of a wrapping OpenClosed component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        components: { OpenClosedWrite },\n        template: html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <OpenClosedWrite :open=\"false\">\n              <PopoverPanel v-slot=\"data\"> {{JSON.stringify(data)}} </PopoverPanel>\n            </OpenClosedWrite>\n          </Popover>\n        `,\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Popover is hidden\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Let's try and open the Popover\n      await click(getPopoverButton())\n\n      // Verify the Popover is still hidden\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to read the OpenClosed state',\n    suppressConsoleLogs(async () => {\n      let readFn = jest.fn()\n      renderTemplate({\n        components: { OpenClosedRead },\n        template: html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <OpenClosedRead @read=\"readFn\">\n              <PopoverPanel></PopoverPanel>\n            </OpenClosedRead>\n          </Popover>\n        `,\n        setup() {\n          return { readFn }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Verify the Popover is hidden\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Let's toggle the Popover 3 times\n      await click(getPopoverButton())\n      await click(getPopoverButton())\n      await click(getPopoverButton())\n\n      // Verify the Popover is visible\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      expect(readFn).toHaveBeenCalledTimes(3)\n      expect(readFn).toHaveBeenNthCalledWith(1, State.Open)\n      expect(readFn).toHaveBeenNthCalledWith(2, State.Closed)\n      expect(readFn).toHaveBeenNthCalledWith(3, State.Open)\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Enter` key', () => {\n    it(\n      'should be possible to open the Popover with Enter',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Open popover\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-panel-3' },\n        })\n\n        // Close popover\n        await press(Keys.Enter)\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should not be possible to open the popover with Enter when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton disabled>Trigger</PopoverButton>\n            <PopoverPanel>Content</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Try to open the popover\n        await press(Keys.Enter)\n\n        // Verify it is still closed\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the popover with Enter when the popover is open',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Open popover\n        await press(Keys.Enter)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-panel-3' },\n        })\n\n        // Close popover\n        await press(Keys.Enter)\n\n        // Verify it is closed again\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close other popover menus when we open a new one',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>Panel 1</PopoverPanel>\n            </Popover>\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Open the first Popover\n        await click(getByText('Trigger 1'))\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Focus trigger 2\n        getByText('Trigger 2')?.focus()\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Open the second popover\n        await press(Keys.Enter)\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Enter` on a PopoverButton inside a PopoverPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Open</PopoverButton>\n            <PopoverPanel>\n              <PopoverButton>Close</PopoverButton>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeBtn = getByText('Close')\n\n        expect(closeBtn).not.toHaveAttribute('id')\n        expect(closeBtn).not.toHaveAttribute('aria-controls')\n        expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Enter, closeBtn)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n      })\n    )\n  })\n\n  describe('`Escape` key', () => {\n    it(\n      'should close the Popover menu, when pressing escape on the PopoverButton',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify button is (still) focused\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should close the Popover menu, when pressing escape on the PopoverPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>\n              <a href=\"/\">Link</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Tab to next focusable item\n        await press(Keys.Tab)\n\n        // Verify the active element is inside the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify button is focused again\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should be possible to close a sibling Popover when pressing escape on a sibling PopoverButton',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton data-trigger=\"1\">Trigger 1</PopoverButton>\n              <PopoverPanel data-panel=\"1\">Panel 1</PopoverPanel>\n            </Popover>\n\n            <Popover>\n              <PopoverButton data-trigger=\"2\">Trigger 2</PopoverButton>\n              <PopoverPanel data-panel=\"2\">Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        function getTrigger(number: number) {\n          return document.querySelector(`[data-trigger=\"${number}\"]`)! as HTMLElement\n        }\n\n        function getPanel(number: number) {\n          return document.querySelector(`[data-panel=\"${number}\"]`)! as HTMLElement\n        }\n\n        // Focus the button of the first Popover\n        getTrigger(1)?.focus()\n\n        // Verify popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(2))\n\n        // Open popover\n        await click(getTrigger(1))\n\n        // Verify popover is open\n        assertPopoverButton({ state: PopoverState.Visible }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(2))\n\n        assertPopoverPanel({ state: PopoverState.Visible }, getPanel(1))\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted }, getPanel(2))\n\n        // Focus the button of the second popover menu\n        getTrigger(2)?.focus()\n\n        // Close popover\n        await press(Keys.Escape)\n\n        // Verify both popovers are closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(1))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getTrigger(2))\n\n        // Verify the button of the second popover is still focused\n        assertActiveElement(getTrigger(2))\n      })\n    )\n  })\n\n  describe('`Tab` key', () => {\n    it(\n      'should be possible to Tab through the panel contents onto the next PopoverButton',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab again\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should be possible to place a focusable item in the PopoverGroup, and keep the Popover open when we focus the focusable element',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n\n            <a href=\"/\">Link in between</a>\n\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab to the in between link\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the in between link is focused\n        assertActiveElement(getByText('Link in between'))\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the PopoverGroup',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <PopoverGroup>\n              <Popover>\n                <PopoverButton>Trigger 1</PopoverButton>\n                <PopoverPanel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Popover>\n\n              <Popover>\n                <PopoverButton>Trigger 2</PopoverButton>\n                <PopoverPanel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </PopoverPanel>\n              </Popover>\n            </PopoverGroup>\n\n            <a href=\"/\">Next</a>\n          </div>\n        `)\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab again\n        await press(Keys.Tab)\n\n        // Verify that the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Verify that the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Let's Tab out of the PopoverGroup\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n        await new Promise<void>(nextTick)\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the Popover',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n\n            <a href=\"/\">Next</a>\n          </div>\n        `)\n\n        // Focus the button of the first Popover\n        getByText('Trigger 1')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 1'))\n\n        // Verify we are focused on the first link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 1'))\n\n        // Verify we are focused on the second link\n        await press(Keys.Tab)\n        assertActiveElement(getByText('Link 2'))\n\n        // Let's Tab out of the Popover\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of a Popover without focusable elements',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>No focusable elements here</PopoverPanel>\n            </Popover>\n\n            <a href=\"/\">Next</a>\n          </div>\n        `)\n\n        // Focus the button of the Popover\n        await focus(getPopoverButton())\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Let's Tab out of the Popover\n        await press(Keys.Tab)\n\n        // Verify the next link is now focused\n        assertActiveElement(getByText('Next'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover when the PopoverPanel has a focus prop',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <PopoverButton>Trigger</PopoverButton>\n              <PopoverPanel focus>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n            <a href=\"/\">Next</a>\n          </div>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // Tab out of the component\n        await press(Keys.Tab) // Tab to link 1\n        await press(Keys.Tab) // Tab out\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Next link outside of the popover\n        assertActiveElement(getByText('Next'))\n      })\n    )\n\n    it(\n      'should close the Popover when the PopoverPanel has a focus prop (PopoverPanel uses a Portal)',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <PopoverButton>Trigger</PopoverButton>\n              <Portal>\n                <PopoverPanel focus>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Portal>\n            </Popover>\n            <a href=\"/\">Next</a>\n          </div>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // The focus should be on the first link\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab to the next link\n        await press(Keys.Tab)\n\n        // The focus should be on the second link\n        assertActiveElement(getByText('Link 2'))\n\n        // Tab out of the component\n        await press(Keys.Tab)\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Next link outside of the popover\n        assertActiveElement(getByText('Next'))\n      })\n    )\n\n    it(\n      'should close the Popover when the PopoverPanel has a focus prop (PopoverPanel uses a Portal), and focus the next focusable item in line',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <a href=\"/\">Previous</a>\n            <Popover>\n              <PopoverButton>Trigger</PopoverButton>\n              <Portal>\n                <PopoverPanel focus>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Portal>\n            </Popover>\n          </div>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Focus should be within the panel\n        assertContainsActiveElement(getPopoverPanel())\n\n        // The focus should be on the first link\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab to the next link\n        await press(Keys.Tab)\n\n        // The focus should be on the second link\n        assertActiveElement(getByText('Link 2'))\n\n        // Tab out of the component\n        await press(Keys.Tab)\n\n        // The popover should be closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // The active element should be the Previous link outside of the popover, this is the next one in line\n        assertActiveElement(getByText('Previous'))\n      })\n    )\n  })\n\n  describe('`Shift+Tab` key', () => {\n    it(\n      'should close the Popover menu once we Tab out of the PopoverGroup',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <a href=\"/\">Previous</a>\n\n            <PopoverGroup>\n              <Popover>\n                <PopoverButton>Trigger 1</PopoverButton>\n                <PopoverPanel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Popover>\n\n              <Popover>\n                <PopoverButton>Trigger 2</PopoverButton>\n                <PopoverPanel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </PopoverPanel>\n              </Popover>\n            </PopoverGroup>\n          </div>\n        `)\n\n        // Focus the button of the second Popover\n        getByText('Trigger 2')?.focus()\n\n        // Open popover\n        await click(getByText('Trigger 2'))\n\n        // Verify we can tab to Trigger 1\n        await press(shift(Keys.Tab))\n        assertActiveElement(getByText('Trigger 1'))\n\n        // Let's Tab out of the PopoverGroup\n        await press(shift(Keys.Tab))\n\n        // Verify the previous link is now focused\n        assertActiveElement(getByText('Previous'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close the Popover menu once we Tab out of the Popover',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <div>\n            <a href=\"/\">Previous</a>\n\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n          </div>\n        `)\n\n        // Focus the button of the Popover\n        getPopoverButton()?.focus()\n\n        // Open popover\n        await click(getPopoverButton())\n\n        // Let's Tab out of the Popover\n        await press(shift(Keys.Tab))\n\n        // Verify the previous link is now focused\n        assertActiveElement(getByText('Previous'))\n\n        // Verify the popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the previous PopoverButton when Shift+Tab on the second PopoverButton',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 3</a>\n                <a href=\"/\">Link 4</a>\n              </PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Open the second popover\n        await click(getByText('Trigger 2'))\n        getByText('Trigger 2')?.focus()\n\n        // Ensure the second popover is open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n\n        // Close the popover\n        await press(Keys.Escape)\n\n        // Ensure the popover is now closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Ensure the second PopoverButton is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Tab backwards\n        await press(shift(Keys.Tab))\n\n        // Ensure the first PopoverButton is open\n        assertActiveElement(getByText('Trigger 1'))\n      })\n    )\n\n    it(\n      'should focus the PopoverButton when pressing Shift+Tab when we focus inside the PopoverPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger 1</PopoverButton>\n            <PopoverPanel focus>\n              <a href=\"/\">Link 1</a>\n              <a href=\"/\">Link 2</a>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the PopoverButton is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the PopoverButton when pressing Shift+Tab when we focus inside the PopoverPanel (inside a Portal)',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger 1</PopoverButton>\n            <Portal>\n              <PopoverPanel focus>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Portal>\n          </Popover>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the PopoverButton is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should focus the Popover.Button when pressing Shift+Tab when we focus inside the Popover.Panel (heuristic based portal)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <Teleport v-if=\"ready\" to=\"#portal\">\n                <PopoverPanel focus>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Teleport>\n              <button>Before</button>\n              <div id=\"portal\" />\n              <button>After</button>\n            </Popover>\n          `,\n          setup() {\n            let ready = ref(false)\n            onMounted(() => {\n              ready.value = true\n            })\n            return { ready }\n          },\n        })\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Ensure the Link 1 is focused\n        assertActiveElement(getByText('Link 1'))\n\n        // Tab out of the Panel\n        await press(shift(Keys.Tab))\n\n        // Ensure the Popover.Button is focused again\n        assertActiveElement(getPopoverButton())\n\n        // Ensure the Popover is closed\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to focus the last item in the PopoverPanel when pressing Shift+Tab on the next PopoverButton',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 1</a>\n                <a href=\"/\">Link 2</a>\n              </PopoverPanel>\n            </Popover>\n\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>\n                <a href=\"/\">Link 3</a>\n                <a href=\"/\">Link 4</a>\n              </PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Open the popover\n        await click(getByText('Trigger 1'))\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Focus the second button\n        getByText('Trigger 2')?.focus()\n\n        // Verify the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Ensure the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Press shift+tab, to move focus to the last item in the PopoverPanel\n        await press(shift(Keys.Tab), getByText('Trigger 2'))\n\n        // Verify we are focusing the last link of the first Popover\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n\n    it(\n      \"should be possible to focus the last item in the PopoverPanel when pressing Shift+Tab on the next PopoverButton (using Portal's)\",\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <Portal>\n                <PopoverPanel>\n                  <a href=\"/\">Link 1</a>\n                  <a href=\"/\">Link 2</a>\n                </PopoverPanel>\n              </Portal>\n            </Popover>\n\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <Portal>\n                <PopoverPanel>\n                  <a href=\"/\">Link 3</a>\n                  <a href=\"/\">Link 4</a>\n                </PopoverPanel>\n              </Portal>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Open the popover\n        await click(getByText('Trigger 1'))\n\n        // Ensure the popover is open\n        assertPopoverButton({ state: PopoverState.Visible })\n\n        // Focus the second button\n        getByText('Trigger 2')?.focus()\n\n        // Verify the second button is focused\n        assertActiveElement(getByText('Trigger 2'))\n\n        // Ensure the first Popover is still open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({ state: PopoverState.Visible })\n\n        // Press shift+tab, to move focus to the last item in the PopoverPanel\n        await press(shift(Keys.Tab), getByText('Trigger 2'))\n\n        // Verify we are focusing the last link of the first Popover\n        assertActiveElement(getByText('Link 2'))\n      })\n    )\n  })\n\n  describe('`Space` key', () => {\n    it(\n      'should be possible to open the popover with Space',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Open popover\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-panel-3' },\n        })\n      })\n    )\n\n    it(\n      'should not be possible to open the popover with Space when the button is disabled',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton disabled>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Try to open the popover\n        await press(Keys.Space)\n\n        // Verify it is still closed\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should be possible to close the popover with Space when the popover is open',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n        `)\n\n        assertPopoverButton({\n          state: PopoverState.InvisibleUnmounted,\n          attributes: { id: 'headlessui-popover-button-1' },\n        })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Focus the button\n        getPopoverButton()?.focus()\n\n        // Open popover\n        await press(Keys.Space)\n\n        // Verify it is open\n        assertPopoverButton({ state: PopoverState.Visible })\n        assertPopoverPanel({\n          state: PopoverState.Visible,\n          attributes: { id: 'headlessui-popover-panel-3' },\n        })\n\n        // Close popover\n        await press(Keys.Space)\n\n        // Verify it is closed again\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n      })\n    )\n\n    it(\n      'should close other popover menus when we open a new one',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <PopoverGroup>\n            <Popover>\n              <PopoverButton>Trigger 1</PopoverButton>\n              <PopoverPanel>Panel 1</PopoverPanel>\n            </Popover>\n            <Popover>\n              <PopoverButton>Trigger 2</PopoverButton>\n              <PopoverPanel>Panel 2</PopoverPanel>\n            </Popover>\n          </PopoverGroup>\n        `)\n\n        // Open the first Popover\n        await click(getByText('Trigger 1'))\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Focus trigger 2\n        getByText('Trigger 2')?.focus()\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 2'))\n\n        // Open the second popover\n        await press(Keys.Space)\n\n        // Verify the correct popovers are open\n        assertPopoverButton({ state: PopoverState.InvisibleUnmounted }, getByText('Trigger 1'))\n        assertPopoverButton({ state: PopoverState.Visible }, getByText('Trigger 2'))\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Space` on a PopoverButton inside a PopoverPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Open</PopoverButton>\n            <PopoverPanel>\n              <PopoverButton>Close</PopoverButton>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeBtn = getByText('Close')\n\n        expect(closeBtn).not.toHaveAttribute('id')\n        expect(closeBtn).not.toHaveAttribute('aria-controls')\n        expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Space, closeBtn)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n      })\n    )\n\n    it(\n      'should close the Popover by pressing `Enter` on a PopoverButton and go to the href of the `a` inside a PopoverPanel',\n      suppressConsoleLogs(async () => {\n        renderTemplate(html`\n          <Popover>\n            <PopoverButton>Open</PopoverButton>\n            <PopoverPanel>\n              <PopoverButton as=\"template\">\n                <a href=\"#closed\">Close</a>\n              </PopoverButton>\n            </PopoverPanel>\n          </Popover>\n        `)\n\n        // Open the popover\n        await click(getPopoverButton())\n\n        let closeLink = getByText('Close')\n\n        expect(closeLink).not.toHaveAttribute('id')\n        expect(closeLink).not.toHaveAttribute('aria-controls')\n        expect(closeLink).not.toHaveAttribute('aria-expanded')\n\n        // The close button should close the popover\n        await press(Keys.Enter, closeLink)\n\n        // Verify it is closed\n        assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n        // Verify we restored the Open button\n        assertActiveElement(getPopoverButton())\n\n        // Verify that we got redirected to the href\n        expect(document.location.hash).toEqual('#closed')\n      })\n    )\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it(\n    'should be possible to open a popover on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n      assertPopoverPanel({\n        state: PopoverState.Visible,\n        attributes: { id: 'headlessui-popover-panel-3' },\n      })\n    })\n  )\n\n  it(\n    'should not be possible to open a popover on right click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Open popover\n      await click(getPopoverButton(), MouseButton.Right)\n\n      // Verify it is still closed\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should not be possible to open a popover on click when the button is disabled',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton disabled>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Try to open the popover\n      await click(getPopoverButton())\n\n      // Verify it is still closed\n      assertPopoverButton({\n        state: PopoverState.InvisibleUnmounted,\n        attributes: { id: 'headlessui-popover-button-1' },\n      })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a popover on click',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      getPopoverButton()?.focus()\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click to close\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close a Popover using a click on the PopoverOverlay',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n          <PopoverOverlay />\n        </Popover>\n      `)\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the overlay to close\n      await click(getPopoverOverlay())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n    })\n  )\n\n  it(\n    'should be possible to close the popover, and re-focus the button when we click outside on the body element',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Trigger</PopoverButton>\n          <PopoverPanel>Contents</PopoverPanel>\n        </Popover>\n      `)\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the body to close\n      await click(document.body)\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the popover, and re-focus the button when we click outside on a non-focusable element',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <div>\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n\n          <span>I am just text</span>\n        </div>\n      `)\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the span to close\n      await click(getByText('I am just text'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the popover, by clicking outside the popover on another focusable element',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <div>\n          <Popover>\n            <PopoverButton>Trigger</PopoverButton>\n            <PopoverPanel>Contents</PopoverPanel>\n          </Popover>\n\n          <button>Different button</button>\n        </div>\n      `)\n\n      // Open popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the extra button to close\n      await click(getByText('Different button'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the other button is focused\n      assertActiveElement(getByText('Different button'))\n    })\n  )\n\n  it(\n    'should be possible to close the popover, by clicking outside the popover on another element inside a focusable element',\n    suppressConsoleLogs(async () => {\n      let focusFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <div>\n            <Popover>\n              <PopoverButton :onFocus=\"focusFn\">Trigger</PopoverButton>\n              <PopoverPanel>Contents</PopoverPanel>\n            </Popover>\n\n            <button id=\"btn\">\n              <span>Different button</span>\n            </button>\n          </div>\n        `,\n        setup() {\n          return { focusFn }\n        },\n      })\n\n      // Open popover\n      await click(getPopoverButton())\n      getPopoverButton()?.focus()\n\n      // Verify it is open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Click the span inside the extra button to close\n      await click(getByText('Different button'))\n\n      // Verify it is closed\n      assertPopoverButton({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the other button is focused\n      assertActiveElement(document.getElementById('btn'))\n\n      // Ensure that the focus button only got focus once (first click)\n      expect(focusFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on a PopoverButton inside a PopoverPanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Open</PopoverButton>\n          <PopoverPanel>\n            <PopoverButton>Close</PopoverButton>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      let closeBtn = getByText('Close')\n\n      expect(closeBtn).not.toHaveAttribute('id')\n      expect(closeBtn).not.toHaveAttribute('aria-controls')\n      expect(closeBtn).not.toHaveAttribute('aria-expanded')\n\n      // The close button should close the popover\n      await click(closeBtn)\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify we restored the Open button\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should not close the Popover when clicking on a focusable element inside a static PopoverPanel',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <Popover>\n            <PopoverButton>Open</PopoverButton>\n            <PopoverPanel static>\n              <button @click=\"clickFn\">btn</button>\n            </PopoverPanel>\n          </Popover>\n        `,\n        setup: () => ({ clickFn }),\n      })\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The button should not close the popover\n      await click(getByText('btn'))\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.Visible })\n\n      // Verify we actually clicked the button\n      expect(clickFn).toHaveBeenCalledTimes(1)\n    })\n  )\n\n  it(\n    'should not close the Popover when clicking on a non-focusable element inside a static PopoverPanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Open</PopoverButton>\n          <PopoverPanel static>\n            <span>element</span>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The element should not close the popover\n      await click(getByText('element'))\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.Visible })\n    })\n  )\n\n  it(\n    'should close the Popover when clicking outside of a static PopoverPanel',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Open</PopoverButton>\n          <PopoverPanel static>\n            <span>element</span>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // The element should close the popover\n      await click(document.body)\n\n      // Verify it is still open\n      assertPopoverButton({ state: PopoverState.InvisibleHidden })\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on the Popover.Button outside the Popover.Panel',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Toggle</PopoverButton>\n          <PopoverPanel>\n            <button>Contents</button>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should be possible to close the Popover by clicking on the Popover.Button outside the Popover.Panel (when using the `focus` prop)',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover>\n          <PopoverButton>Toggle</PopoverButton>\n          <PopoverPanel focus>\n            <button>Contents</button>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Close the popover\n      await click(getPopoverButton())\n\n      // Verify it is closed\n      assertPopoverPanel({ state: PopoverState.InvisibleUnmounted })\n\n      // Verify the button is focused\n      assertActiveElement(getPopoverButton())\n    })\n  )\n\n  it(\n    'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using Portals',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <Popover>\n            <PopoverButton>Toggle</PopoverButton>\n            <PopoverPanel>\n              <Portal>\n                <button @click=\"clickFn\">foo</button>\n              </Portal>\n            </PopoverPanel>\n          </Popover>\n        `,\n        setup: () => ({ clickFn }),\n      })\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Click the button outside the Popover (DOM) but inside (Portal / React tree)\n      await click(getByText('foo'))\n\n      // Verify it is still open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Verify the button was clicked\n      expect(clickFn).toHaveBeenCalled()\n    })\n  )\n\n  it(\n    'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using nested Portals',\n    suppressConsoleLogs(async () => {\n      let clickFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <Popover>\n            <PopoverButton>Toggle</PopoverButton>\n            <PopoverPanel>\n              Level 0\n              <Portal>\n                Level 1\n                <Portal>\n                  Level 2\n                  <Portal>\n                    Level 3\n                    <Portal>\n                      Level 4\n                      <button @click=\"clickFn\">foo</button>\n                    </Portal>\n                  </Portal>\n                </Portal>\n              </Portal>\n            </PopoverPanel>\n          </Popover>\n        `,\n        setup: () => ({ clickFn }),\n      })\n\n      // Open the popover\n      await click(getPopoverButton())\n\n      // Verify it is open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Click the button outside the Popover (DOM) but inside (Portal / React tree)\n      await click(getByText('foo'))\n\n      // Verify it is still open\n      assertPopoverPanel({ state: PopoverState.Visible })\n\n      // Verify the button was clicked\n      expect(clickFn).toHaveBeenCalled()\n    })\n  )\n})\n\ndescribe('Nested popovers', () => {\n  it(\n    'should be possible to nest Popover components and control them individually',\n    suppressConsoleLogs(async () => {\n      renderTemplate(html`\n        <Popover data-testid=\"popover-a\">\n          <PopoverButton>Toggle A</PopoverButton>\n          <PopoverPanel>\n            <span>Contents A</span>\n            <Popover data-testid=\"popover-b\">\n              <PopoverButton>Toggle B</PopoverButton>\n              <PopoverPanel>\n                <span>Contents B</span>\n              </PopoverPanel>\n            </Popover>\n          </PopoverPanel>\n        </Popover>\n      `)\n\n      // Verify that Popover B is not there yet\n      expect(document.querySelector('[data-testid=\"popover-b\"]')).toBeNull()\n\n      // Open Popover A\n      await click(getByText('Toggle A'))\n\n      // Ensure Popover A is visible\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-a\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n\n      // Ensure Popover B is visible\n      assertPopoverPanel(\n        { state: PopoverState.InvisibleUnmounted },\n        document.querySelector(\n          '[data-testid=\"popover-b\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n\n      // Open Popover B\n      await click(getByText('Toggle B'))\n\n      // Ensure both popovers are open\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-a\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n      assertPopoverPanel(\n        { state: PopoverState.Visible },\n        document.querySelector(\n          '[data-testid=\"popover-b\"] [id^=\"headlessui-popover-panel-\"]'\n        ) as HTMLElement\n      )\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/popover/popover.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  shallowRef,\n  watchEffect,\n  type ComponentPublicInstance,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useNestedPortals } from '../../components/portal/portal'\nimport { useEventListener } from '../../hooks/use-event-listener'\nimport { useId } from '../../hooks/use-id'\nimport { useOutsideClick } from '../../hooks/use-outside-click'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { useMainTreeNode, useRootContainers } from '../../hooks/use-root-containers'\nimport { Direction as TabDirection, useTabDirection } from '../../hooks/use-tab-direction'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { State, useOpenClosed, useOpenClosedProvider } from '../../internal/open-closed'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport {\n  Focus,\n  FocusResult,\n  FocusableMode,\n  focusIn,\n  getFocusableElements,\n  isFocusableElement,\n} from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { Features, render } from '../../utils/render'\n\nenum PopoverStates {\n  Open,\n  Closed,\n}\n\ninterface StateDefinition {\n  // State\n  popoverState: Ref<PopoverStates>\n  button: Ref<HTMLElement | null>\n  buttonId: Ref<string | null>\n  panel: Ref<HTMLElement | null>\n  panelId: Ref<string | null>\n\n  isPortalled: Ref<boolean>\n\n  beforePanelSentinel: Ref<HTMLElement | null>\n  afterPanelSentinel: Ref<HTMLElement | null>\n\n  // State mutators\n  togglePopover(): void\n  closePopover(): void\n\n  // Exposed functions\n  close(focusableElement: HTMLElement | Ref<HTMLElement | null>): void\n}\n\nlet PopoverContext = Symbol('PopoverContext') as InjectionKey<StateDefinition>\nfunction usePopoverContext(component: string) {\n  let context = inject(PopoverContext, null)\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <${Popover.name} /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, usePopoverContext)\n    throw err\n  }\n  return context\n}\n\nlet PopoverGroupContext = Symbol('PopoverGroupContext') as InjectionKey<{\n  registerPopover(registerBag: PopoverRegisterBag): void\n  unregisterPopover(registerBag: PopoverRegisterBag): void\n  isFocusWithinPopoverGroup(): boolean\n  closeOthers(buttonId: string): void\n  mainTreeNodeRef: Ref<HTMLElement | null>\n} | null>\n\nfunction usePopoverGroupContext() {\n  return inject(PopoverGroupContext, null)\n}\n\nlet PopoverPanelContext = Symbol('PopoverPanelContext') as InjectionKey<Ref<string | null>>\nfunction usePopoverPanelContext() {\n  return inject(PopoverPanelContext, null)\n}\n\ninterface PopoverRegisterBag {\n  buttonId: Ref<string | null>\n  panelId: Ref<string | null>\n  close(): void\n}\n\n// ---\n\nexport let Popover = defineComponent({\n  name: 'Popover',\n  inheritAttrs: false,\n  props: {\n    as: { type: [Object, String], default: 'div' },\n  },\n  setup(props, { slots, attrs, expose }) {\n    let internalPopoverRef = ref<HTMLElement | null>(null)\n\n    expose({ el: internalPopoverRef, $el: internalPopoverRef })\n\n    let popoverState = ref<StateDefinition['popoverState']['value']>(PopoverStates.Closed)\n    let button = ref<StateDefinition['button']['value']>(null)\n    let beforePanelSentinel = ref<StateDefinition['beforePanelSentinel']['value']>(null)\n    let afterPanelSentinel = ref<StateDefinition['afterPanelSentinel']['value']>(null)\n    let panel = ref<StateDefinition['panel']['value']>(null)\n    let ownerDocument = computed(() => getOwnerDocument(internalPopoverRef))\n    let isPortalled = computed(() => {\n      if (!dom(button)) return false\n      if (!dom(panel)) return false\n\n      // We are part of a different \"root\" tree, so therefore we can consider it portalled. This is a\n      // heuristic because 3rd party tools could use some form of portal, typically rendered at the\n      // end of the body but we don't have an actual reference to that.\n      for (let root of document.querySelectorAll('body > *')) {\n        if (Number(root?.contains(dom(button))) ^ Number(root?.contains(dom(panel)))) {\n          return true\n        }\n      }\n\n      // Use another heuristic to try and calculate whether or not the focusable\n      // elements are near each other (aka, following the default focus/tab\n      // order from the browser). If they are then it doesn't really matter if\n      // they are portalled or not because we can follow the default tab order.\n      // But if they are not, then we can consider it being portalled so that we\n      // can ensure that tab and shift+tab (hopefully) go to the correct spot.\n      let elements = getFocusableElements()\n      let buttonIdx = elements.indexOf(dom(button)!)\n\n      let beforeIdx = (buttonIdx + elements.length - 1) % elements.length\n      let afterIdx = (buttonIdx + 1) % elements.length\n\n      let beforeElement = elements[beforeIdx]\n      let afterElement = elements[afterIdx]\n\n      if (!dom(panel)?.contains(beforeElement) && !dom(panel)?.contains(afterElement)) {\n        return true\n      }\n\n      return false\n    })\n\n    let api = {\n      popoverState,\n      buttonId: ref(null),\n      panelId: ref(null),\n      panel,\n      button,\n      isPortalled,\n      beforePanelSentinel,\n      afterPanelSentinel,\n      togglePopover() {\n        popoverState.value = match(popoverState.value, {\n          [PopoverStates.Open]: PopoverStates.Closed,\n          [PopoverStates.Closed]: PopoverStates.Open,\n        })\n      },\n      closePopover() {\n        if (popoverState.value === PopoverStates.Closed) return\n        popoverState.value = PopoverStates.Closed\n      },\n      close(focusableElement: HTMLElement | Ref<HTMLElement | null>) {\n        api.closePopover()\n\n        let restoreElement = (() => {\n          if (!focusableElement) return dom(api.button)\n          if (focusableElement instanceof HTMLElement) return focusableElement\n          if (focusableElement.value instanceof HTMLElement) return dom(focusableElement)\n\n          return dom(api.button)\n        })()\n\n        restoreElement?.focus()\n      },\n    } as StateDefinition\n\n    provide(PopoverContext, api)\n    useOpenClosedProvider(\n      computed(() =>\n        match(popoverState.value, {\n          [PopoverStates.Open]: State.Open,\n          [PopoverStates.Closed]: State.Closed,\n        })\n      )\n    )\n\n    let registerBag = {\n      buttonId: api.buttonId,\n      panelId: api.panelId,\n      close() {\n        api.closePopover()\n      },\n    }\n\n    let groupContext = usePopoverGroupContext()\n    let registerPopover = groupContext?.registerPopover\n\n    let [portals, PortalWrapper] = useNestedPortals()\n    let root = useRootContainers({\n      mainTreeNodeRef: groupContext?.mainTreeNodeRef,\n      portals,\n      defaultContainers: [button, panel],\n    })\n\n    function isFocusWithinPopoverGroup() {\n      return (\n        groupContext?.isFocusWithinPopoverGroup() ??\n        (ownerDocument.value?.activeElement &&\n          (dom(button)?.contains(ownerDocument.value.activeElement) ||\n            dom(panel)?.contains(ownerDocument.value.activeElement)))\n      )\n    }\n\n    watchEffect(() => registerPopover?.(registerBag))\n\n    // Handle focus out\n    useEventListener(\n      ownerDocument.value?.defaultView,\n      'focus',\n      (event) => {\n        if (event.target === window) return\n        if (!(event.target instanceof HTMLElement)) return\n        if (popoverState.value !== PopoverStates.Open) return\n        if (isFocusWithinPopoverGroup()) return\n        if (!button) return\n        if (!panel) return\n        if (root.contains(event.target)) return\n        if (dom(api.beforePanelSentinel)?.contains(event.target)) return\n        if (dom(api.afterPanelSentinel)?.contains(event.target)) return\n\n        api.closePopover()\n      },\n      true\n    )\n\n    // Handle outside click\n    useOutsideClick(\n      root.resolveContainers,\n      (event, target) => {\n        api.closePopover()\n\n        if (!isFocusableElement(target, FocusableMode.Loose)) {\n          event.preventDefault()\n          dom(button)?.focus()\n        }\n      },\n      computed(() => popoverState.value === PopoverStates.Open)\n    )\n\n    return () => {\n      let slot = { open: popoverState.value === PopoverStates.Open, close: api.close }\n      return h(Fragment, [\n        h(PortalWrapper, {}, () =>\n          render({\n            theirProps: { ...props, ...attrs },\n            ourProps: { ref: internalPopoverRef },\n            slot,\n            slots,\n            attrs,\n            name: 'Popover',\n          })\n        ),\n        h(root.MainTreeNode),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let PopoverButton = defineComponent({\n  name: 'PopoverButton',\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    disabled: { type: [Boolean], default: false },\n    id: { type: String, default: () => `headlessui-popover-button-${useId()}` },\n  },\n  inheritAttrs: false,\n  setup(props, { attrs, slots, expose }) {\n    let api = usePopoverContext('PopoverButton')\n    let ownerDocument = computed(() => getOwnerDocument(api.button))\n\n    expose({ el: api.button, $el: api.button })\n\n    onMounted(() => {\n      api.buttonId.value = props.id\n    })\n    onUnmounted(() => {\n      api.buttonId.value = null\n    })\n\n    let groupContext = usePopoverGroupContext()\n    let closeOthers = groupContext?.closeOthers\n\n    let panelContext = usePopoverPanelContext()\n\n    // A button inside a panel will just have \"close\" functionality, no \"open\" functionality.\n    // However, if a `Popover.Button` is rendered inside a `Popover` which in turn is rendered\n    // inside a `Popover.Panel` (aka nested popovers), then we need to make sure that the button is\n    // able to open the nested popover.\n    let isWithinPanel = computed(() =>\n      panelContext === null ? false : panelContext.value === api.panelId.value\n    )\n\n    let elementRef = ref<HTMLElement | ComponentPublicInstance | null>(null)\n    let sentinelId = `headlessui-focus-sentinel-${useId()}`\n\n    if (!isWithinPanel.value) {\n      watchEffect(() => {\n        // `elementRef` could be a Vue component in which case we want to grab the DOM element from it\n        api.button.value = dom(elementRef)\n      })\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      elementRef\n    )\n\n    function handleKeyDown(event: KeyboardEvent) {\n      if (isWithinPanel.value) {\n        if (api.popoverState.value === PopoverStates.Closed) return\n        switch (event.key) {\n          case Keys.Space:\n          case Keys.Enter:\n            event.preventDefault() // Prevent triggering a *click* event\n            // @ts-expect-error\n            event.target.click?.()\n            api.closePopover()\n            dom(api.button)?.focus() // Re-focus the original opening Button\n            break\n        }\n      } else {\n        switch (event.key) {\n          case Keys.Space:\n          case Keys.Enter:\n            event.preventDefault() // Prevent triggering a *click* event\n            event.stopPropagation()\n            if (api.popoverState.value === PopoverStates.Closed) closeOthers?.(api.buttonId.value!)\n            api.togglePopover()\n            break\n\n          case Keys.Escape:\n            if (api.popoverState.value !== PopoverStates.Open)\n              return closeOthers?.(api.buttonId.value!)\n            if (!dom(api.button)) return\n            if (\n              ownerDocument.value?.activeElement &&\n              !dom(api.button)?.contains(ownerDocument.value.activeElement)\n            )\n              return\n            event.preventDefault()\n            event.stopPropagation()\n            api.closePopover()\n            break\n        }\n      }\n    }\n\n    function handleKeyUp(event: KeyboardEvent) {\n      if (isWithinPanel.value) return\n      if (event.key === Keys.Space) {\n        // Required for firefox, event.preventDefault() in handleKeyDown for\n        // the Space key doesn't cancel the handleKeyUp, which in turn\n        // triggers a *click*.\n        event.preventDefault()\n      }\n    }\n\n    function handleClick(event: MouseEvent) {\n      if (props.disabled) return\n      if (isWithinPanel.value) {\n        api.closePopover()\n        dom(api.button)?.focus() // Re-focus the original opening Button\n      } else {\n        event.preventDefault()\n        event.stopPropagation()\n        if (api.popoverState.value === PopoverStates.Closed) closeOthers?.(api.buttonId.value!)\n        api.togglePopover()\n        dom(api.button)?.focus()\n      }\n    }\n\n    function handleMouseDown(event: MouseEvent) {\n      event.preventDefault()\n      event.stopPropagation()\n    }\n\n    let direction = useTabDirection()\n    function handleFocus() {\n      let el = dom(api.panel) as HTMLElement\n      if (!el) return\n\n      function run() {\n        let result = match(direction.value, {\n          [TabDirection.Forwards]: () => focusIn(el, Focus.First),\n          [TabDirection.Backwards]: () => focusIn(el, Focus.Last),\n        })\n\n        if (result === FocusResult.Error) {\n          focusIn(\n            getFocusableElements().filter((el) => el.dataset.headlessuiFocusGuard !== 'true'),\n            match(direction.value, {\n              [TabDirection.Forwards]: Focus.Next,\n              [TabDirection.Backwards]: Focus.Previous,\n            }),\n            { relativeTo: dom(api.button) }\n          )\n        }\n      }\n\n      // TODO: Cleanup once we are using real browser tests\n      if (process.env.NODE_ENV === 'test') {\n        microTask(run)\n      } else {\n        run()\n      }\n    }\n\n    return () => {\n      let visible = api.popoverState.value === PopoverStates.Open\n      let slot = { open: visible }\n      let { id, ...theirProps } = props\n      let ourProps = isWithinPanel.value\n        ? {\n            ref: elementRef,\n            type: type.value,\n            onKeydown: handleKeyDown,\n            onClick: handleClick,\n          }\n        : {\n            ref: elementRef,\n            id,\n            type: type.value,\n            'aria-expanded': api.popoverState.value === PopoverStates.Open,\n            'aria-controls': dom(api.panel) ? api.panelId.value : undefined,\n            disabled: props.disabled ? true : undefined,\n            onKeydown: handleKeyDown,\n            onKeyup: handleKeyUp,\n            onClick: handleClick,\n            onMousedown: handleMouseDown,\n          }\n\n      return h(Fragment, [\n        render({\n          ourProps,\n          theirProps: { ...attrs, ...theirProps },\n          slot,\n          attrs: attrs,\n          slots: slots,\n          name: 'PopoverButton',\n        }),\n        visible &&\n          !isWithinPanel.value &&\n          api.isPortalled.value &&\n          h(Hidden, {\n            id: sentinelId,\n            features: HiddenFeatures.Focusable,\n            'data-headlessui-focus-guard': true,\n            as: 'button',\n            type: 'button',\n            onFocus: handleFocus,\n          }),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let PopoverOverlay = defineComponent({\n  name: 'PopoverOverlay',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n  },\n  setup(props, { attrs, slots }) {\n    let api = usePopoverContext('PopoverOverlay')\n    let id = `headlessui-popover-overlay-${useId()}`\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.popoverState.value === PopoverStates.Open\n    })\n\n    function handleClick() {\n      api.closePopover()\n    }\n\n    return () => {\n      let slot = { open: api.popoverState.value === PopoverStates.Open }\n      let ourProps = {\n        id,\n        'aria-hidden': true,\n        onClick: handleClick,\n      }\n\n      return render({\n        ourProps,\n        theirProps: props,\n        slot,\n        attrs,\n        slots,\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'PopoverOverlay',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let PopoverPanel = defineComponent({\n  name: 'PopoverPanel',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    focus: { type: Boolean, default: false },\n    id: { type: String, default: () => `headlessui-popover-panel-${useId()}` },\n  },\n  inheritAttrs: false,\n  setup(props, { attrs, slots, expose }) {\n    let { focus } = props\n    let api = usePopoverContext('PopoverPanel')\n    let ownerDocument = computed(() => getOwnerDocument(api.panel))\n\n    let beforePanelSentinelId = `headlessui-focus-sentinel-before-${useId()}`\n    let afterPanelSentinelId = `headlessui-focus-sentinel-after-${useId()}`\n\n    expose({ el: api.panel, $el: api.panel })\n\n    onMounted(() => {\n      api.panelId.value = props.id\n    })\n    onUnmounted(() => {\n      api.panelId.value = null\n    })\n\n    provide(PopoverPanelContext, api.panelId)\n\n    // Move focus within panel\n    watchEffect(() => {\n      if (!focus) return\n      if (api.popoverState.value !== PopoverStates.Open) return\n      if (!api.panel) return\n\n      let activeElement = ownerDocument.value?.activeElement as HTMLElement\n      if (dom(api.panel)?.contains(activeElement)) return // Already focused within Dialog\n\n      focusIn(dom(api.panel)!, Focus.First)\n    })\n\n    let usesOpenClosedState = useOpenClosed()\n    let visible = computed(() => {\n      if (usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return api.popoverState.value === PopoverStates.Open\n    })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      switch (event.key) {\n        case Keys.Escape:\n          if (api.popoverState.value !== PopoverStates.Open) return\n          if (!dom(api.panel)) return\n          if (ownerDocument.value && !dom(api.panel)?.contains(ownerDocument.value.activeElement)) {\n            return\n          }\n          event.preventDefault()\n          event.stopPropagation()\n          api.closePopover()\n          dom(api.button)?.focus()\n          break\n      }\n    }\n\n    function handleBlur(event: MouseEvent) {\n      let el = event.relatedTarget as HTMLElement\n      if (!el) return\n      if (!dom(api.panel)) return\n      if (dom(api.panel)?.contains(el)) return\n\n      api.closePopover()\n\n      if (\n        dom(api.beforePanelSentinel)?.contains?.(el) ||\n        dom(api.afterPanelSentinel)?.contains?.(el)\n      ) {\n        el.focus({ preventScroll: true })\n      }\n    }\n\n    let direction = useTabDirection()\n    function handleBeforeFocus() {\n      let el = dom(api.panel) as HTMLElement\n      if (!el) return\n\n      function run() {\n        match(direction.value, {\n          [TabDirection.Forwards]: () => {\n            // Try to focus the first thing in the panel. But if that fails (e.g.: there are no\n            // focusable elements, then we can move outside of the panel)\n            let result = focusIn(el, Focus.First)\n            if (result === FocusResult.Error) {\n              dom(api.afterPanelSentinel)?.focus()\n            }\n          },\n          [TabDirection.Backwards]: () => {\n            // Coming from the Popover.Panel (which is portalled to somewhere else). Let's redirect\n            // the focus to the Popover.Button again.\n            dom(api.button)?.focus({ preventScroll: true })\n          },\n        })\n      }\n\n      // TODO: Cleanup once we are using real browser tests\n      if (process.env.NODE_ENV === 'test') {\n        microTask(run)\n      } else {\n        run()\n      }\n    }\n\n    function handleAfterFocus() {\n      let el = dom(api.panel) as HTMLElement\n      if (!el) return\n\n      function run() {\n        match(direction.value, {\n          [TabDirection.Forwards]: () => {\n            let button = dom(api.button)\n            let panel = dom(api.panel)\n            if (!button) return\n\n            let elements = getFocusableElements()\n\n            let idx = elements.indexOf(button)\n            let before = elements.slice(0, idx + 1)\n            let after = elements.slice(idx + 1)\n\n            let combined = [...after, ...before]\n\n            // Ignore sentinel buttons and items inside the panel\n            for (let element of combined.slice()) {\n              if (element.dataset.headlessuiFocusGuard === 'true' || panel?.contains(element)) {\n                let idx = combined.indexOf(element)\n                if (idx !== -1) combined.splice(idx, 1)\n              }\n            }\n\n            focusIn(combined, Focus.First, { sorted: false })\n          },\n          [TabDirection.Backwards]: () => {\n            // Try to focus the first thing in the panel. But if that fails (e.g.: there are no\n            // focusable elements, then we can move outside of the panel)\n            let result = focusIn(el, Focus.Previous)\n            if (result === FocusResult.Error) {\n              dom(api.button)?.focus()\n            }\n          },\n        })\n      }\n\n      // TODO: Cleanup once we are using real browser tests\n      if (process.env.NODE_ENV === 'test') {\n        microTask(run)\n      } else {\n        run()\n      }\n    }\n\n    return () => {\n      let slot = {\n        open: api.popoverState.value === PopoverStates.Open,\n        close: api.close,\n      }\n\n      let { id, focus: _focus, ...theirProps } = props\n      let ourProps = {\n        ref: api.panel,\n        id,\n        onKeydown: handleKeyDown,\n        onFocusout: focus && api.popoverState.value === PopoverStates.Open ? handleBlur : undefined,\n        tabIndex: -1,\n      }\n\n      return render({\n        ourProps,\n        theirProps: { ...attrs, ...theirProps },\n        attrs,\n        slot,\n        slots: {\n          ...slots,\n          default: (...args) => [\n            h(Fragment, [\n              visible.value &&\n                api.isPortalled.value &&\n                h(Hidden, {\n                  id: beforePanelSentinelId,\n                  ref: api.beforePanelSentinel,\n                  features: HiddenFeatures.Focusable,\n                  'data-headlessui-focus-guard': true,\n                  as: 'button',\n                  type: 'button',\n                  onFocus: handleBeforeFocus,\n                }),\n              slots.default?.(...args),\n              visible.value &&\n                api.isPortalled.value &&\n                h(Hidden, {\n                  id: afterPanelSentinelId,\n                  ref: api.afterPanelSentinel,\n                  features: HiddenFeatures.Focusable,\n                  'data-headlessui-focus-guard': true,\n                  as: 'button',\n                  type: 'button',\n                  onFocus: handleAfterFocus,\n                }),\n            ]),\n          ],\n        },\n        features: Features.RenderStrategy | Features.Static,\n        visible: visible.value,\n        name: 'PopoverPanel',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let PopoverGroup = defineComponent({\n  name: 'PopoverGroup',\n  inheritAttrs: false,\n  props: {\n    as: { type: [Object, String], default: 'div' },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let groupRef = ref<HTMLElement | null>(null)\n    let popovers = shallowRef<PopoverRegisterBag[]>([])\n    let ownerDocument = computed(() => getOwnerDocument(groupRef))\n    let root = useMainTreeNode()\n\n    expose({ el: groupRef, $el: groupRef })\n\n    function unregisterPopover(registerBag: PopoverRegisterBag) {\n      let idx = popovers.value.indexOf(registerBag)\n      if (idx !== -1) popovers.value.splice(idx, 1)\n    }\n\n    function registerPopover(registerBag: PopoverRegisterBag) {\n      popovers.value.push(registerBag)\n      return () => {\n        unregisterPopover(registerBag)\n      }\n    }\n\n    function isFocusWithinPopoverGroup() {\n      let owner = ownerDocument.value\n      if (!owner) return false\n      let element = owner.activeElement as HTMLElement\n\n      if (dom(groupRef)?.contains(element)) return true\n\n      // Check if the focus is in one of the button or panel elements. This is important in case you are rendering inside a Portal.\n      return popovers.value.some((bag) => {\n        return (\n          owner!.getElementById(bag.buttonId.value!)?.contains(element) ||\n          owner!.getElementById(bag.panelId.value!)?.contains(element)\n        )\n      })\n    }\n\n    function closeOthers(buttonId: string) {\n      for (let popover of popovers.value) {\n        if (popover.buttonId.value !== buttonId) popover.close()\n      }\n    }\n\n    provide(PopoverGroupContext, {\n      registerPopover,\n      unregisterPopover,\n      isFocusWithinPopoverGroup,\n      closeOthers,\n      mainTreeNodeRef: root.mainTreeNodeRef,\n    })\n\n    return () => {\n      let ourProps = { ref: groupRef }\n\n      return h(Fragment, [\n        render({\n          ourProps,\n          theirProps: { ...props, ...attrs },\n          slot: {},\n          attrs,\n          slots,\n          name: 'PopoverGroup',\n        }),\n        h(root.MainTreeNode),\n      ])\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/portal/__snapshots__/portal.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should be possible to force the Portal into a specific element using PortalGroup 1`] = `\"<div><div><div data-v-app=\\\\\"\\\\\"><main><aside id=\\\\\"group-1\\\\\">A<div data-headlessui-portal=\\\\\"\\\\\">Next to A</div></aside><section id=\\\\\"group-2\\\\\"><span>B</span></section><!--teleport start--><!--teleport end--></main></div></div></div>\"`;\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/portal/portal.test.ts",
    "content": "import {\n  createSSRApp,\n  defineComponent,\n  h,\n  nextTick,\n  ref,\n  type ComponentOptionsWithoutProps,\n} from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { html } from '../../test-utils/html'\nimport { click } from '../../test-utils/interactions'\nimport { createRenderTemplate } from '../../test-utils/vue-testing-library'\nimport { Portal, PortalGroup } from './portal'\n\nfunction getPortalRoot() {\n  return document.getElementById('headlessui-portal-root')!\n}\n\nbeforeEach(() => {\n  document.body.innerHTML = ''\n})\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({ Portal, PortalGroup })\n\nasync function ssrRenderTemplate(input: string | ComponentOptionsWithoutProps) {\n  let defaultComponents = { Portal, PortalGroup }\n\n  if (typeof input === 'string') {\n    let app = createSSRApp({\n      render: () => h(defineComponent({ template: input, components: defaultComponents })),\n    })\n\n    return await renderToString(app)\n  }\n\n  let app = createSSRApp({\n    render: () =>\n      h(\n        defineComponent(\n          Object.assign({}, input, {\n            components: { ...defaultComponents, ...input.components },\n          }) as Parameters<typeof defineComponent>[0]\n        )\n      ),\n  })\n\n  return await renderToString(app)\n}\n\nasync function withoutBrowserGlobals<T>(fn: () => Promise<T>) {\n  let oldWindow = globalThis.window\n  let oldDocument = globalThis.document\n\n  Object.defineProperty(globalThis, '_document', {\n    value: undefined,\n    configurable: true,\n  })\n\n  Object.defineProperty(globalThis, '_globalProxy', {\n    value: undefined,\n    configurable: true,\n  })\n\n  try {\n    return await fn()\n  } finally {\n    Object.defineProperty(globalThis, '_globalProxy', {\n      value: oldWindow,\n      configurable: true,\n    })\n\n    Object.defineProperty(globalThis, '_document', {\n      value: oldDocument,\n      configurable: true,\n    })\n  }\n}\n\nit('SSR-rendering a Portal should not error', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  let result = await withoutBrowserGlobals(() =>\n    ssrRenderTemplate(html`\n      <main id=\"parent\">\n        <Portal>\n          <p id=\"content\">Contents...</p>\n        </Portal>\n      </main>\n    `)\n  )\n\n  expect(getPortalRoot()).toBe(null)\n\n  expect(result).toBe(html`<main id=\"parent\"><!----></main>`)\n})\n\nit('should be possible to use a Portal', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  renderTemplate(html`\n    <main id=\"parent\">\n      <Portal>\n        <p id=\"content\">Contents...</p>\n      </Portal>\n    </main>\n  `)\n\n  await nextTick()\n\n  let parent = document.getElementById('parent')\n  let content = document.getElementById('content')\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Ensure the content is not part of the parent\n  expect(parent).not.toContainElement(content)\n\n  // Ensure the content does exist\n  expect(content).not.toBe(null)\n  expect(content).toHaveTextContent('Contents...')\n})\n\nit('should be possible to use multiple Portal elements', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  renderTemplate(html`\n    <main id=\"parent\">\n      <Portal>\n        <p id=\"content1\">Contents 1 ...</p>\n      </Portal>\n      <hr />\n      <Portal>\n        <p id=\"content2\">Contents 2 ...</p>\n      </Portal>\n    </main>\n  `)\n\n  await nextTick()\n\n  let parent = document.getElementById('parent')\n  let content1 = document.getElementById('content1')\n  let content2 = document.getElementById('content2')\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Ensure the content1 is not part of the parent\n  expect(parent).not.toContainElement(content1)\n\n  // Ensure the content2 is not part of the parent\n  expect(parent).not.toContainElement(content2)\n\n  // Ensure the content does exist\n  expect(content1).not.toBe(null)\n  expect(content1).toHaveTextContent('Contents 1 ...')\n\n  // Ensure the content does exist\n  expect(content2).not.toBe(null)\n  expect(content2).toHaveTextContent('Contents 2 ...')\n})\n\nit('should cleanup the Portal root when the last Portal is unmounted', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  renderTemplate({\n    template: html`\n      <main id=\"parent\">\n        <button id=\"a\" @click=\"toggleA\">Toggle A</button>\n        <button id=\"b\" @click=\"toggleB\">Toggle B</button>\n\n        <Portal v-if=\"renderA\">\n          <p id=\"content1\">Contents 1 ...</p>\n        </Portal>\n\n        <Portal v-if=\"renderB\">\n          <p id=\"content2\">Contents 2 ...</p>\n        </Portal>\n      </main>\n    `,\n    setup() {\n      let renderA = ref(false)\n      let renderB = ref(false)\n\n      return {\n        renderA,\n        renderB,\n        toggleA() {\n          renderA.value = !renderA.value\n        },\n        toggleB() {\n          renderB.value = !renderB.value\n        },\n      }\n    },\n  })\n\n  let a = document.getElementById('a')\n  let b = document.getElementById('b')\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Let's render the first Portal\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(1)\n\n  // Let's render the second Portal\n  await click(b)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(2)\n\n  // Let's remove the first portal\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(1)\n\n  // Let's remove the second Portal\n  await click(b)\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Let's render the first Portal again\n  await click(a)\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(1)\n})\n\nit('should be possible to render multiple portals at the same time', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  renderTemplate({\n    template: html`\n      <main id=\"parent\">\n        <button id=\"a\" @click=\"toggleA\">Toggle A</button>\n        <button id=\"b\" @click=\"toggleB\">Toggle B</button>\n        <button id=\"c\" @click=\"toggleC\">Toggle C</button>\n\n        <button id=\"double\" @click=\"toggleAB\">Toggle A & B</button>\n\n        <Portal v-if=\"renderA\">\n          <p id=\"content1\">Contents 1 ...</p>\n        </Portal>\n\n        <Portal v-if=\"renderB\">\n          <p id=\"content2\">Contents 2 ...</p>\n        </Portal>\n\n        <Portal v-if=\"renderC\">\n          <p id=\"content3\">Contents 3 ...</p>\n        </Portal>\n      </main>\n    `,\n    setup() {\n      let renderA = ref(true)\n      let renderB = ref(true)\n      let renderC = ref(true)\n\n      return {\n        renderA,\n        renderB,\n        renderC,\n        toggleA() {\n          renderA.value = !renderA.value\n        },\n        toggleB() {\n          renderB.value = !renderB.value\n        },\n        toggleC() {\n          renderC.value = !renderC.value\n        },\n        toggleAB() {\n          renderA.value = !renderA.value\n          renderB.value = !renderB.value\n        },\n      }\n    },\n  })\n\n  await nextTick()\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(3)\n\n  // Remove Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot().children).toHaveLength(2)\n\n  // Remove Portal 2\n  await click(document.getElementById('b'))\n  expect(getPortalRoot().children).toHaveLength(1)\n\n  // Re-add Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot().children).toHaveLength(2)\n\n  // Remove Portal 3\n  await click(document.getElementById('c'))\n  expect(getPortalRoot().children).toHaveLength(1)\n\n  // Remove Portal 1\n  await click(document.getElementById('a'))\n  expect(getPortalRoot()).toBe(null)\n\n  // Render A and B at the same time!\n  await click(document.getElementById('double'))\n  expect(getPortalRoot().children).toHaveLength(2)\n})\n\nit('should be possible to tamper with the modal root and restore correctly', async () => {\n  expect(getPortalRoot()).toBe(null)\n\n  renderTemplate({\n    template: html`\n      <main id=\"parent\">\n        <button id=\"a\" @click=\"toggleA\">Toggle A</button>\n        <button id=\"b\" @click=\"toggleB\">Toggle B</button>\n\n        <Portal v-if=\"renderA\">\n          <p id=\"content1\">Contents 1 ...</p>\n        </Portal>\n\n        <Portal v-if=\"renderB\">\n          <p id=\"content2\">Contents 2 ...</p>\n        </Portal>\n      </main>\n    `,\n    setup() {\n      let renderA = ref(true)\n      let renderB = ref(true)\n\n      return {\n        renderA,\n        renderB,\n        toggleA() {\n          renderA.value = !renderA.value\n        },\n        toggleB() {\n          renderB.value = !renderB.value\n        },\n      }\n    },\n  })\n\n  expect(getPortalRoot()).not.toBe(null)\n\n  // Tamper tamper\n  document.body.removeChild(document.getElementById('headlessui-portal-root')!)\n\n  // Hide Portal 1 and 2\n  await click(document.getElementById('a'))\n  await click(document.getElementById('b'))\n\n  expect(getPortalRoot()).toBe(null)\n\n  // Re-show Portal 1 and 2\n  await click(document.getElementById('a'))\n  await click(document.getElementById('b'))\n\n  expect(getPortalRoot()).not.toBe(null)\n  expect(getPortalRoot().children).toHaveLength(2)\n})\n\nit('should be possible to force the Portal into a specific element using PortalGroup', async () => {\n  renderTemplate({\n    template: html`\n      <main>\n        <aside ref=\"container\" id=\"group-1\">A</aside>\n\n        <PortalGroup :target=\"container\">\n          <section id=\"group-2\">\n            <span>B</span>\n          </section>\n          <Portal>Next to A</Portal>\n        </PortalGroup>\n      </main>\n    `,\n    setup() {\n      let container = ref(null)\n      return { container }\n    },\n  })\n\n  await new Promise<void>(nextTick)\n\n  expect(document.body.innerHTML).toMatchSnapshot()\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/portal/portal.ts",
    "content": "import {\n  Teleport,\n  computed,\n  defineComponent,\n  getCurrentInstance,\n  h,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  reactive,\n  ref,\n  watch,\n  watchEffect,\n  type InjectionKey,\n  type PropType,\n  type Ref,\n} from 'vue'\nimport { usePortalRoot } from '../../internal/portal-force-root'\nimport { dom } from '../../utils/dom'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { render } from '../../utils/render'\n\ntype ContextType<T> = T extends InjectionKey<infer V> ? V : never\n\n// ---\n\nfunction getPortalRoot(contextElement?: HTMLElement | null) {\n  let ownerDocument = getOwnerDocument(contextElement)\n  if (!ownerDocument) {\n    if (contextElement === null) {\n      return null\n    }\n\n    throw new Error(\n      `[Headless UI]: Cannot find ownerDocument for contextElement: ${contextElement}`\n    )\n  }\n  let existingRoot = ownerDocument.getElementById('headlessui-portal-root')\n  if (existingRoot) return existingRoot\n\n  let root = ownerDocument.createElement('div')\n  root.setAttribute('id', 'headlessui-portal-root')\n  return ownerDocument.body.appendChild(root)\n}\n\nexport let Portal = defineComponent({\n  name: 'Portal',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n  },\n  setup(props, { slots, attrs }) {\n    let element = ref<HTMLElement | null>(null)\n    let ownerDocument = computed(() => getOwnerDocument(element))\n\n    let forcePortalRoot = usePortalRoot()\n    let groupContext = inject(PortalGroupContext, null)\n    let myTarget = ref(\n      forcePortalRoot === true\n        ? getPortalRoot(element.value)\n        : groupContext == null\n          ? getPortalRoot(element.value)\n          : groupContext.resolveTarget()\n    )\n\n    let ready = ref(false)\n    onMounted(() => {\n      ready.value = true\n    })\n\n    watchEffect(() => {\n      if (forcePortalRoot) return\n      if (groupContext == null) return\n      myTarget.value = groupContext.resolveTarget()\n    })\n\n    let parent = inject(PortalParentContext, null)\n\n    // Since the element is mounted lazily (because of SSR hydration)\n    // We use `watch` on `element` + a local var rather than\n    // `onMounted` to ensure registration only happens once\n    let didRegister = false\n    let instance = getCurrentInstance()\n    watch(element, () => {\n      if (didRegister) return\n      if (!parent) return\n      let domElement = dom(element)\n      if (!domElement) return\n      onUnmounted(parent.register(domElement), instance)\n      didRegister = true\n    })\n\n    onUnmounted(() => {\n      let root = ownerDocument.value?.getElementById('headlessui-portal-root')\n      if (!root) return\n      if (myTarget.value !== root) return\n\n      if (myTarget.value.children.length <= 0) {\n        myTarget.value.parentElement?.removeChild(myTarget.value)\n      }\n    })\n\n    return () => {\n      if (!ready.value) return null\n      if (myTarget.value === null) return null\n\n      let ourProps = {\n        ref: element,\n        'data-headlessui-portal': '',\n      }\n\n      return h(\n        // @ts-expect-error Children can be an object, but TypeScript is not happy\n        // with it. Once this is fixed upstream we can remove this assertion.\n        Teleport,\n        { to: myTarget.value },\n        render({\n          ourProps,\n          theirProps: props,\n          slot: {},\n          attrs,\n          slots,\n          name: 'Portal',\n        })\n      )\n    }\n  },\n})\n\n// ---\n\nlet PortalParentContext = Symbol('PortalParentContext') as InjectionKey<{\n  register: (portal: HTMLElement) => () => void\n  unregister: (portal: HTMLElement) => void\n  portals: Ref<HTMLElement[]>\n}>\n\nexport function useNestedPortals() {\n  let parent = inject(PortalParentContext, null)\n  let portals = ref<HTMLElement[]>([])\n\n  function register(portal: HTMLElement) {\n    portals.value.push(portal)\n    if (parent) parent.register(portal)\n    return () => unregister(portal)\n  }\n\n  function unregister(portal: HTMLElement) {\n    let idx = portals.value.indexOf(portal)\n    if (idx !== -1) portals.value.splice(idx, 1)\n    if (parent) parent.unregister(portal)\n  }\n\n  let api = {\n    register,\n    unregister,\n    portals,\n  } as ContextType<typeof PortalParentContext>\n\n  return [\n    portals,\n    defineComponent({\n      name: 'PortalWrapper',\n      setup(_, { slots }) {\n        provide(PortalParentContext, api)\n        return () => slots.default?.()\n      },\n    }),\n  ] as const\n}\n\n// ---\n\nlet PortalGroupContext = Symbol('PortalGroupContext') as InjectionKey<{\n  resolveTarget(): HTMLElement | null\n}>\n\nexport let PortalGroup = defineComponent({\n  name: 'PortalGroup',\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    target: { type: Object as PropType<HTMLElement | null>, default: null },\n  },\n  setup(props, { attrs, slots }) {\n    let api = reactive({\n      resolveTarget() {\n        return props.target\n      },\n    })\n\n    provide(PortalGroupContext, api)\n\n    return () => {\n      let { target: _, ...theirProps } = props\n\n      return render({\n        theirProps,\n        ourProps: {},\n        slot: {},\n        attrs,\n        slots,\n        name: 'PortalGroup',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts",
    "content": "import { defineComponent, nextTick, reactive, ref, watch } from 'vue'\nimport {\n  assertActiveElement,\n  assertFocusable,\n  assertNotFocusable,\n  assertRadioGroupLabel,\n  getByText,\n  getRadioGroupOptions,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, click, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { RadioGroup, RadioGroupDescription, RadioGroupLabel, RadioGroupOption } from './radio-group'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction nextFrame() {\n  return new Promise<void>((resolve) => {\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        resolve()\n      })\n    })\n  })\n}\n\nconst renderTemplate = createRenderTemplate({\n  RadioGroup,\n  RadioGroupOption,\n  RadioGroupLabel,\n  RadioGroupDescription,\n})\n\ndescribe('Safe guards', () => {\n  it.each([['RadioGroupOption', RadioGroupOption]])(\n    'should error when we are using a <%s /> without a parent <RadioGroup />',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <RadioGroup /> component.`\n      )\n    })\n  )\n\n  it(\n    'should be possible to render a RadioGroup without crashing',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup() {\n          let deliveryMethod = ref(undefined)\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertRadioGroupLabel({ textContent: 'Pizza Delivery' })\n    })\n  )\n\n  it('should be possible to render a RadioGroup without options and without crashing', () => {\n    renderTemplate({\n      template: html` <RadioGroup v-model=\"deliveryMethod\" /> `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        return { deliveryMethod }\n      },\n    })\n  })\n})\n\ndescribe('Rendering', () => {\n  it('should be possible to render a RadioGroup, where the first element is tabbable (value is undefined)', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        return { deliveryMethod }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    expect(getRadioGroupOptions()).toHaveLength(3)\n\n    assertFocusable(getByText('Pickup'))\n    assertNotFocusable(getByText('Home delivery'))\n    assertNotFocusable(getByText('Dine in'))\n  })\n\n  it('should be possible to render a RadioGroup, where the first element is tabbable (value is null)', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(null)\n        return { deliveryMethod }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    expect(getRadioGroupOptions()).toHaveLength(3)\n\n    assertFocusable(getByText('Pickup'))\n    assertNotFocusable(getByText('Home delivery'))\n    assertNotFocusable(getByText('Dine in'))\n  })\n\n  it('should be possible to render a RadioGroup with an active value', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref('home-delivery')\n        return { deliveryMethod }\n      },\n    })\n\n    expect(getRadioGroupOptions()).toHaveLength(3)\n\n    assertNotFocusable(getByText('Pickup'))\n    assertFocusable(getByText('Home delivery'))\n    assertNotFocusable(getByText('Dine in'))\n  })\n\n  it('should guarantee the radio option order after a few unmounts', async () => {\n    renderTemplate({\n      template: html`\n        <button @click=\"showFirst = !showFirst\">Toggle</button>\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption v-if=\"showFirst\" value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let showFirst = ref(false)\n        let deliveryMethod = ref(undefined)\n        return { showFirst, deliveryMethod }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    await click(getByText('Toggle')) // Render the pickup again\n\n    await press(Keys.Tab) // Focus first element\n    assertActiveElement(getByText('Pickup'))\n\n    await press(Keys.ArrowUp) // Loop around\n    assertActiveElement(getByText('Dine in'))\n\n    await press(Keys.ArrowUp) // Up again\n    assertActiveElement(getByText('Home delivery'))\n  })\n\n  it('should be possible to render a RadioGroupOption with a render prop', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\" v-slot=\"data\"\n            >Pickup - {{JSON.stringify(data)}}</RadioGroupOption\n          >\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        return { deliveryMethod }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    expect(document.querySelector('[id^=\"headlessui-radiogroup-option-\"]')).toHaveTextContent(\n      `Pickup - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n  })\n\n  it('should set the checked v-slot info to true for the selected item (testing with objects, because Vue proxies)', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption v-for=\"option in options\" key=\"option.id\" :value=\"option\" v-slot=\"data\"\n            >{{option.label}} - {{JSON.stringify(data)}}</RadioGroupOption\n          >\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        let options = ref([\n          { id: 1, label: 'Pickup' },\n          { id: 2, label: 'Home delivery' },\n          { id: 3, label: 'Dine in' },\n        ])\n        return { deliveryMethod, options }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    let [pickup, homeDelivery, dineIn] = Array.from(\n      document.querySelectorAll('[id^=\"headlessui-radiogroup-option-\"]')\n    )\n    expect(pickup).toHaveTextContent(\n      `Pickup - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n    expect(homeDelivery).toHaveTextContent(\n      `Home delivery - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n    expect(dineIn).toHaveTextContent(\n      `Dine in - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n\n    await click(homeDelivery)\n    ;[pickup, homeDelivery, dineIn] = Array.from(\n      document.querySelectorAll('[id^=\"headlessui-radiogroup-option-\"]')\n    )\n\n    expect(pickup).toHaveTextContent(\n      `Pickup - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n    expect(homeDelivery).toHaveTextContent(\n      `Home delivery - ${JSON.stringify({ checked: true, disabled: false, active: true })}`\n    )\n    expect(dineIn).toHaveTextContent(\n      `Dine in - ${JSON.stringify({ checked: false, disabled: false, active: false })}`\n    )\n  })\n\n  it('should be possible to put classes on a RadioGroup', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\" as=\"div\" class=\"abc\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption v-for=\"option in options\" key=\"option.id\" :value=\"option\" v-slot=\"data\"\n            >{{option.label}}</RadioGroupOption\n          >\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        let options = ref([{ id: 1, label: 'Pickup' }])\n        return { deliveryMethod, options }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    expect(document.querySelector('[id^=\"headlessui-radiogroup-\"]')).toHaveClass('abc')\n  })\n\n  it('should be possible to put classes on a RadioGroupOption', async () => {\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption\n            v-for=\"option in options\"\n            key=\"option.id\"\n            :value=\"option\"\n            v-slot=\"data\"\n            class=\"abc\"\n            >{{option.label}}</RadioGroupOption\n          >\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        let options = ref([{ id: 1, label: 'Pickup' }])\n        return { deliveryMethod, options }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    expect(getByText('Pickup')).toHaveClass('abc')\n  })\n\n  it('should be possible to disable a RadioGroup', async () => {\n    let changeFn = jest.fn()\n    renderTemplate({\n      template: html`\n        <button @click=\"disabled = !disabled\">Toggle</button>\n        <RadioGroup v-model=\"deliveryMethod\" :disabled=\"disabled\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          <RadioGroupOption value=\"render-prop\" data-value=\"render-prop\" v-slot=\"data\">\n            {{JSON.stringify(data)}}\n          </RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        let disabled = ref(true)\n        watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n        return { deliveryMethod, disabled }\n      },\n    })\n\n    // Try to click one a few options\n    await click(getByText('Pickup'))\n    await click(getByText('Dine in'))\n\n    // Verify that the RadioGroup.Option gets the disabled state\n    expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n      JSON.stringify({\n        checked: false,\n        disabled: true,\n        active: false,\n      })\n    )\n\n    // Make sure that the onChange handler never got called\n    expect(changeFn).toHaveBeenCalledTimes(0)\n\n    // Make sure that all the options get an `aria-disabled`\n    let options = getRadioGroupOptions()\n    expect(options).toHaveLength(4)\n    for (let option of options) expect(option).toHaveAttribute('aria-disabled', 'true')\n\n    // Toggle the disabled state\n    await click(getByText('Toggle'))\n\n    // Verify that the RadioGroup.Option gets the disabled state\n    expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n      JSON.stringify({\n        checked: false,\n        disabled: false,\n        active: false,\n      })\n    )\n\n    // Try to click one a few options\n    await click(getByText('Pickup'))\n\n    // Make sure that the onChange handler got called\n    expect(changeFn).toHaveBeenCalledTimes(1)\n  })\n\n  it('should be possible to disable a RadioGroup.Option', async () => {\n    let changeFn = jest.fn()\n    renderTemplate({\n      template: html`\n        <button @click=\"disabled = !disabled\">Toggle</button>\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          <RadioGroupOption\n            value=\"render-prop\"\n            :disabled=\"disabled\"\n            data-value=\"render-prop\"\n            v-slot=\"data\"\n          >\n            {{JSON.stringify(data)}}\n          </RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        let deliveryMethod = ref(undefined)\n        let disabled = ref(true)\n        watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n        return { deliveryMethod, disabled }\n      },\n    })\n\n    // Try to click the disabled option\n    await click(document.querySelector('[data-value=\"render-prop\"]'))\n\n    // Verify that the RadioGroup.Option gets the disabled state\n    expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n      JSON.stringify({\n        checked: false,\n        disabled: true,\n        active: false,\n      })\n    )\n\n    // Make sure that the onChange handler never got called\n    expect(changeFn).toHaveBeenCalledTimes(0)\n\n    // Make sure that the option with value \"render-prop\" gets an `aria-disabled`\n    let options = getRadioGroupOptions()\n    expect(options).toHaveLength(4)\n    for (let option of options) {\n      if (option.dataset.value) {\n        expect(option).toHaveAttribute('aria-disabled', 'true')\n      } else {\n        expect(option).not.toHaveAttribute('aria-disabled')\n      }\n    }\n\n    // Toggle the disabled state\n    await click(getByText('Toggle'))\n\n    // Verify that the RadioGroup.Option gets the disabled state\n    expect(document.querySelector('[data-value=\"render-prop\"]')).toHaveTextContent(\n      JSON.stringify({\n        checked: false,\n        disabled: false,\n        active: false,\n      })\n    )\n\n    // Try to click one a few options\n    await click(document.querySelector('[data-value=\"render-prop\"]'))\n\n    // Make sure that the onChange handler got called\n    expect(changeFn).toHaveBeenCalledTimes(1)\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    let props = reactive({ hide: false })\n\n    renderTemplate({\n      template: html`\n        <RadioGroup v-model=\"value\">\n          <RadioGroupOption value=\"a\">Option 1</RadioGroupOption>\n          <RadioGroupOption v-if=\"!hide\" value=\"b\">Option 2</RadioGroupOption>\n          <RadioGroupOption value=\"c\">Option 3</RadioGroupOption>\n        </RadioGroup>\n      `,\n      setup() {\n        return {\n          value: ref('a'),\n          get hide() {\n            return props.hide\n          },\n        }\n      },\n    })\n\n    // Focus the RadioGroup\n    await press(Keys.Tab)\n\n    props.hide = true\n    await nextFrame()\n\n    props.hide = false\n    await nextFrame()\n\n    // Verify that the first radio group option is active\n    assertActiveElement(getByText('Option 1'))\n\n    await press(Keys.ArrowDown)\n    // Verify that the second radio group option is active\n    assertActiveElement(getByText('Option 2'))\n\n    await press(Keys.ArrowDown)\n    // Verify that the third radio group option is active\n    assertActiveElement(getByText('Option 3'))\n  })\n\n  it(\n    'should be possible to use a custom component using the `as` prop without crashing',\n    suppressConsoleLogs(async () => {\n      let CustomComponent = defineComponent({\n        template: html`<button><slot /></button>`,\n      })\n\n      renderTemplate({\n        template: html`\n          <RadioGroup name=\"assignee\">\n            <RadioGroupOption :as=\"CustomComponent\" value=\"alice\">Alice</RadioGroupOption>\n            <RadioGroupOption :as=\"CustomComponent\" value=\"bob\">Bob</RadioGroupOption>\n            <RadioGroupOption :as=\"CustomComponent\" value=\"charlie\">Charlie</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup: () => ({ CustomComponent }),\n      })\n    })\n  )\n\n  describe('Equality', () => {\n    let options = [\n      { id: 1, name: 'Alice' },\n      { id: 2, name: 'Bob' },\n      { id: 3, name: 'Charlie' },\n    ]\n\n    it(\n      'should use object equality by default',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <RadioGroup v-model=\"value\">\n              <RadioGroupButton>Trigger</RadioGroupButton>\n              <RadioGroupOption\n                v-for=\"option in options\"\n                :key=\"option.id\"\n                :value=\"option\"\n                v-slot=\"data\"\n                >{{ JSON.stringify(data) }}</RadioGroupOption\n              >\n            </RadioGroup>\n          `,\n          setup: () => {\n            let value = ref(options[1])\n            return { options, value }\n          },\n        })\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveTextContent(\n          JSON.stringify({ checked: true, disabled: false, active: false })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare null values by a field',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <RadioGroup v-model=\"value\" by=\"id\">\n              <RadioGroupButton>Trigger</RadioGroupButton>\n              <RadioGroupOption\n                v-for=\"option in options\"\n                :key=\"option.id\"\n                :value=\"option\"\n                v-slot=\"data\"\n                >{{ JSON.stringify(data) }}</RadioGroupOption\n              >\n            </RadioGroup>\n          `,\n          setup: () => {\n            let value = ref(null)\n            return { options, value }\n          },\n        })\n\n        let [alice, bob, charlie] = getRadioGroupOptions()\n        expect(alice).toHaveTextContent(\n          JSON.stringify({ checked: false, disabled: false, active: false })\n        )\n        expect(bob).toHaveTextContent(\n          JSON.stringify({ checked: false, disabled: false, active: false })\n        )\n        expect(charlie).toHaveTextContent(\n          JSON.stringify({ checked: false, disabled: false, active: false })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare objects by a field',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <RadioGroup v-model=\"value\" by=\"id\">\n              <RadioGroupButton>Trigger</RadioGroupButton>\n              <RadioGroupOption\n                v-for=\"option in options\"\n                :key=\"option.id\"\n                :value=\"option\"\n                v-slot=\"data\"\n                >{{ JSON.stringify(data) }}</RadioGroupOption\n              >\n            </RadioGroup>\n          `,\n          setup: () => {\n            let value = ref({ id: 2, name: 'Bob' })\n            return { options, value }\n          },\n        })\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveTextContent(\n          JSON.stringify({ checked: true, disabled: false, active: false })\n        )\n      })\n    )\n\n    it(\n      'should be possible to compare objects by a comparator function',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <RadioGroup v-model=\"value\" :by=\"compare\">\n              <RadioGroupButton>Trigger</RadioGroupButton>\n              <RadioGroupOption\n                v-for=\"option in options\"\n                :key=\"option.id\"\n                :value=\"option\"\n                v-slot=\"data\"\n                >{{ JSON.stringify(data) }}</RadioGroupOption\n              >\n            </RadioGroup>\n          `,\n          setup: () => {\n            let value = ref({ id: 2, name: 'Bob' })\n            return { options, value, compare: (a: any, z: any) => a.id === z.id }\n          },\n        })\n\n        let bob = getRadioGroupOptions()[1]\n        expect(bob).toHaveTextContent(\n          JSON.stringify({ checked: true, disabled: false, active: false })\n        )\n      })\n    )\n  })\n\n  describe('Uncontrolled', () => {\n    it(\n      'should be possible to use in an uncontrolled way',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <RadioGroup name=\"assignee\">\n                <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n                <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n                <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n              </RadioGroup>\n              <button id=\"submit\">submit</button>\n            </form>\n          `,\n          setup: () => ({\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // No values\n        expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Alice should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n        // Choose charlie\n        await click(getRadioGroupOptions()[2])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Charlie should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'charlie' })\n      })\n    )\n\n    it(\n      'should be possible to provide a default value',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <RadioGroup name=\"assignee\" defaultValue=\"bob\">\n                <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n                <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n                <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n              </RadioGroup>\n              <button id=\"submit\">submit</button>\n            </form>\n          `,\n          setup: () => ({\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Submit\n        await click(document.getElementById('submit'))\n\n        // Alice should be submitted\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <RadioGroup name=\"assignee\" defaultValue=\"bob\">\n                <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n                <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n                <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n              </RadioGroup>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        // Bob is the defaultValue\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Alice is now chosen\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'alice' })\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // Bob should be submitted again\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n      })\n    )\n\n    it(\n      'should be possible to reset to the default value if the form is reset (using objects)',\n      suppressConsoleLogs(async () => {\n        let handleSubmission = jest.fn()\n\n        let data = [\n          { id: 1, name: 'alice', label: 'Alice' },\n          { id: 2, name: 'bob', label: 'Bob' },\n          { id: 3, name: 'charlie', label: 'Charlie' },\n        ]\n\n        renderTemplate({\n          template: html`\n            <form @submit=\"handleSubmit\">\n              <RadioGroup\n                name=\"assignee\"\n                :defaultValue=\"{ id: 2, name: 'bob', label: 'Bob' }\"\n                by=\"id\"\n              >\n                <RadioGroupOption v-for=\"person in data\" :key=\"person.id\" :value=\"person\">\n                  {{ person.label }}\n                </RadioGroupOption>\n              </RadioGroup>\n              <button id=\"submit\">submit</button>\n              <button type=\"reset\" id=\"reset\">reset</button>\n            </form>\n          `,\n          setup: () => ({\n            data,\n            handleSubmit(e: SubmitEvent) {\n              e.preventDefault()\n              handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n            },\n          }),\n        })\n\n        await click(document.getElementById('submit'))\n\n        // Bob is the defaultValue\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Alice is now chosen\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '1',\n          'assignee[name]': 'alice',\n          'assignee[label]': 'Alice',\n        })\n\n        // Reset\n        await click(document.getElementById('reset'))\n\n        // Bob should be submitted again\n        await click(document.getElementById('submit'))\n        expect(handleSubmission).toHaveBeenLastCalledWith({\n          'assignee[id]': '2',\n          'assignee[name]': 'bob',\n          'assignee[label]': 'Bob',\n        })\n      })\n    )\n\n    it(\n      'should still call the onChange listeners when choosing new values',\n      suppressConsoleLogs(async () => {\n        let handleChange = jest.fn()\n\n        renderTemplate({\n          template: html`\n            <RadioGroup name=\"assignee\" @update:modelValue=\"handleChange\">\n              <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n              <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n              <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n            </RadioGroup>\n          `,\n          setup: () => ({ handleChange }),\n        })\n\n        // Choose alice\n        await click(getRadioGroupOptions()[0])\n\n        // Choose bob\n        await click(getRadioGroupOptions()[1])\n\n        // Change handler should have been called twice\n        expect(handleChange).toHaveBeenNthCalledWith(1, 'alice')\n        expect(handleChange).toHaveBeenNthCalledWith(2, 'bob')\n      })\n    )\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Tab` key', () => {\n    it('should be possible to tab to the first item', async () => {\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n    })\n\n    it('should not change the selected element on focus', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to tab to the active item', async () => {\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          return { deliveryMethod }\n        },\n      })\n\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Home delivery'))\n    })\n\n    it('should not change the selected element on focus (when selecting the active item)', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to tab out of the radio group (no selected value)', async () => {\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          return { deliveryMethod }\n        },\n      })\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Before'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('After'))\n    })\n\n    it('should be possible to tab out of the radio group (selected value)', async () => {\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          return { deliveryMethod }\n        },\n      })\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Before'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Home delivery'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('After'))\n    })\n  })\n\n  describe('`Shift+Tab` key', () => {\n    it('should be possible to tab to the first item', async () => {\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n\n      assertActiveElement(getByText('Pickup'))\n    })\n\n    it('should not change the selected element on focus', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to tab to the active item', async () => {\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          return { deliveryMethod }\n        },\n      })\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n\n      assertActiveElement(getByText('Home delivery'))\n    })\n\n    it('should not change the selected element on focus (when selecting the active item)', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenCalledTimes(0)\n    })\n\n    it('should be possible to tab out of the radio group (no selected value)', async () => {\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Pickup'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Before'))\n    })\n\n    it('should be possible to tab out of the radio group (selected value)', async () => {\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref('home-delivery')\n          return { deliveryMethod }\n        },\n      })\n\n      getByText('After')?.focus()\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Home delivery'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Before'))\n    })\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it('should go to the previous item when pressing the ArrowLeft key', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.ArrowLeft) // Loop around\n      assertActiveElement(getByText('Dine in'))\n\n      await press(Keys.ArrowLeft)\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenCalledTimes(2)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'dine-in')\n      expect(changeFn).toHaveBeenNthCalledWith(2, 'home-delivery')\n    })\n  })\n\n  describe('`ArrowUp` key', () => {\n    it('should go to the previous item when pressing the ArrowUp key', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.ArrowUp) // Loop around\n      assertActiveElement(getByText('Dine in'))\n\n      await press(Keys.ArrowUp)\n      assertActiveElement(getByText('Home delivery'))\n\n      expect(changeFn).toHaveBeenCalledTimes(2)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'dine-in')\n      expect(changeFn).toHaveBeenNthCalledWith(2, 'home-delivery')\n    })\n  })\n\n  describe('`ArrowRight` key', () => {\n    it('should go to the next item when pressing the ArrowRight key', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.ArrowRight)\n      assertActiveElement(getByText('Home delivery'))\n\n      await press(Keys.ArrowRight)\n      assertActiveElement(getByText('Dine in'))\n\n      await press(Keys.ArrowRight) // Loop around\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(3)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n      expect(changeFn).toHaveBeenNthCalledWith(2, 'dine-in')\n      expect(changeFn).toHaveBeenNthCalledWith(3, 'pickup')\n    })\n  })\n\n  describe('`ArrowDown` key', () => {\n    it('should go to the next item when pressing the ArrowDown key', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.ArrowDown)\n      assertActiveElement(getByText('Home delivery'))\n\n      await press(Keys.ArrowDown)\n      assertActiveElement(getByText('Dine in'))\n\n      await press(Keys.ArrowDown) // Loop around\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(3)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n      expect(changeFn).toHaveBeenNthCalledWith(2, 'dine-in')\n      expect(changeFn).toHaveBeenNthCalledWith(3, 'pickup')\n    })\n  })\n\n  describe('`Space` key', () => {\n    it('should select the current option when pressing space', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.Space)\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(1)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'pickup')\n    })\n\n    it('should select the current option only once when pressing space', async () => {\n      let changeFn = jest.fn()\n      renderTemplate({\n        template: html`\n          <button>Before</button>\n          <RadioGroup v-model=\"deliveryMethod\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>After</button>\n        `,\n        setup() {\n          let deliveryMethod = ref()\n          watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n          return { deliveryMethod }\n        },\n      })\n\n      // Focus the \"Before\" button\n      await press(Keys.Tab)\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      assertActiveElement(getByText('Pickup'))\n\n      await press(Keys.Space)\n      await press(Keys.Space)\n      await press(Keys.Space)\n      await press(Keys.Space)\n      await press(Keys.Space)\n      assertActiveElement(getByText('Pickup'))\n\n      expect(changeFn).toHaveBeenCalledTimes(1)\n      expect(changeFn).toHaveBeenNthCalledWith(1, 'pickup')\n    })\n  })\n\n  describe('`Enter`', () => {\n    it('should submit the form on `Enter`', async () => {\n      let submits = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <RadioGroup v-model=\"value\" name=\"option\">\n              <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n              <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n              <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n            </RadioGroup>\n            <button>Submit</button>\n          </form>\n        `,\n        setup() {\n          let value = ref('bob')\n          return {\n            value,\n            handleSubmit(event: KeyboardEvent) {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n            },\n          }\n        },\n      })\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'bob']])\n    })\n\n    it('should submit the form on `Enter` (when no submit button was found)', async () => {\n      let submits = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <RadioGroup v-model=\"value\" name=\"option\">\n              <RadioGroupOption value=\"alice\">Alice</RadioGroupOption>\n              <RadioGroupOption value=\"bob\">Bob</RadioGroupOption>\n              <RadioGroupOption value=\"charlie\">Charlie</RadioGroupOption>\n            </RadioGroup>\n          </form>\n        `,\n        setup() {\n          let value = ref('bob')\n          return {\n            value,\n            handleSubmit(event: KeyboardEvent) {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n            },\n          }\n        },\n      })\n\n      // Focus the RadioGroup\n      await press(Keys.Tab)\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'bob']])\n    })\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it('should be possible to change the current radio group value when clicking on a radio option', async () => {\n    let changeFn = jest.fn()\n    renderTemplate({\n      template: html`\n        <button>Before</button>\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n        <button>After</button>\n      `,\n      setup() {\n        let deliveryMethod = ref()\n        watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n        return { deliveryMethod }\n      },\n    })\n\n    await click(getByText('Home delivery'))\n\n    assertActiveElement(getByText('Home delivery'))\n\n    expect(changeFn).toHaveBeenNthCalledWith(1, 'home-delivery')\n  })\n\n  it('should be a no-op when clicking on the same item', async () => {\n    let changeFn = jest.fn()\n    renderTemplate({\n      template: html`\n        <button>Before</button>\n        <RadioGroup v-model=\"deliveryMethod\">\n          <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n          <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n          <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n          <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n        </RadioGroup>\n        <button>After</button>\n      `,\n      setup() {\n        let deliveryMethod = ref()\n        watch([deliveryMethod], () => changeFn(deliveryMethod.value))\n        return { deliveryMethod }\n      },\n    })\n\n    await click(getByText('Home delivery'))\n    await click(getByText('Home delivery'))\n    await click(getByText('Home delivery'))\n    await click(getByText('Home delivery'))\n\n    assertActiveElement(getByText('Home delivery'))\n\n    expect(changeFn).toHaveBeenCalledTimes(1)\n  })\n})\n\ndescribe('Form compatibility', () => {\n  it(\n    'should be possible to set the `form`, which is forwarded to the hidden inputs',\n    suppressConsoleLogs(async () => {\n      let submits = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <div>\n            <RadioGroup form=\"my-form\" v-model=\"deliveryMethod\" name=\"delivery\">\n              <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n              <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n              <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n              <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n            </RadioGroup>\n            <form id=\"my-form\" @submit=\"handleSubmit\">\n              <button>Submit</button>\n            </form>\n          </div>\n        `,\n        setup() {\n          let deliveryMethod = ref(null)\n          return {\n            deliveryMethod,\n            handleSubmit(event: SubmitEvent) {\n              event.preventDefault()\n\n              submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n            },\n          }\n        },\n      })\n\n      // Choose pickup\n      await click(getByText('Pickup'))\n\n      // Submit the form\n      await click(getByText('Submit'))\n\n      expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n    })\n  )\n\n  it('should be possible to submit a form with a value', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <RadioGroup v-model=\"deliveryMethod\" name=\"delivery\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup() {\n        let deliveryMethod = ref(null)\n        return {\n          deliveryMethod,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'home-delivery']])\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])\n  })\n\n  it('should not submit the data if the RadioGroup is disabled', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <RadioGroup v-model=\"value\" name=\"delivery\" disabled>\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption value=\"pickup\">Pickup</RadioGroupOption>\n            <RadioGroupOption value=\"home-delivery\">Home delivery</RadioGroupOption>\n            <RadioGroupOption value=\"dine-in\">Dine in</RadioGroupOption>\n          </RadioGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let value = ref('home-delivery')\n        return {\n          value,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n\n  it('should be possible to submit a form with a complex value object', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <RadioGroup v-model=\"deliveryMethod\" name=\"delivery\">\n            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>\n            <RadioGroupOption v-for=\"option in options\" :key=\"option.id\" :value=\"option\"\n              >{{ option.label }}</RadioGroupOption\n            >\n          </RadioGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup() {\n        let options = ref([\n          {\n            id: 1,\n            value: 'pickup',\n            label: 'Pickup',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 2,\n            value: 'home-delivery',\n            label: 'Home delivery',\n            extra: { info: 'Some extra info' },\n          },\n          {\n            id: 3,\n            value: 'dine-in',\n            label: 'Dine in',\n            extra: { info: 'Some extra info' },\n          },\n        ])\n        let deliveryMethod = ref(options.value[0])\n\n        return {\n          deliveryMethod,\n          options,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Choose home delivery\n    await click(getByText('Home delivery'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '2'],\n      ['delivery[value]', 'home-delivery'],\n      ['delivery[label]', 'Home delivery'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n\n    // Choose pickup\n    await click(getByText('Pickup'))\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['delivery[id]', '1'],\n      ['delivery[value]', 'pickup'],\n      ['delivery[label]', 'Pickup'],\n      ['delivery[extra][info]', 'Some extra info'],\n    ])\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/radio-group/radio-group.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  toRaw,\n  watch,\n  type InjectionKey,\n  type Ref,\n  type UnwrapRef,\n} from 'vue'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useId } from '../../hooks/use-id'\nimport { useTreeWalker } from '../../hooks/use-tree-walker'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport { Focus, FocusResult, focusIn, sortByDomNode } from '../../utils/focus-management'\nimport { attemptSubmit, objectToFormEntries } from '../../utils/form'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { compact, omit, render } from '../../utils/render'\nimport { Description, useDescriptions } from '../description/description'\nimport { Label, useLabels } from '../label/label'\n\nfunction defaultComparator<T>(a: T, z: T): boolean {\n  return a === z\n}\n\ninterface Option {\n  id: string\n  element: Ref<HTMLElement | null>\n  propsRef: Ref<{ value: unknown; disabled: boolean }>\n}\n\ninterface StateDefinition {\n  // State\n  options: Ref<Option[]>\n  value: Ref<unknown>\n  disabled: Ref<boolean>\n  firstOption: Ref<Option | undefined>\n  containsCheckedOption: Ref<boolean>\n\n  compare(a: unknown, z: unknown): boolean\n\n  // State mutators\n  change(nextValue: unknown): boolean\n  registerOption(action: Option): void\n  unregisterOption(id: Option['id']): void\n}\n\nlet RadioGroupContext = Symbol('RadioGroupContext') as InjectionKey<StateDefinition>\n\nfunction useRadioGroupContext(component: string) {\n  let context = inject(RadioGroupContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <RadioGroup /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useRadioGroupContext)\n    throw err\n  }\n\n  return context\n}\n\n// ---\n\nexport let RadioGroup = defineComponent({\n  name: 'RadioGroup',\n  emits: { 'update:modelValue': (_value: any) => true },\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    disabled: { type: [Boolean], default: false },\n    by: { type: [String, Function], default: () => defaultComparator },\n    modelValue: { type: [Object, String, Number, Boolean], default: undefined },\n    defaultValue: { type: [Object, String, Number, Boolean], default: undefined },\n    form: { type: String, optional: true },\n    name: { type: String, optional: true },\n    id: { type: String, default: () => `headlessui-radiogroup-${useId()}` },\n  },\n  inheritAttrs: false,\n  setup(props, { emit, attrs, slots, expose }) {\n    let radioGroupRef = ref<HTMLElement | null>(null)\n    let options = ref<StateDefinition['options']['value']>([])\n    let labelledby = useLabels({ name: 'RadioGroupLabel' })\n    let describedby = useDescriptions({ name: 'RadioGroupDescription' })\n\n    expose({ el: radioGroupRef, $el: radioGroupRef })\n\n    let [value, theirOnChange] = useControllable(\n      computed(() => props.modelValue),\n      (value: unknown) => emit('update:modelValue', value),\n      computed(() => props.defaultValue)\n    )\n\n    // TODO: Fix type\n    let api: any = {\n      options,\n      value,\n      disabled: computed(() => props.disabled),\n      firstOption: computed(() =>\n        options.value.find((option) => {\n          if (option.propsRef.disabled) return false\n          return true\n        })\n      ),\n      containsCheckedOption: computed(() =>\n        options.value.some((option) =>\n          api.compare(toRaw(option.propsRef.value), toRaw(props.modelValue))\n        )\n      ),\n      compare(a: any, z: any) {\n        if (typeof props.by === 'string') {\n          let property = props.by as unknown as any\n          return a?.[property] === z?.[property]\n        }\n        return props.by(a, z)\n      },\n      change(nextValue: unknown) {\n        if (props.disabled) return false\n        if (api.compare(toRaw(value.value), toRaw(nextValue))) return false\n        let nextOption = options.value.find((option) =>\n          api.compare(toRaw(option.propsRef.value), toRaw(nextValue))\n        )?.propsRef\n        if (nextOption?.disabled) return false\n        theirOnChange(nextValue)\n        return true\n      },\n      registerOption(action: UnwrapRef<Option>) {\n        options.value.push(action)\n        options.value = sortByDomNode(options.value, (option) => option.element)\n      },\n      unregisterOption(id: Option['id']) {\n        let idx = options.value.findIndex((radio) => radio.id === id)\n        if (idx === -1) return\n        options.value.splice(idx, 1)\n      },\n    }\n\n    provide(RadioGroupContext, api)\n\n    useTreeWalker({\n      container: computed(() => dom(radioGroupRef)),\n      accept(node) {\n        if (node.getAttribute('role') === 'radio') return NodeFilter.FILTER_REJECT\n        if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP\n        return NodeFilter.FILTER_ACCEPT\n      },\n      walk(node) {\n        node.setAttribute('role', 'none')\n      },\n    })\n\n    function handleKeyDown(event: KeyboardEvent) {\n      if (!radioGroupRef.value) return\n      if (!radioGroupRef.value.contains(event.target as HTMLElement)) return\n\n      let all = options.value\n        .filter((option) => option.propsRef.disabled === false)\n        .map((radio) => radio.element) as HTMLElement[]\n\n      switch (event.key) {\n        case Keys.Enter:\n          attemptSubmit(event.currentTarget as unknown as EventTarget & HTMLButtonElement)\n          break\n        case Keys.ArrowLeft:\n        case Keys.ArrowUp:\n          {\n            event.preventDefault()\n            event.stopPropagation()\n\n            let result = focusIn(all, Focus.Previous | Focus.WrapAround)\n\n            if (result === FocusResult.Success) {\n              let activeOption = options.value.find(\n                (option) => option.element === getOwnerDocument(radioGroupRef)?.activeElement\n              )\n              if (activeOption) api.change(activeOption.propsRef.value)\n            }\n          }\n          break\n\n        case Keys.ArrowRight:\n        case Keys.ArrowDown:\n          {\n            event.preventDefault()\n            event.stopPropagation()\n\n            let result = focusIn(all, Focus.Next | Focus.WrapAround)\n\n            if (result === FocusResult.Success) {\n              let activeOption = options.value.find(\n                (option) => option.element === getOwnerDocument(option.element)?.activeElement\n              )\n              if (activeOption) api.change(activeOption.propsRef.value)\n            }\n          }\n          break\n\n        case Keys.Space:\n          {\n            event.preventDefault()\n            event.stopPropagation()\n\n            let activeOption = options.value.find(\n              (option) => option.element === getOwnerDocument(option.element)?.activeElement\n            )\n            if (activeOption) api.change(activeOption.propsRef.value)\n          }\n          break\n      }\n    }\n\n    let form = computed(() => dom(radioGroupRef)?.closest('form'))\n    onMounted(() => {\n      watch(\n        [form],\n        () => {\n          if (!form.value) return\n          if (props.defaultValue === undefined) return\n\n          function handle() {\n            api.change(props.defaultValue)\n          }\n\n          form.value.addEventListener('reset', handle)\n\n          return () => {\n            form.value?.removeEventListener('reset', handle)\n          }\n        },\n        { immediate: true }\n      )\n    })\n\n    return () => {\n      let { disabled, name, id, form, ...theirProps } = props\n\n      let ourProps = {\n        ref: radioGroupRef,\n        id,\n        role: 'radiogroup',\n        'aria-labelledby': labelledby.value,\n        'aria-describedby': describedby.value,\n        onKeydown: handleKeyDown,\n      }\n\n      return h(Fragment, [\n        ...(name != null && value.value != null\n          ? objectToFormEntries({ [name]: value.value }).map(([name, value]) =>\n              h(\n                Hidden,\n                compact({\n                  features: HiddenFeatures.Hidden,\n                  key: name,\n                  as: 'input',\n                  type: 'hidden',\n                  hidden: true,\n                  readOnly: true,\n                  form,\n                  disabled,\n                  name,\n                  value,\n                })\n              )\n            )\n          : []),\n        render({\n          ourProps,\n          theirProps: { ...attrs, ...omit(theirProps, ['modelValue', 'defaultValue', 'by']) },\n          slot: {},\n          attrs,\n          slots,\n          name: 'RadioGroup',\n        }),\n      ])\n    }\n  },\n})\n\n// ---\n\nenum OptionState {\n  Empty = 1 << 0,\n  Active = 1 << 1,\n}\n\nexport let RadioGroupOption = defineComponent({\n  name: 'RadioGroupOption',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    value: { type: [Object, String, Number, Boolean] },\n    disabled: { type: Boolean, default: false },\n    id: { type: String, default: () => `headlessui-radiogroup-option-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useRadioGroupContext('RadioGroupOption')\n    let labelledby = useLabels({ name: 'RadioGroupLabel' })\n    let describedby = useDescriptions({ name: 'RadioGroupDescription' })\n\n    let optionRef = ref<HTMLElement | null>(null)\n    let propsRef = computed(() => ({ value: props.value, disabled: props.disabled }))\n    let state = ref(OptionState.Empty)\n\n    expose({ el: optionRef, $el: optionRef })\n\n    let element = computed(() => dom(optionRef))\n    onMounted(() => api.registerOption({ id: props.id, element, propsRef }))\n    onUnmounted(() => api.unregisterOption(props.id))\n\n    let isFirstOption = computed(() => api.firstOption.value?.id === props.id)\n    let disabled = computed(() => api.disabled.value || props.disabled)\n    let checked = computed(() => api.compare(toRaw(api.value.value), toRaw(props.value)))\n    let tabIndex = computed(() => {\n      if (disabled.value) return -1\n      if (checked.value) return 0\n      if (!api.containsCheckedOption.value && isFirstOption.value) return 0\n      return -1\n    })\n\n    function handleClick() {\n      if (!api.change(props.value)) return\n\n      state.value |= OptionState.Active\n      dom(optionRef)?.focus()\n    }\n\n    function handleFocus() {\n      state.value |= OptionState.Active\n    }\n\n    function handleBlur() {\n      state.value &= ~OptionState.Active\n    }\n\n    return () => {\n      let { id, value: _value, disabled: _disabled, ...theirProps } = props\n\n      let slot = {\n        checked: checked.value,\n        disabled: disabled.value,\n        active: Boolean(state.value & OptionState.Active),\n      }\n\n      let ourProps = {\n        id,\n        ref: optionRef,\n        role: 'radio',\n        'aria-checked': checked.value ? 'true' : 'false',\n        'aria-labelledby': labelledby.value,\n        'aria-describedby': describedby.value,\n        'aria-disabled': disabled.value ? true : undefined,\n        tabIndex: tabIndex.value,\n        onClick: disabled.value ? undefined : handleClick,\n        onFocus: disabled.value ? undefined : handleFocus,\n        onBlur: disabled.value ? undefined : handleBlur,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'RadioGroupOption',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let RadioGroupLabel = Label\nexport let RadioGroupDescription = Description\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/switch/switch.test.tsx",
    "content": "import { defineComponent, h, ref, watch } from 'vue'\nimport {\n  SwitchState,\n  assertActiveElement,\n  assertSwitch,\n  getByText,\n  getSwitch,\n  getSwitchLabel,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, click, mouseEnter, press } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate } from '../../test-utils/vue-testing-library'\nimport { Switch, SwitchDescription, SwitchGroup, SwitchLabel } from './switch'\n\njest.mock('../../hooks/use-id')\n\nconst renderTemplate = createRenderTemplate({ Switch, SwitchLabel, SwitchDescription, SwitchGroup })\n\ndescribe('Safe guards', () => {\n  it('should be possible to render a Switch without crashing', () => {\n    renderTemplate({\n      template: html` <Switch v-model=\"checked\" /> `,\n      setup: () => ({ checked: ref(false) }),\n    })\n  })\n})\n\ndescribe('Rendering', () => {\n  it('should be possible to render an (on) Switch using a render prop', () => {\n    renderTemplate({\n      template: html`\n        <Switch v-model=\"checked\">\n          {({ checked }) => <span>{checked ? 'On' : 'Off'}</span>}\n        </Switch>\n      `,\n      setup: () => ({ checked: ref(true) }),\n    })\n\n    assertSwitch({ state: SwitchState.On, textContent: 'On' })\n  })\n\n  it('should be possible to render an (off) Switch using a render prop', () => {\n    renderTemplate({\n      template: html`\n        <Switch v-model=\"checked\">\n          {({ checked }) => <span>{checked ? 'On' : 'Off'}</span>}\n        </Switch>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    assertSwitch({ state: SwitchState.Off, textContent: 'Off' })\n  })\n\n  it('should be possible to render an (on) Switch using an `as` prop', () => {\n    renderTemplate({\n      template: html` <Switch as=\"span\" v-model=\"checked\" /> `,\n      setup: () => ({ checked: ref(true) }),\n    })\n    assertSwitch({ state: SwitchState.On, tag: 'span' })\n  })\n\n  it('should be possible to render an (off) Switch using an `as` prop', () => {\n    renderTemplate({\n      template: html` <Switch as=\"span\" v-model=\"checked\" /> `,\n      setup: () => ({ checked: ref(false) }),\n    })\n    assertSwitch({ state: SwitchState.Off, tag: 'span' })\n  })\n\n  it('should be possible to use the switch contents as the label', () => {\n    renderTemplate({\n      template: html`\n        <Switch v-model=\"checked\">\n          <span>Enable notifications</span>\n        </Switch>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })\n  })\n\n  describe('`type` attribute', () => {\n    it('should set the `type` to \"button\" by default', async () => {\n      renderTemplate({\n        template: html` <Switch v-model=\"checked\"> Trigger </Switch> `,\n        setup: () => ({ checked: ref(false) }),\n      })\n\n      expect(getSwitch()).toHaveAttribute('type', 'button')\n    })\n\n    it('should not set the `type` to \"button\" if it already contains a `type`', async () => {\n      renderTemplate({\n        template: html` <Switch v-model=\"checked\" type=\"submit\"> Trigger </Switch> `,\n        setup: () => ({ checked: ref(false) }),\n      })\n\n      expect(getSwitch()).toHaveAttribute('type', 'submit')\n    })\n\n    it(\n      'should set the `type` to \"button\" when using the `as` prop which resolves to a \"button\"',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html` <Switch v-model=\"checked\" :as=\"CustomButton\"> Trigger </Switch> `,\n          setup: () => ({\n            checked: ref(false),\n            CustomButton: defineComponent({\n              setup: (props) => () => h('button', { ...props }),\n            }),\n          }),\n        })\n\n        await new Promise(requestAnimationFrame)\n\n        expect(getSwitch()).toHaveAttribute('type', 'button')\n      })\n    )\n\n    it('should not set the type if the \"as\" prop is not a \"button\"', async () => {\n      renderTemplate({\n        template: html` <Switch v-model=\"checked\" as=\"div\"> Trigger </Switch> `,\n        setup: () => ({ checked: ref(false) }),\n      })\n\n      expect(getSwitch()).not.toHaveAttribute('type')\n    })\n\n    it(\n      'should not set the `type` to \"button\" when using the `as` prop which resolves to a \"div\"',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html` <Switch v-model=\"checked\" :as=\"CustomButton\"> Trigger </Switch> `,\n          setup: () => ({\n            checked: ref(false),\n            CustomButton: defineComponent({\n              setup: (props) => () => h('div', props),\n            }),\n          }),\n        })\n\n        await new Promise(requestAnimationFrame)\n\n        expect(getSwitch()).not.toHaveAttribute('type')\n      })\n    )\n  })\n\n  describe('Uncontrolled', () => {\n    it('should be possible to use in an uncontrolled way', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Switch name=\"notifications\" />\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case, non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to use in an uncontrolled way with a value', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Switch name=\"notifications\" value=\"enabled\" />\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // No values\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'enabled' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case, non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to provide a default value', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Switch name=\"notifications\" defaultChecked />\n            <button id=\"submit\">submit</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmit(e: SubmitEvent) {\n            e.preventDefault()\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      await click(document.getElementById('submit'))\n\n      // Notifications should be on by default\n      expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Submit\n      await click(document.getElementById('submit'))\n\n      // Notifications should be off (or in this case non-existent)\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n    })\n\n    it('should be possible to reset to the default value if the form is reset', async () => {\n      let handleSubmission = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <form @submit.prevent=\"handleSubmission\">\n            <Switch name=\"assignee\" value=\"bob\" defaultChecked />\n            <button id=\"submit\">submit</button>\n            <button type=\"reset\" id=\"reset\">reset</button>\n          </form>\n        `,\n        setup: () => ({\n          handleSubmission(e: SubmitEvent) {\n            handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))\n          },\n        }),\n      })\n\n      // Bob is the defaultValue\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n\n      // Toggle the switch\n      await click(getSwitch())\n\n      // Bob should not be active anymore\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({})\n\n      // Reset\n      await click(document.getElementById('reset'))\n\n      // Bob should be submitted again\n      await click(document.getElementById('submit'))\n      expect(handleSubmission).toHaveBeenLastCalledWith({ assignee: 'bob' })\n    })\n\n    it('should still call the onChange listeners when choosing new values', async () => {\n      let handleChange = jest.fn()\n\n      renderTemplate({\n        template: html`<Switch name=\"notifications\" @update:modelValue=\"handleChange\" />`,\n        setup: () => ({ handleChange }),\n      })\n\n      // Toggle\n      await click(getSwitch())\n\n      // Toggle\n      await click(getSwitch())\n\n      // Toggle\n      await click(getSwitch())\n\n      // Change handler should have been called 3 times\n      expect(handleChange).toHaveBeenNthCalledWith(1, true)\n      expect(handleChange).toHaveBeenNthCalledWith(2, false)\n      expect(handleChange).toHaveBeenNthCalledWith(3, true)\n    })\n  })\n\n  describe('`tabIndex` attribute', () => {\n    it('should have a default tabIndex of `0`', () => {\n      renderTemplate(html`<Switch :checked=\"false\" :tabIndex=\"0\">Enable notifications</Switch>`)\n\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '0' },\n      })\n    })\n\n    it('should be possible to override the `tabIndex`', () => {\n      renderTemplate(html`<Switch :checked=\"false\" :tabIndex=\"3\">Enable notifications</Switch>`)\n\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '3' },\n      })\n    })\n\n    it('should not be possible to override the `tabIndex` to `-1`', () => {\n      renderTemplate(html`<Switch :checked=\"false\" :tabIndex=\"-1\">Enable notifications</Switch>`)\n\n      assertSwitch({\n        state: SwitchState.Off,\n        label: 'Enable notifications',\n        attributes: { tabindex: '0' },\n      })\n    })\n  })\n})\n\ndescribe('Render composition', () => {\n  it('should be possible to render a SwitchGroup, Switch and SwitchLabel', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <Switch v-model=\"checked\" />\n          <SwitchLabel>Enable notifications</SwitchLabel>\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })\n  })\n\n  it('should be possible to render a SwitchGroup, Switch and SwitchLabel (before the Switch)', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <SwitchLabel>Label B</SwitchLabel>\n          <Switch v-model=\"checked\"> Label A </Switch>\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    // Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive\n    // technologies.\n    //\n    // Thus: Label A should not be part of the \"label\" in this case\n    assertSwitch({ state: SwitchState.Off, label: 'Label B' })\n  })\n\n  it('should be possible to render a SwitchGroup, Switch and SwitchLabel (after the Switch)', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <Switch v-model=\"checked\"> Label A </Switch>\n          <SwitchLabel>Label B</SwitchLabel>\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    // Warning! Using aria-label or aria-labelledby will hide any descendant content from assistive\n    // technologies.\n    //\n    // Thus: Label A should not be part of the \"label\" in this case\n    assertSwitch({ state: SwitchState.Off, label: 'Label B' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Description (before the Switch)', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <SwitchDescription>This is an important feature</SwitchDescription>\n          <Switch v-model=\"checked\" />\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch and Switch.Description (after the Switch)', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <Switch v-model=\"checked\" />\n          <SwitchDescription>This is an important feature</SwitchDescription>\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({ state: SwitchState.Off, description: 'This is an important feature' })\n  })\n\n  it('should be possible to render a Switch.Group, Switch, Switch.Label and Switch.Description', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <SwitchLabel>Label A</SwitchLabel>\n          <Switch v-model=\"checked\" />\n          <SwitchDescription>This is an important feature</SwitchDescription>\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({\n      state: SwitchState.Off,\n      label: 'Label A',\n      description: 'This is an important feature',\n    })\n  })\n\n  it('should be possible to put classes on a SwitchLabel', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <SwitchLabel class=\"abc\">Label A</SwitchLabel>\n          <Switch v-model=\"checked\" />\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({\n      state: SwitchState.Off,\n      label: 'Label A',\n    })\n\n    expect(getByText('Label A')).toHaveClass('abc')\n  })\n\n  it('should be possible to put classes on a SwitchDescription', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <SwitchDescription class=\"abc\">Description A</SwitchDescription>\n          <Switch v-model=\"checked\" />\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({\n      state: SwitchState.Off,\n      description: 'Description A',\n    })\n\n    expect(getByText('Description A')).toHaveClass('abc')\n  })\n\n  it('should be possible to put classes on a SwitchGroup', async () => {\n    renderTemplate({\n      template: html`\n        <SwitchGroup as=\"div\" class=\"abc\" id=\"group\">\n          <Switch v-model=\"checked\" />\n        </SwitchGroup>\n      `,\n      setup: () => ({ checked: ref(false) }),\n    })\n\n    await new Promise(requestAnimationFrame)\n\n    assertSwitch({ state: SwitchState.Off })\n\n    expect(document.getElementById('group')).toHaveClass('abc')\n  })\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Space` key', () => {\n    it('should be possible to toggle the Switch with Space', async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        template: html` <Switch v-model=\"checked\" /> `,\n        setup() {\n          let checked = ref(false)\n          watch([checked], () => handleChange(checked.value))\n          return { checked }\n        },\n      })\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      getSwitch()?.focus()\n\n      // Toggle\n      await press(Keys.Space)\n\n      // Ensure state is on\n      assertSwitch({ state: SwitchState.On })\n\n      // Toggle\n      await press(Keys.Space)\n\n      // Ensure state is off\n      assertSwitch({ state: SwitchState.Off })\n    })\n  })\n\n  describe('`Enter` key', () => {\n    it('should not be possible to use Enter to toggle the Switch', async () => {\n      let handleChange = jest.fn()\n      renderTemplate({\n        template: html` <Switch v-model=\"checked\" /> `,\n        setup() {\n          let checked = ref(false)\n          watch([checked], () => handleChange(checked.value))\n          return { checked }\n        },\n      })\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      getSwitch()?.focus()\n\n      // Try to toggle\n      await press(Keys.Enter)\n\n      expect(handleChange).not.toHaveBeenCalled()\n    })\n\n    it('should submit the form on `Enter`', async () => {\n      let submits = jest.fn()\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Switch v-model=\"checked\" name=\"option\" />\n            <button>Submit</button>\n          </form>\n        `,\n        setup() {\n          let checked = ref(true)\n          return {\n            checked,\n            handleSubmit(event: KeyboardEvent) {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n            },\n          }\n        },\n      })\n\n      // Focus the input field\n      getSwitch()?.focus()\n      assertActiveElement(getSwitch())\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'on']])\n    })\n\n    it('should submit the form on `Enter` (when no submit button was found)', async () => {\n      let submits = jest.fn()\n      renderTemplate({\n        template: html`\n          <form @submit=\"handleSubmit\">\n            <Switch v-model=\"checked\" name=\"option\" />\n          </form>\n        `,\n        setup() {\n          let checked = ref(true)\n          return {\n            checked,\n            handleSubmit(event: KeyboardEvent) {\n              event.preventDefault()\n              submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n            },\n          }\n        },\n      })\n\n      // Focus the input field\n      getSwitch()?.focus()\n      assertActiveElement(getSwitch())\n\n      // Press enter (which should submit the form)\n      await press(Keys.Enter)\n\n      // Verify the form was submitted\n      expect(submits).toHaveBeenCalledTimes(1)\n      expect(submits).toHaveBeenCalledWith([['option', 'on']])\n    })\n  })\n\n  describe('`Tab` key', () => {\n    it('should be possible to tab away from the Switch', async () => {\n      renderTemplate({\n        template: html`\n          <div>\n            <Switch v-model=\"checked\" />\n            <button id=\"btn\">Other element</button>\n          </div>\n        `,\n        setup: () => ({ checked: ref(false) }),\n      })\n\n      // Ensure checkbox is off\n      assertSwitch({ state: SwitchState.Off })\n\n      // Focus the switch\n      getSwitch()?.focus()\n\n      // Expect the switch to be active\n      assertActiveElement(getSwitch())\n\n      // Toggle\n      await press(Keys.Tab)\n\n      // Expect the button to be active\n      assertActiveElement(document.getElementById('btn'))\n    })\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it('should be possible to toggle the Switch with a click', async () => {\n    let handleChange = jest.fn()\n    renderTemplate({\n      template: html` <Switch v-model=\"checked\" /> `,\n      setup() {\n        let checked = ref(false)\n        watch([checked], () => handleChange(checked.value))\n        return { checked }\n      },\n    })\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitch())\n\n    // Ensure state is on\n    assertSwitch({ state: SwitchState.On })\n\n    // Toggle\n    await click(getSwitch())\n\n    // Ensure state is off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  it('should be possible to toggle the Switch with a click on the Label', async () => {\n    let handleChange = jest.fn()\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <Switch v-model=\"checked\" />\n          <SwitchLabel>The label</SwitchLabel>\n        </SwitchGroup>\n      `,\n      setup() {\n        let checked = ref(false)\n        watch([checked], () => handleChange(checked.value))\n        return { checked }\n      },\n    })\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure the switch is focused\n    assertActiveElement(getSwitch())\n\n    // Ensure state is on\n    assertSwitch({ state: SwitchState.On })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure the switch is focused\n    assertActiveElement(getSwitch())\n\n    // Ensure state is off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  it('should not be possible to toggle the Switch with a click on the Label (passive)', async () => {\n    let handleChange = jest.fn()\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <Switch v-model=\"checked\" />\n          <SwitchLabel passive>The label</SwitchLabel>\n        </SwitchGroup>\n      `,\n      setup() {\n        let checked = ref(false)\n        watch([checked], () => handleChange(checked.value))\n        return { checked }\n      },\n    })\n\n    // Ensure checkbox is off\n    assertSwitch({ state: SwitchState.Off })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Ensure state is still Off\n    assertSwitch({ state: SwitchState.Off })\n  })\n\n  xit('should be possible to hover the label and trigger a hover on the switch', async () => {\n    // This test doesn't work in JSDOM :(\n    // Keeping it here for reference when we can test this in a real browser\n    renderTemplate({\n      template: html`\n        <SwitchGroup>\n          <style>\n            .bg {\n              background-color: rgba(0, 255, 0);\n            }\n            .bg-on-hover:hover {\n              background-color: rgba(255, 0, 0);\n            }\n          </style>\n          <Switch v-model=\"checked\" className=\"bg bg-on-hover\" />\n          <SwitchLabel>The label</SwitchLabel>\n        </SwitchGroup>\n      `,\n      setup() {\n        return { checked: ref(false) }\n      },\n    })\n\n    // Verify the switch is not hovered\n    expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(0, 255, 0)')\n\n    // Hover over the *label*\n    await mouseEnter(getSwitchLabel())\n\n    // Make sure the switch gets hover styles\n    expect(window.getComputedStyle(getSwitch()!).backgroundColor).toBe('rgb(255, 0, 0)')\n  })\n})\n\ndescribe('Form compatibility', () => {\n  it('should be possible to set the `form`, which is forwarded to the hidden inputs', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <div>\n          <SwitchGroup>\n            <Switch form=\"my-form\" v-model=\"checked\" name=\"notifications\" />\n            <SwitchLabel>Enable notifications</SwitchLabel>\n          </SwitchGroup>\n          <form id=\"my-form\" @submit=\"handleSubmit\">\n            <button>Submit</button>\n          </form>\n        </div>\n      `,\n      setup() {\n        let checked = ref(false)\n        return {\n          checked,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['notifications', 'on']])\n  })\n\n  it('should be possible to submit a form with an boolean value', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <SwitchGroup>\n            <Switch v-model=\"checked\" name=\"notifications\" />\n            <SwitchLabel>Enable notifications</SwitchLabel>\n          </SwitchGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup() {\n        let checked = ref(false)\n        return {\n          checked,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['notifications', 'on']])\n  })\n\n  it('should be possible to submit a form with a provided string value', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <SwitchGroup>\n            <Switch v-model=\"checked\" name=\"fruit\" value=\"apple\" />\n            <SwitchLabel>Apple</SwitchLabel>\n          </SwitchGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup() {\n        let checked = ref(false)\n        return {\n          checked,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([]) // no data\n\n    // Toggle\n    await click(getSwitchLabel())\n\n    // Submit the form again\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([['fruit', 'apple']])\n  })\n\n  it('should not submit the data if the Switch is disabled', async () => {\n    let submits = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <form @submit=\"handleSubmit\">\n          <input type=\"hidden\" name=\"foo\" value=\"bar\" />\n          <SwitchGroup>\n            <Switch v-model=\"checked\" name=\"fruit\" value=\"apple\" disabled />\n            <SwitchLabel>Apple</SwitchLabel>\n          </SwitchGroup>\n          <button>Submit</button>\n        </form>\n      `,\n      setup: () => {\n        let checked = ref(true)\n        return {\n          checked,\n          handleSubmit(event: SubmitEvent) {\n            event.preventDefault()\n            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])\n          },\n        }\n      },\n    })\n\n    // Submit the form\n    await click(getByText('Submit'))\n\n    // Verify that the form has been submitted\n    expect(submits).toHaveBeenLastCalledWith([\n      ['foo', 'bar'], // The only available field\n    ])\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/switch/switch.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  onMounted,\n  provide,\n  ref,\n  watch,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useControllable } from '../../hooks/use-controllable'\nimport { useId } from '../../hooks/use-id'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { Hidden, Features as HiddenFeatures } from '../../internal/hidden'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport { attemptSubmit } from '../../utils/form'\nimport { compact, omit, render } from '../../utils/render'\nimport { Description, useDescriptions } from '../description/description'\nimport { Label, useLabels } from '../label/label'\n\ntype StateDefinition = {\n  // State\n  switchRef: Ref<HTMLButtonElement | null>\n  labelledby: Ref<string | undefined>\n  describedby: Ref<string | undefined>\n}\n\nlet GroupContext = Symbol('GroupContext') as InjectionKey<StateDefinition>\n\n// ---\n\nexport let SwitchGroup = defineComponent({\n  name: 'SwitchGroup',\n  props: {\n    as: { type: [Object, String], default: 'template' },\n  },\n  setup(props, { slots, attrs }) {\n    let switchRef = ref<StateDefinition['switchRef']['value']>(null)\n    let labelledby = useLabels({\n      name: 'SwitchLabel',\n      props: {\n        htmlFor: computed(() => switchRef.value?.id),\n        onClick(event: MouseEvent & { currentTarget: HTMLElement }) {\n          if (!switchRef.value) return\n          if (event.currentTarget.tagName === 'LABEL') {\n            event.preventDefault()\n          }\n          switchRef.value.click()\n          switchRef.value.focus({ preventScroll: true })\n        },\n      },\n    })\n    let describedby = useDescriptions({ name: 'SwitchDescription' })\n\n    let api = { switchRef, labelledby, describedby }\n\n    provide(GroupContext, api)\n\n    return () =>\n      render({ theirProps: props, ourProps: {}, slot: {}, slots, attrs, name: 'SwitchGroup' })\n  },\n})\n\n// ---\n\nexport let Switch = defineComponent({\n  name: 'Switch',\n  emits: { 'update:modelValue': (_value: boolean) => true },\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    modelValue: { type: Boolean, default: undefined },\n    defaultChecked: { type: Boolean, optional: true },\n    form: { type: String, optional: true },\n    name: { type: String, optional: true },\n    value: { type: String, optional: true },\n    id: { type: String, default: () => `headlessui-switch-${useId()}` },\n    disabled: { type: Boolean, default: false },\n    tabIndex: { type: Number, default: 0 },\n  },\n  inheritAttrs: false,\n  setup(props, { emit, attrs, slots, expose }) {\n    let api = inject(GroupContext, null)\n\n    let [checked, theirOnChange] = useControllable(\n      computed(() => props.modelValue),\n      (value: boolean) => emit('update:modelValue', value),\n      computed(() => props.defaultChecked)\n    )\n\n    function toggle() {\n      theirOnChange(!checked.value)\n    }\n\n    let internalSwitchRef = ref<HTMLButtonElement | null>(null)\n    let switchRef = api === null ? internalSwitchRef : api.switchRef\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      switchRef\n    )\n\n    expose({ el: switchRef, $el: switchRef })\n\n    function handleClick(event: MouseEvent) {\n      event.preventDefault()\n      toggle()\n    }\n\n    function handleKeyUp(event: KeyboardEvent) {\n      if (event.key === Keys.Space) {\n        event.preventDefault()\n        toggle()\n      } else if (event.key === Keys.Enter) {\n        attemptSubmit(event.currentTarget as HTMLElement)\n      }\n    }\n\n    // This is needed so that we can \"cancel\" the click event when we use the `Enter` key on a button.\n    function handleKeyPress(event: KeyboardEvent) {\n      event.preventDefault()\n    }\n\n    let form = computed(() => dom(switchRef)?.closest?.('form'))\n    onMounted(() => {\n      watch(\n        [form],\n        () => {\n          if (!form.value) return\n          if (props.defaultChecked === undefined) return\n\n          function handle() {\n            theirOnChange(props.defaultChecked)\n          }\n\n          form.value.addEventListener('reset', handle)\n          return () => {\n            form.value?.removeEventListener('reset', handle)\n          }\n        },\n        { immediate: true }\n      )\n    })\n\n    return () => {\n      let { id, name, value, form, tabIndex, ...theirProps } = props\n      let slot = { checked: checked.value }\n      let ourProps = {\n        id,\n        ref: switchRef,\n        role: 'switch',\n        type: type.value,\n        tabIndex: tabIndex === -1 ? 0 : tabIndex,\n        'aria-checked': checked.value,\n        'aria-labelledby': api?.labelledby.value,\n        'aria-describedby': api?.describedby.value,\n        onClick: handleClick,\n        onKeyup: handleKeyUp,\n        onKeypress: handleKeyPress,\n      }\n\n      return h(Fragment, [\n        name != null && checked.value != null\n          ? h(\n              Hidden,\n              compact({\n                features: HiddenFeatures.Hidden,\n                as: 'input',\n                type: 'checkbox',\n                hidden: true,\n                readOnly: true,\n                checked: checked.value,\n                form,\n                disabled: theirProps.disabled,\n                name,\n                value,\n              })\n            )\n          : null,\n        render({\n          ourProps,\n          theirProps: { ...attrs, ...omit(theirProps, ['modelValue', 'defaultChecked']) },\n          slot,\n          attrs,\n          slots,\n          name: 'Switch',\n        }),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let SwitchLabel = Label\nexport let SwitchDescription = Description\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/tabs/tabs.ssr.test.ts",
    "content": "import { defineComponent } from 'vue'\nimport { html } from '../../test-utils/html'\nimport { renderHydrate, renderSSR } from '../../test-utils/ssr'\nimport { Tab, TabGroup, TabList, TabPanel, TabPanels } from './tabs'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nlet Example = defineComponent({\n  components: { TabGroup, TabList, Tab, TabPanels, TabPanel },\n  template: html`\n    <TabGroup>\n      <TabList>\n        <Tab>Tab 1</Tab>\n        <Tab>Tab 2</Tab>\n        <Tab>Tab 3</Tab>\n      </TabList>\n\n      <TabPanels>\n        <TabPanel>Content 1</TabPanel>\n        <TabPanel>Content 2</TabPanel>\n        <TabPanel>Content 3</TabPanel>\n      </TabPanels>\n    </TabGroup>\n  `,\n})\n\ndescribe('Rendering', () => {\n  describe('SSR', () => {\n    it('should be possible to server side render the first Tab and Panel', async () => {\n      let { contents } = await renderSSR(Example)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n\n    it('should be possible to server side render the defaultIndex Tab and Panel', async () => {\n      let { contents } = await renderSSR(Example, { defaultIndex: 1 })\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n  })\n\n  describe('Hydration', () => {\n    it('should be possible to server side render the first Tab and Panel', async () => {\n      let { contents } = await renderHydrate(Example)\n\n      expect(contents).toContain(`Content 1`)\n      expect(contents).not.toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n\n    it('should be possible to server side render the defaultIndex Tab and Panel', async () => {\n      let { contents } = await renderHydrate(Example, { defaultIndex: 1 })\n\n      expect(contents).not.toContain(`Content 1`)\n      expect(contents).toContain(`Content 2`)\n      expect(contents).not.toContain(`Content 3`)\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/tabs/tabs.test.ts",
    "content": "import { nextTick, ref } from 'vue'\nimport {\n  assertActiveElement,\n  assertTabs,\n  getByText,\n  getTabs,\n} from '../../test-utils/accessibility-assertions'\nimport { html } from '../../test-utils/html'\nimport { Keys, click, press, shift } from '../../test-utils/interactions'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, render } from '../../test-utils/vue-testing-library'\nimport { Dialog } from '../dialog/dialog'\nimport { Tab, TabGroup, TabList, TabPanel, TabPanels } from './tabs'\n\njest.mock('../../hooks/use-id')\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({ TabGroup, TabList, Tab, TabPanels, TabPanel, Dialog })\n\ndescribe('safeguards', () => {\n  it.each([\n    ['TabList', TabList],\n    ['Tab', Tab],\n    ['TabPanels', TabPanels],\n    ['TabPanel', TabPanel],\n  ])(\n    'should error when we are using a <%s /> without a parent <TabGroup /> component',\n    suppressConsoleLogs((name, Component) => {\n      expect(() => render(Component)).toThrow(\n        `<${name} /> is missing a parent <TabGroup /> component.`\n      )\n    })\n  )\n\n  it('should be possible to render TabGroup without crashing', async () => {\n    renderTemplate(html`\n      <TabGroup>\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertTabs({ active: 0 })\n  })\n})\n\ndescribe('Rendering', () => {\n  it('should be possible to render the TabPanels first, then the TabList', async () => {\n    renderTemplate(html`\n      <TabGroup>\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n      </TabGroup>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertTabs({ active: 0 })\n  })\n\n  it('should guarantee the order of DOM nodes when performing actions', async () => {\n    renderTemplate({\n      template: html`\n        <button @click=\"toggle()\">toggle</button>\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab v-if=\"!hide\">Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel v-if=\"!hide\">Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `,\n      setup() {\n        let hide = ref(false)\n\n        return {\n          hide,\n          toggle() {\n            hide.value = !hide.value\n          },\n        }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    await click(getByText('toggle')) // Remove Tab 2\n    await click(getByText('toggle')) // Re-add Tab 2\n\n    await press(Keys.Tab)\n    assertTabs({ active: 0 })\n\n    await press(Keys.ArrowRight)\n    assertTabs({ active: 1 })\n\n    await press(Keys.ArrowRight)\n    assertTabs({ active: 2 })\n  })\n\n  it(\n    'should guarantee the order when injecting new tabs dynamically',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <TabGroup>\n            <TabList>\n              <Tab v-for=\"(t, i) in tabs\" :key=\"t\">Tab {{ i + 1 }}</Tab>\n              <Tab>Insert new</Tab>\n            </TabList>\n            <TabPanels>\n              <TabPanel v-for=\"t in tabs\" :key=\"t\">{{ t }}</TabPanel>\n              <TabPanel>\n                <button @click=\"add\">Insert</button>\n              </TabPanel>\n            </TabPanels>\n          </TabGroup>\n        `,\n        setup() {\n          let tabs = ref<string[]>([])\n\n          return {\n            tabs,\n            add() {\n              tabs.value.push(`Panel ${tabs.value.length + 1}`)\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertTabs({ active: 0, tabContents: 'Insert new', panelContents: 'Insert' })\n\n      // Add some new tabs\n      await click(getByText('Insert'))\n\n      // We should still be on the tab we were on\n      assertTabs({ active: 1, tabContents: 'Insert new', panelContents: 'Insert' })\n    })\n  )\n\n  it(\n    'should use the `selectedIndex` when injecting new tabs dynamically',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <TabGroup :selectedIndex=\"1\">\n            <TabList>\n              <Tab v-for=\"t in tabs\" :key=\"t\">Tab {{ t }}</Tab>\n            </TabList>\n            <TabPanels>\n              <TabPanel v-for=\"t in tabs\" :key=\"t\">Panel {{ t }}</TabPanel>\n            </TabPanels>\n          </TabGroup>\n          <button @click=\"add\">Insert</button>\n        `,\n        setup() {\n          let tabs = ref<string[]>(['A', 'B', 'C'])\n\n          return {\n            tabs,\n            add() {\n              tabs.value.splice(1, 0, 'D')\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertTabs({ active: 1, tabContents: 'Tab B', panelContents: 'Panel B' })\n\n      // Add some new tabs\n      await click(getByText('Insert'))\n\n      // We should still be at the same tab position, but the tab itself changed\n      assertTabs({ active: 1, tabContents: 'Tab D', panelContents: 'Panel D' })\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when reversing the tabs and panels themselves, then performing actions (controlled component)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <button @click=\"reverse\">reverse</button>\n          <TabGroup :selectedIndex=\"selectedIndex\" @change=\"handleChange\">\n            <TabList>\n              <Tab v-for=\"tab in tabs\" :key=\"tab\">Tab {{ tab }}</Tab>\n            </TabList>\n            <TabPanels>\n              <TabPanel v-for=\"tab in tabs\" :key=\"tab\">Content {{ tab }}</TabPanel>\n            </TabPanels>\n          </TabGroup>\n          <p id=\"selectedIndex\">{{ selectedIndex }}</p>\n        `,\n        setup() {\n          let selectedIndex = ref(1)\n          let tabs = ref([0, 1, 2])\n\n          return {\n            tabs,\n            selectedIndex,\n            reverse() {\n              tabs.value = tabs.value.slice().reverse()\n            },\n            handleChange(value: number) {\n              selectedIndex.value = value\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      let selectedIndexElement = document.getElementById('selectedIndex')\n\n      assertTabs({ active: 1 })\n\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed now\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed again now (back to normal)\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n    })\n  )\n\n  it(\n    'should guarantee the order of DOM nodes when reversing the tabs and panels themselves, then performing actions (uncontrolled component)',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <button @click=\"reverse\">reverse</button>\n          <TabGroup\n            :selectedIndex=\"selectedIndex\"\n            @change=\"handleChange\"\n            v-slot=\"{ selectedIndex }\"\n          >\n            <TabList>\n              <Tab v-for=\"tab in tabs\" :key=\"tab\">Tab {{ tab }}</Tab>\n            </TabList>\n            <TabPanels>\n              <TabPanel v-for=\"tab in tabs\" :key=\"tab\">Content {{ tab }}</TabPanel>\n            </TabPanels>\n            <p id=\"selectedIndex\">{{ selectedIndex }}</p>\n          </TabGroup>\n        `,\n        setup() {\n          let selectedIndex = ref(1)\n          let tabs = ref([0, 1, 2])\n\n          return {\n            tabs,\n            selectedIndex,\n            reverse() {\n              tabs.value = tabs.value.slice().reverse()\n            },\n            handleChange(value: number) {\n              selectedIndex.value = value\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      let selectedIndexElement = document.getElementById('selectedIndex')\n\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed now\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('reverse'))\n\n      // Note: the indices are reversed again now (back to normal)\n      await click(getByText('Tab 0'))\n      assertTabs({ active: 0 })\n      expect(selectedIndexElement).toHaveTextContent('0')\n\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 1 })\n      expect(selectedIndexElement).toHaveTextContent('1')\n\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 })\n      expect(selectedIndexElement).toHaveTextContent('2')\n    })\n  )\n\n  describe('`renderProps`', () => {\n    it('should expose the `selectedIndex` on the `Tabs` component', async () => {\n      renderTemplate(html`\n        <TabGroup v-slot=\"data\">\n          <pre id=\"exposed\">{{JSON.stringify(data)}}</pre>\n\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 0 })\n      )\n\n      await click(getByText('Tab 2'))\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 1 })\n      )\n    })\n\n    it('should expose the `selectedIndex` on the `TabList` component', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList v-slot=\"data\">\n            <pre id=\"exposed\">{{JSON.stringify(data)}}</pre>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 0 })\n      )\n\n      await click(getByText('Tab 2'))\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 1 })\n      )\n    })\n\n    it('should expose the `selectedIndex` on the `TabPanels` component', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels v-slot=\"data\">\n            <pre id=\"exposed\">{{JSON.stringify(data)}}</pre>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 0 })\n      )\n\n      await click(getByText('Tab 2'))\n\n      expect(document.getElementById('exposed')).toHaveTextContent(\n        JSON.stringify({ selectedIndex: 1 })\n      )\n    })\n\n    it('should expose the `selected` state on the `Tab` components', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab v-slot=\"data\">\n              <pre data-tab=\"0\">{{JSON.stringify(data)}}</pre>\n              <span>Tab 1</span>\n            </Tab>\n            <Tab v-slot=\"data\">\n              <pre data-tab=\"1\">{{JSON.stringify(data)}}</pre>\n              <span>Tab 2</span>\n            </Tab>\n            <Tab v-slot=\"data\">\n              <pre data-tab=\"2\">{{JSON.stringify(data)}}</pre>\n              <span>Tab 3</span>\n            </Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      expect(document.querySelector('[data-tab=\"0\"]')).toHaveTextContent(\n        JSON.stringify({ selected: true, disabled: false })\n      )\n      expect(document.querySelector('[data-tab=\"1\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false, disabled: false })\n      )\n      expect(document.querySelector('[data-tab=\"2\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false, disabled: false })\n      )\n\n      await click(getTabs()[1])\n\n      expect(document.querySelector('[data-tab=\"0\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false, disabled: false })\n      )\n      expect(document.querySelector('[data-tab=\"1\"]')).toHaveTextContent(\n        JSON.stringify({ selected: true, disabled: false })\n      )\n      expect(document.querySelector('[data-tab=\"2\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false, disabled: false })\n      )\n    })\n\n    it('should expose the `selected` state on the `TabPanel` components', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel :unmount=\"false\" v-slot=\"data\">\n              <pre data-panel=\"0\">{{JSON.stringify(data)}}</pre>\n              <span>Content 1</span>\n            </TabPanel>\n            <TabPanel :unmount=\"false\" v-slot=\"data\">\n              <pre data-panel=\"1\">{{JSON.stringify(data)}}</pre>\n              <span>Content 2</span>\n            </TabPanel>\n            <TabPanel :unmount=\"false\" v-slot=\"data\">\n              <pre data-panel=\"2\">{{JSON.stringify(data)}}</pre>\n              <span>Content 3</span>\n            </TabPanel>\n          </TabPanels>\n        </TabGroup>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      expect(document.querySelector('[data-panel=\"0\"]')).toHaveTextContent(\n        JSON.stringify({ selected: true })\n      )\n      expect(document.querySelector('[data-panel=\"1\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false })\n      )\n      expect(document.querySelector('[data-panel=\"2\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false })\n      )\n\n      await click(getByText('Tab 2'))\n\n      expect(document.querySelector('[data-panel=\"0\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false })\n      )\n      expect(document.querySelector('[data-panel=\"1\"]')).toHaveTextContent(\n        JSON.stringify({ selected: true })\n      )\n      expect(document.querySelector('[data-panel=\"2\"]')).toHaveTextContent(\n        JSON.stringify({ selected: false })\n      )\n    })\n  })\n\n  describe('`defaultIndex`', () => {\n    it('should jump to the nearest tab when the defaultIndex is out of bounds (-2)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"-2\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 0 })\n      assertActiveElement(getByText('Tab 1'))\n    })\n\n    it('should jump to the nearest tab when the defaultIndex is out of bounds (+5)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"5\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 2 })\n      assertActiveElement(getByText('Tab 3'))\n    })\n\n    it('should jump to the next available tab when the defaultIndex is a disabled tab', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"0\">\n          <TabList>\n            <Tab disabled>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 1 })\n      assertActiveElement(getByText('Tab 2'))\n    })\n\n    it('should jump to the next available tab when the defaultIndex is a disabled tab and wrap around', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab disabled>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 0 })\n      assertActiveElement(getByText('Tab 1'))\n    })\n\n    it('should not change the Tab if the defaultIndex changes', async () => {\n      renderTemplate({\n        template: html`\n          <TabGroup :defaultIndex=\"defaultIndex\">\n            <TabList>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </TabList>\n\n            <TabPanels>\n              <TabPanel>Content 1</TabPanel>\n              <TabPanel>Content 2</TabPanel>\n              <TabPanel>Content 3</TabPanel>\n            </TabPanels>\n          </TabGroup>\n\n          <button>after</button>\n          <button @click=\"defaultIndex = 0\">change</button>\n        `,\n        setup() {\n          let defaultIndex = ref(1)\n          return { defaultIndex }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 1 })\n      assertActiveElement(getByText('Tab 2'))\n\n      await click(getByText('Tab 3'))\n\n      assertTabs({ active: 2 })\n      assertActiveElement(getByText('Tab 3'))\n\n      // Change default index\n      await click(getByText('change'))\n\n      // Nothing should change...\n      assertTabs({ active: 2 })\n    })\n\n    it(\n      'should select first tab if no tabs were provided originally',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <TabGroup :defaultIndex=\"0\">\n              <TabList>\n                <Tab v-for=\"tab in tabs\">{{ tab }}</Tab>\n              </TabList>\n              <TabPanels>\n                <TabPanel v-for=\"tab in tabs\">content for: {{ tab }}</TabPanel>\n              </TabPanels>\n            </TabGroup>\n\n            <button>after</button>\n            <button @click=\"tabs.value = ['tab 1', 'tab 2', 'tab 3']\">change</button>\n          `,\n          setup() {\n            let tabs = ref<string[]>([])\n            return {\n              tabs,\n            }\n          },\n        })\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 0 })\n      })\n    )\n\n    it(\n      'should select first tab if no tabs were provided originally (with a defaultIndex of 1)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <TabGroup :defaultIndex=\"1\">\n              <TabList>\n                <Tab v-for=\"tab in tabs\">{{ tab }}</Tab>\n              </TabList>\n              <TabPanels>\n                <TabPanel v-for=\"tab in tabs\">content for: {{ tab }}</TabPanel>\n              </TabPanels>\n            </TabGroup>\n\n            <button>after</button>\n            <button @click=\"tabs.value = ['tab 1', 'tab 2', 'tab 3']\">change</button>\n          `,\n          setup() {\n            let tabs = ref<string[]>([])\n            return {\n              tabs,\n            }\n          },\n        })\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 1 })\n      })\n    )\n\n    it(\n      'should select first tab if no tabs were provided originally (with a defaultIndex of 1)',\n      suppressConsoleLogs(async () => {\n        renderTemplate({\n          template: html`\n            <TabGroup :defaultIndex=\"1\">\n              <TabList>\n                <Tab v-for=\"tab in tabs\">{{ tab }}</Tab>\n              </TabList>\n              <TabPanels>\n                <TabPanel v-for=\"tab in tabs\">content for: {{ tab }}</TabPanel>\n              </TabPanels>\n            </TabGroup>\n\n            <button>after</button>\n            <button @click=\"tabs.value = ['tab 1', 'tab 2', 'tab 3']\">change 1</button>\n            <button @click=\"tabs.value = []\">change 2</button>\n            <button @click=\"tabs.value = ['tab 1', 'tab 2', 'tab 3']\">change 3</button>\n          `,\n          setup() {\n            let tabs = ref<string[]>([])\n            return {\n              tabs,\n            }\n          },\n        })\n\n        assertActiveElement(document.body)\n\n        // There are no tab initially\n        assertTabs({ active: -1 })\n\n        // There are not tabs so this should not change anything\n        await press(Keys.Tab)\n        assertTabs({ active: -1 })\n\n        // Add some tabs\n        await click(getByText('change 1'))\n\n        // Add some tabs\n        await click(getByText('change 2'))\n\n        // Add some tabs\n        await click(getByText('change 3'))\n\n        // When going from no tabs to some tabs, the tab based on defaultIndex should be selected\n        assertTabs({ active: 1 })\n      })\n    )\n  })\n})\n\ndescribe('`selectedIndex`', () => {\n  it(\n    'should not change the tab in a controlled component if you do not respond to the @change',\n    suppressConsoleLogs(async () => {\n      let handleChange = jest.fn()\n\n      renderTemplate({\n        template: html`\n          <TabGroup @change=\"handleChange\" :selectedIndex=\"selectedIndex\">\n            <TabList>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </TabList>\n\n            <TabPanels>\n              <TabPanel>Content 1</TabPanel>\n              <TabPanel>Content 2</TabPanel>\n              <TabPanel>Content 3</TabPanel>\n            </TabPanels>\n          </TabGroup>\n          <button>after</button>\n          <button @click=\"next\">setSelectedIndex</button>\n        `,\n        setup() {\n          let selectedIndex = ref(0)\n\n          return {\n            selectedIndex,\n            handleChange(value: number) {\n              handleChange(value)\n            },\n            next() {\n              selectedIndex.value += 1\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      // test controlled behaviour\n      await click(getByText('setSelectedIndex'))\n      assertTabs({ active: 1 })\n      await click(getByText('setSelectedIndex'))\n      assertTabs({ active: 2 })\n\n      // test uncontrolled behaviour again\n      await click(getByText('Tab 1'))\n      assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n      await click(getByText('Tab 2'))\n      assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n      await click(getByText('Tab 3'))\n      assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n      await click(getByText('Tab 1'))\n      expect(handleChange).toHaveBeenCalledTimes(3) // We did see the '@change' calls, but only 3 because clicking Tab 3 is already the active one which means that this doesn't trigger the @change\n      assertTabs({ active: 2 }) // Should still be Tab 3 because `selectedIndex` didn't update\n    })\n  )\n\n  it('should be possible to change active tab controlled and uncontrolled', async () => {\n    let handleChange = jest.fn()\n\n    renderTemplate({\n      template: html`\n        <TabGroup @change=\"handleChange\" :selectedIndex=\"selectedIndex\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n        <button>after</button>\n        <button @click=\"next\">setSelectedIndex</button>\n      `,\n      setup() {\n        let selectedIndex = ref(0)\n\n        return {\n          selectedIndex,\n          handleChange(value: number) {\n            selectedIndex.value = value\n            handleChange(value)\n          },\n          next() {\n            selectedIndex.value += 1\n          },\n        }\n      },\n    })\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    // test uncontrolled behaviour\n    await click(getByText('Tab 2'))\n    expect(handleChange).toHaveBeenCalledTimes(1)\n    expect(handleChange).toHaveBeenNthCalledWith(1, 1)\n    assertTabs({ active: 1 })\n\n    // test controlled behaviour\n    await click(getByText('setSelectedIndex'))\n    assertTabs({ active: 2 })\n\n    // test uncontrolled behaviour again\n    await click(getByText('Tab 2'))\n    expect(handleChange).toHaveBeenCalledTimes(2)\n    expect(handleChange).toHaveBeenNthCalledWith(2, 1)\n    assertTabs({ active: 1 })\n  })\n\n  it('should jump to the nearest tab when the selectedIndex is out of bounds (-2)', async () => {\n    renderTemplate(html`\n      <TabGroup :selectedIndex=\"-2\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    await press(Keys.Tab)\n\n    assertTabs({ active: 0 })\n    assertActiveElement(getByText('Tab 1'))\n  })\n\n  it('should jump to the nearest tab when the selectedIndex is out of bounds (+5)', async () => {\n    renderTemplate(html`\n      <TabGroup :selectedIndex=\"5\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    await press(Keys.Tab)\n\n    assertTabs({ active: 2 })\n    assertActiveElement(getByText('Tab 3'))\n  })\n\n  it('should jump to the next available tab when the selectedIndex is a disabled tab', async () => {\n    renderTemplate(html`\n      <TabGroup :selectedIndex=\"0\">\n        <TabList>\n          <Tab disabled>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    await press(Keys.Tab)\n\n    assertTabs({ active: 1 })\n    assertActiveElement(getByText('Tab 2'))\n  })\n\n  it('should jump to the next available tab when the selectedIndex is a disabled tab and wrap around', async () => {\n    renderTemplate(html`\n      <TabGroup :selectedIndex=\"2\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab disabled>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    await press(Keys.Tab)\n\n    assertTabs({ active: 0 })\n    assertActiveElement(getByText('Tab 1'))\n  })\n\n  it('should prefer selectedIndex over defaultIndex', async () => {\n    renderTemplate(html`\n      <TabGroup :selectedIndex=\"0\" :defaultIndex=\"2\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n\n    await press(Keys.Tab)\n\n    assertTabs({ active: 0 })\n    assertActiveElement(getByText('Tab 1'))\n  })\n\n  it(\n    'should wrap around when overflowing the index when using a controlled component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <TabGroup :selectedIndex=\"value\" @change=\"set\" v-slot=\"{ selectedIndex }\">\n            <TabList>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </TabList>\n\n            <TabPanels>\n              <TabPanel>Content 1</TabPanel>\n              <TabPanel>Content 2</TabPanel>\n              <TabPanel>Content 3</TabPanel>\n            </TabPanels>\n\n            <button @click=\"set(selectedIndex + 1)\">Next</button>\n          </TabGroup>\n        `,\n        setup() {\n          let value = ref(0)\n          return {\n            value,\n            set(v: number) {\n              value.value = v\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await click(getByText('Next'))\n      assertTabs({ active: 1 })\n\n      await click(getByText('Next'))\n      assertTabs({ active: 2 })\n\n      await click(getByText('Next'))\n      assertTabs({ active: 0 })\n\n      await click(getByText('Next'))\n      assertTabs({ active: 1 })\n    })\n  )\n\n  it(\n    'should wrap around when underflowing the index when using a controlled component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: html`\n          <TabGroup :selectedIndex=\"value\" @change=\"set\" v-slot=\"{ selectedIndex }\">\n            <TabList>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </TabList>\n\n            <TabPanels>\n              <TabPanel>Content 1</TabPanel>\n              <TabPanel>Content 2</TabPanel>\n              <TabPanel>Content 3</TabPanel>\n            </TabPanels>\n\n            <button @click=\"set(selectedIndex - 1)\">Previous</button>\n          </TabGroup>\n        `,\n        setup() {\n          let value = ref(0)\n          return {\n            value,\n            set(v: number) {\n              value.value = v\n            },\n          }\n        },\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await click(getByText('Previous'))\n      assertTabs({ active: 2 })\n\n      await click(getByText('Previous'))\n      assertTabs({ active: 1 })\n\n      await click(getByText('Previous'))\n      assertTabs({ active: 0 })\n\n      await click(getByText('Previous'))\n      assertTabs({ active: 2 })\n    })\n  )\n})\n\ndescribe('Keyboard interactions', () => {\n  describe('`Tab` key', () => {\n    it('should be possible to tab to the default initial first tab', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 0 })\n      assertActiveElement(getByText('Tab 1'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Content 1'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('after'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Content 1'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Tab 1'))\n    })\n\n    it('should be possible to tab to the default index tab', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n\n      assertTabs({ active: 1 })\n      assertActiveElement(getByText('Tab 2'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('Content 2'))\n\n      await press(Keys.Tab)\n      assertActiveElement(getByText('after'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Content 2'))\n\n      await press(shift(Keys.Tab))\n      assertActiveElement(getByText('Tab 2'))\n    })\n  })\n\n  describe('`ArrowRight` key', () => {\n    it('should be possible to go to the next item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n    })\n\n    it('should be possible to go to the next item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n    })\n\n    it('should wrap around at the end (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n    })\n\n    it('should wrap around at the end (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n      await press(Keys.Enter)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n    })\n\n    it('should not be possible to go right when in vertical mode (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowRight)\n      // no-op\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n\n    it('should not be possible to go right when in vertical mode (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n      // no-op\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n  })\n\n  describe('`ArrowLeft` key', () => {\n    it('should be possible to go to the previous item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 0 })\n    })\n\n    it('should be possible to go to the previous item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 2 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 0 })\n    })\n\n    it('should wrap around at the beginning (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 1 })\n    })\n\n    it('should wrap around at the beginning (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 2 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 2 })\n      await press(Keys.Enter)\n      assertTabs({ active: 1 })\n    })\n\n    it('should not be possible to go left when in vertical mode (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowLeft)\n      // no-op\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n\n    it('should not be possible to go left when in vertical mode (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowLeft)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n\n      // no-op\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n  })\n\n  describe('`ArrowDown` key', () => {\n    it('should be possible to go to the next item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 2, orientation: 'vertical' })\n    })\n\n    it('should be possible to go to the next item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 1, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 2, orientation: 'vertical' })\n    })\n\n    it('should wrap around at the end (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 1, orientation: 'vertical' })\n    })\n\n    it('should wrap around at the end (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 1, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 2, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n    })\n\n    it('should not be possible to go down when in horizontal mode (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowDown)\n      // no-op\n      assertTabs({ active: 0 })\n    })\n\n    it('should not be possible to go down when in horizontal mode (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowDown)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n\n      // no-op\n      assertTabs({ active: 0 })\n    })\n  })\n\n  describe('`ArrowUp` key', () => {\n    it('should be possible to go to the previous item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n\n    it('should be possible to go to the previous item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 2, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 1, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 0, orientation: 'vertical' })\n    })\n\n    it('should wrap around at the beginning (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" vertical>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 1, orientation: 'vertical' })\n    })\n\n    it('should wrap around at the beginning (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"2\" vertical manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 2, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 1, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 0, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 0, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 2, orientation: 'vertical' })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 2, orientation: 'vertical' })\n      await press(Keys.Enter)\n      assertTabs({ active: 1, orientation: 'vertical' })\n    })\n\n    it('should not be possible to go left when in vertical mode (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowUp)\n      // no-op\n      assertTabs({ active: 0 })\n    })\n\n    it('should not be possible to go left when in vertical mode (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      await press(Keys.ArrowUp)\n      assertTabs({ active: 0 })\n      await press(Keys.Enter)\n\n      // no-op\n      assertTabs({ active: 0 })\n    })\n  })\n\n  describe('`Home` key', () => {\n    it('should be possible to go to the first focusable item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.Home)\n      assertTabs({ active: 0 })\n    })\n\n    it('should be possible to go to the first focusable item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.Home)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 0 })\n    })\n  })\n\n  describe('`PageUp` key', () => {\n    it('should be possible to go to the first focusable item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.PageUp)\n      assertTabs({ active: 0 })\n    })\n\n    it('should be possible to go to the first focusable item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.PageUp)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 0 })\n    })\n  })\n\n  describe('`End` key', () => {\n    it('should be possible to go to the first focusable item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.End)\n      assertTabs({ active: 2 })\n    })\n\n    it('should be possible to go to the first focusable item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.End)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n    })\n  })\n\n  describe('`PageDown` key', () => {\n    it('should be possible to go to the first focusable item (activation = `auto`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\">\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.PageDown)\n      assertTabs({ active: 2 })\n    })\n\n    it('should be possible to go to the first focusable item (activation = `manual`)', async () => {\n      renderTemplate(html`\n        <TabGroup :defaultIndex=\"1\" manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 1 })\n\n      await press(Keys.PageDown)\n      assertTabs({ active: 1 })\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n    })\n  })\n\n  describe('`Enter` key', () => {\n    it('should be possible to activate the focused tab', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      getByText('Tab 3')?.focus()\n\n      assertActiveElement(getByText('Tab 3'))\n      assertTabs({ active: 0 })\n\n      await press(Keys.Enter)\n      assertTabs({ active: 2 })\n    })\n  })\n\n  describe('`Space` key', () => {\n    it('should be possible to activate the focused tab', async () => {\n      renderTemplate(html`\n        <TabGroup manual>\n          <TabList>\n            <Tab>Tab 1</Tab>\n            <Tab>Tab 2</Tab>\n            <Tab>Tab 3</Tab>\n          </TabList>\n\n          <TabPanels>\n            <TabPanel>Content 1</TabPanel>\n            <TabPanel>Content 2</TabPanel>\n            <TabPanel>Content 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n\n        <button>after</button>\n      `)\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      getByText('Tab 3')?.focus()\n\n      assertActiveElement(getByText('Tab 3'))\n      assertTabs({ active: 0 })\n\n      await press(Keys.Space)\n      assertTabs({ active: 2 })\n    })\n  })\n})\n\ndescribe('Mouse interactions', () => {\n  it('should be possible to click on a tab to focus it', async () => {\n    renderTemplate(html`\n      <TabGroup :defaultIndex=\"1\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n    await press(Keys.Tab)\n    assertTabs({ active: 1 })\n\n    await click(getByText('Tab 1'))\n    assertTabs({ active: 0 })\n\n    await click(getByText('Tab 3'))\n    assertTabs({ active: 2 })\n\n    await click(getByText('Tab 2'))\n    assertTabs({ active: 1 })\n  })\n\n  it('should be a no-op when clicking on a disabled tab', async () => {\n    renderTemplate(html`\n      <TabGroup :defaultIndex=\"1\">\n        <TabList>\n          <Tab disabled>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `)\n\n    await new Promise<void>(nextTick)\n\n    assertActiveElement(document.body)\n    await press(Keys.Tab)\n    assertTabs({ active: 1 })\n\n    await click(getByText('Tab 1'))\n    // No-op, Tab 2 is still active\n    assertTabs({ active: 1 })\n  })\n})\n\nit('should trigger the `change` when the tab changes', async () => {\n  let changes = jest.fn()\n\n  renderTemplate({\n    template: html`\n      <TabGroup @change=\"changes\">\n        <TabList>\n          <Tab>Tab 1</Tab>\n          <Tab>Tab 2</Tab>\n          <Tab>Tab 3</Tab>\n        </TabList>\n\n        <TabPanels>\n          <TabPanel>Content 1</TabPanel>\n          <TabPanel>Content 2</TabPanel>\n          <TabPanel>Content 3</TabPanel>\n        </TabPanels>\n      </TabGroup>\n\n      <button>after</button>\n    `,\n    setup: () => ({ changes }),\n  })\n\n  await new Promise<void>(nextTick)\n\n  await click(getByText('Tab 2'))\n  await click(getByText('Tab 3'))\n  await click(getByText('Tab 2'))\n  await click(getByText('Tab 1'))\n\n  expect(changes).toHaveBeenCalledTimes(4)\n\n  expect(changes).toHaveBeenNthCalledWith(1, 1)\n  expect(changes).toHaveBeenNthCalledWith(2, 2)\n  expect(changes).toHaveBeenNthCalledWith(3, 1)\n  expect(changes).toHaveBeenNthCalledWith(4, 0)\n})\n\ndescribe('Composition', () => {\n  it(\n    'should be possible to go to the next item containing a Dialog component',\n    suppressConsoleLogs(async () => {\n      renderTemplate({\n        template: `\n          <TabGroup>\n            <TabList>\n              <Tab>Tab 1</Tab>\n              <Tab>Tab 2</Tab>\n              <Tab>Tab 3</Tab>\n            </TabList>\n\n            <TabPanels>\n              <TabPanel data-panel=\"0\">Content 1</TabPanel>\n              <TabPanel data-panel=\"1\">\n                <button>open</button>\n                <Dialog :open=\"false\" @close=\"noop\" />\n              </TabPanel>\n              <TabPanel data-panel=\"2\">Content 3</TabPanel>\n            </TabPanels>\n          </TabGroup>\n        `,\n        setup: () => ({\n          noop: console.log,\n        }),\n      })\n\n      await new Promise<void>(nextTick)\n\n      assertActiveElement(document.body)\n\n      await press(Keys.Tab)\n      assertTabs({ active: 0 })\n\n      // Navigate to Dialog tab\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 1 })\n\n      // Focus on to the Dialog panel\n      await press(Keys.Tab)\n      assertActiveElement(document.querySelector('[data-panel=\"1\"]'))\n\n      // Focus on to the Dialog trigger button\n      await press(Keys.Tab)\n      assertActiveElement(getByText('open'))\n\n      // Focus back to the panel\n      await press(shift(Keys.Tab))\n      assertActiveElement(document.querySelector('[data-panel=\"1\"]'))\n\n      // Focus back to tabs\n      await press(shift(Keys.Tab))\n      assertTabs({ active: 1 })\n\n      // Navigate to the next tab\n      await press(Keys.ArrowRight)\n      assertTabs({ active: 2 })\n\n      // Focus on to the content panel\n      await press(Keys.Tab)\n      assertActiveElement(document.querySelector('[data-panel=\"2\"]'))\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/tabs/tabs.ts",
    "content": "import {\n  Fragment,\n  computed,\n  defineComponent,\n  h,\n  inject,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  watch,\n  watchEffect,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport { useResolveButtonType } from '../../hooks/use-resolve-button-type'\nimport { FocusSentinel } from '../../internal/focus-sentinel'\nimport { Hidden } from '../../internal/hidden'\nimport { Keys } from '../../keyboard'\nimport { dom } from '../../utils/dom'\nimport { Focus, FocusResult, focusIn, sortByDomNode } from '../../utils/focus-management'\nimport { match } from '../../utils/match'\nimport { microTask } from '../../utils/micro-task'\nimport { getOwnerDocument } from '../../utils/owner'\nimport { Features, omit, render } from '../../utils/render'\n\nenum Direction {\n  Forwards,\n  Backwards,\n}\n\nenum Ordering {\n  Less = -1,\n  Equal = 0,\n  Greater = 1,\n}\n\ntype StateDefinition = {\n  // State\n  selectedIndex: Ref<number | null>\n  orientation: Ref<'vertical' | 'horizontal'>\n  activation: Ref<'auto' | 'manual'>\n\n  tabs: Ref<Ref<HTMLElement | null>[]>\n  panels: Ref<Ref<HTMLElement | null>[]>\n\n  // State mutators\n  setSelectedIndex(index: number): void\n  registerTab(tab: Ref<HTMLElement | null>): void\n  unregisterTab(tab: Ref<HTMLElement | null>): void\n  registerPanel(panel: Ref<HTMLElement | null>): void\n  unregisterPanel(panel: Ref<HTMLElement | null>): void\n}\n\nlet TabsContext = Symbol('TabsContext') as InjectionKey<StateDefinition>\n\nfunction useTabsContext(component: string) {\n  let context = inject(TabsContext, null)\n\n  if (context === null) {\n    let err = new Error(`<${component} /> is missing a parent <TabGroup /> component.`)\n    if (Error.captureStackTrace) Error.captureStackTrace(err, useTabsContext)\n    throw err\n  }\n\n  return context\n}\n\nlet TabsSSRContext = Symbol('TabsSSRContext') as InjectionKey<\n  Ref<{ tabs: string[]; panels: string[] } | null>\n>\n\n// ---\n\nexport let TabGroup = defineComponent({\n  name: 'TabGroup',\n  emits: {\n    change: (_index: number) => true,\n  },\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    selectedIndex: { type: [Number], default: null },\n    defaultIndex: { type: [Number], default: 0 },\n    vertical: { type: [Boolean], default: false },\n    manual: { type: [Boolean], default: false },\n  },\n  inheritAttrs: false,\n  setup(props, { slots, attrs, emit }) {\n    let selectedIndex = ref<StateDefinition['selectedIndex']['value']>(\n      props.selectedIndex ?? props.defaultIndex\n    )\n    let tabs = ref<StateDefinition['tabs']['value']>([])\n    let panels = ref<StateDefinition['panels']['value']>([])\n\n    let isControlled = computed(() => props.selectedIndex !== null)\n    let realSelectedIndex = computed(() =>\n      isControlled.value ? props.selectedIndex : selectedIndex.value\n    )\n\n    function setSelectedIndex(indexToSet: number) {\n      let tabs = sortByDomNode(api.tabs.value, dom)\n      let panels = sortByDomNode(api.panels.value, dom)\n\n      let focusableTabs = tabs.filter((tab) => !dom(tab)?.hasAttribute('disabled'))\n\n      if (\n        // Underflow\n        indexToSet < 0 ||\n        // Overflow\n        indexToSet > tabs.length - 1\n      ) {\n        let direction = match(\n          selectedIndex.value === null // Not set yet\n            ? Ordering.Equal\n            : Math.sign(indexToSet - selectedIndex.value!),\n          {\n            [Ordering.Less]: () => Direction.Backwards,\n            [Ordering.Equal]: () => {\n              return match(Math.sign(indexToSet), {\n                [Ordering.Less]: () => Direction.Forwards,\n                [Ordering.Equal]: () => Direction.Forwards,\n                [Ordering.Greater]: () => Direction.Backwards,\n              })\n            },\n            [Ordering.Greater]: () => Direction.Forwards,\n          }\n        )\n\n        let nextSelectedIndex = match(direction, {\n          [Direction.Forwards]: () => tabs.indexOf(focusableTabs[0]),\n          [Direction.Backwards]: () => tabs.indexOf(focusableTabs[focusableTabs.length - 1]),\n        })\n        if (nextSelectedIndex !== -1) {\n          selectedIndex.value = nextSelectedIndex\n        }\n        api.tabs.value = tabs\n        api.panels.value = panels\n      }\n\n      // Middle\n      else {\n        let before = tabs.slice(0, indexToSet)\n        let after = tabs.slice(indexToSet)\n\n        let next = [...after, ...before].find((tab) => focusableTabs.includes(tab))\n        if (!next) return\n\n        let localSelectedIndex = tabs.indexOf(next) ?? api.selectedIndex.value\n        if (localSelectedIndex === -1) localSelectedIndex = api.selectedIndex.value\n\n        selectedIndex.value = localSelectedIndex\n        api.tabs.value = tabs\n        api.panels.value = panels\n      }\n    }\n\n    let api = {\n      selectedIndex: computed(() => selectedIndex.value ?? props.defaultIndex ?? null),\n      orientation: computed(() => (props.vertical ? 'vertical' : 'horizontal')),\n      activation: computed(() => (props.manual ? 'manual' : 'auto')),\n      tabs,\n      panels,\n      setSelectedIndex(index: number) {\n        if (realSelectedIndex.value !== index) {\n          emit('change', index)\n        }\n\n        if (!isControlled.value) {\n          setSelectedIndex(index)\n        }\n      },\n      registerTab(tab: (typeof tabs)['value'][number]) {\n        if (tabs.value.includes(tab)) return\n        let activeTab = tabs.value[selectedIndex.value!]\n\n        tabs.value.push(tab)\n        tabs.value = sortByDomNode(tabs.value, dom)\n\n        // When the component is uncontrolled, then we want to maintain the\n        // actively selected tab even if new tabs are inserted or removed before\n        // the active tab.\n        //\n        // When the component is controlled, then we don't want to do this and\n        // instead we want to select the tab based on the `selectedIndex` prop.\n        if (!isControlled.value) {\n          let localSelectedIndex = tabs.value.indexOf(activeTab) ?? selectedIndex.value\n\n          if (localSelectedIndex !== -1) {\n            selectedIndex.value = localSelectedIndex\n          }\n        }\n      },\n      unregisterTab(tab: (typeof tabs)['value'][number]) {\n        let idx = tabs.value.indexOf(tab)\n        if (idx !== -1) tabs.value.splice(idx, 1)\n      },\n      registerPanel(panel: (typeof panels)['value'][number]) {\n        if (panels.value.includes(panel)) return\n        panels.value.push(panel)\n        panels.value = sortByDomNode(panels.value, dom)\n      },\n      unregisterPanel(panel: (typeof panels)['value'][number]) {\n        let idx = panels.value.indexOf(panel)\n        if (idx !== -1) panels.value.splice(idx, 1)\n      },\n    }\n\n    provide(TabsContext, api)\n\n    let SSRCounter = ref({ tabs: [], panels: [] })\n    let mounted = ref(false)\n    onMounted(() => {\n      mounted.value = true\n    })\n    provide(\n      TabsSSRContext,\n      computed(() => (mounted.value ? null : SSRCounter.value))\n    )\n\n    let incomingSelectedIndex = computed(() => props.selectedIndex)\n\n    onMounted(() => {\n      watch(\n        [incomingSelectedIndex /* Deliberately skipping defaultIndex */],\n        () => setSelectedIndex(props.selectedIndex ?? props.defaultIndex),\n        { immediate: true }\n      )\n    })\n\n    watchEffect(() => {\n      if (!isControlled.value) return\n      if (realSelectedIndex.value == null) return\n      if (api.tabs.value.length <= 0) return\n\n      let sorted = sortByDomNode(api.tabs.value, dom)\n      let didOrderChange = sorted.some((tab, i) => dom(api.tabs.value[i]) !== dom(tab))\n\n      if (didOrderChange) {\n        api.setSelectedIndex(\n          sorted.findIndex((x) => dom(x) === dom(api.tabs.value[realSelectedIndex.value!]))\n        )\n      }\n    })\n\n    return () => {\n      let slot = { selectedIndex: selectedIndex.value }\n\n      return h(Fragment, [\n        tabs.value.length <= 0 &&\n          h(FocusSentinel, {\n            onFocus: () => {\n              for (let tab of tabs.value) {\n                let el = dom(tab)\n                if (el?.tabIndex === 0) {\n                  el.focus()\n                  return true\n                }\n              }\n\n              return false\n            },\n          }),\n        render({\n          theirProps: {\n            ...attrs,\n            ...omit(props, ['selectedIndex', 'defaultIndex', 'manual', 'vertical', 'onChange']),\n          },\n          ourProps: {},\n          slot,\n          slots,\n          attrs,\n          name: 'TabGroup',\n        }),\n      ])\n    }\n  },\n})\n\n// ---\n\nexport let TabList = defineComponent({\n  name: 'TabList',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n  },\n  setup(props, { attrs, slots }) {\n    let api = useTabsContext('TabList')\n\n    return () => {\n      let slot = { selectedIndex: api.selectedIndex.value }\n\n      let ourProps = {\n        role: 'tablist',\n        'aria-orientation': api.orientation.value,\n      }\n      let theirProps = props\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'TabList',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let Tab = defineComponent({\n  name: 'Tab',\n  props: {\n    as: { type: [Object, String], default: 'button' },\n    disabled: { type: [Boolean], default: false },\n    id: { type: String, default: () => `headlessui-tabs-tab-${useId()}` },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useTabsContext('Tab')\n\n    let internalTabRef = ref<HTMLElement | null>(null)\n\n    expose({ el: internalTabRef, $el: internalTabRef })\n\n    onMounted(() => api.registerTab(internalTabRef))\n    onUnmounted(() => api.unregisterTab(internalTabRef))\n\n    let SSRContext = inject(TabsSSRContext)!\n    // Note: there's a divergence here between React and Vue. Vue can work with `indexOf` implementation while React on the server can't.\n    let mySSRIndex = computed(() => {\n      if (SSRContext.value) {\n        let mySSRIndex = SSRContext.value.tabs.indexOf(props.id)\n        if (mySSRIndex === -1) return SSRContext.value.tabs.push(props.id) - 1\n        return mySSRIndex\n      }\n\n      return -1\n    })\n\n    let myIndex = computed(() => {\n      let myIndex = api.tabs.value.indexOf(internalTabRef)\n      if (myIndex === -1) return mySSRIndex.value\n      return myIndex\n    })\n    let selected = computed(() => myIndex.value === api.selectedIndex.value)\n\n    function activateUsing(cb: () => FocusResult) {\n      let result = cb()\n      if (result === FocusResult.Success && api.activation.value === 'auto') {\n        let newTab = getOwnerDocument(internalTabRef)?.activeElement\n        let idx = api.tabs.value.findIndex((tab) => dom(tab) === newTab)\n        if (idx !== -1) api.setSelectedIndex(idx)\n      }\n      return result\n    }\n\n    function handleKeyDown(event: KeyboardEvent) {\n      let list = api.tabs.value.map((tab) => dom(tab)).filter(Boolean) as HTMLElement[]\n\n      if (event.key === Keys.Space || event.key === Keys.Enter) {\n        event.preventDefault()\n        event.stopPropagation()\n\n        api.setSelectedIndex(myIndex.value)\n        return\n      }\n\n      switch (event.key) {\n        case Keys.Home:\n        case Keys.PageUp:\n          event.preventDefault()\n          event.stopPropagation()\n\n          return activateUsing(() => focusIn(list, Focus.First))\n\n        case Keys.End:\n        case Keys.PageDown:\n          event.preventDefault()\n          event.stopPropagation()\n\n          return activateUsing(() => focusIn(list, Focus.Last))\n      }\n\n      let result = activateUsing(() =>\n        match(api.orientation.value, {\n          vertical() {\n            if (event.key === Keys.ArrowUp) return focusIn(list, Focus.Previous | Focus.WrapAround)\n            if (event.key === Keys.ArrowDown) return focusIn(list, Focus.Next | Focus.WrapAround)\n            return FocusResult.Error\n          },\n          horizontal() {\n            if (event.key === Keys.ArrowLeft)\n              return focusIn(list, Focus.Previous | Focus.WrapAround)\n            if (event.key === Keys.ArrowRight) return focusIn(list, Focus.Next | Focus.WrapAround)\n            return FocusResult.Error\n          },\n        })\n      )\n\n      if (result === FocusResult.Success) {\n        return event.preventDefault()\n      }\n    }\n\n    let ready = ref(false)\n    function handleSelection() {\n      if (ready.value) return\n      ready.value = true\n\n      if (props.disabled) return\n\n      dom(internalTabRef)?.focus({ preventScroll: true })\n      api.setSelectedIndex(myIndex.value)\n\n      microTask(() => {\n        ready.value = false\n      })\n    }\n\n    // This is important because we want to only focus the tab when it gets focus\n    // OR it finished the click event (mouseup). However, if you perform a `click`,\n    // then you will first get the `focus` and then get the `click` event.\n    function handleMouseDown(event: MouseEvent) {\n      event.preventDefault()\n    }\n\n    let type = useResolveButtonType(\n      computed(() => ({ as: props.as, type: attrs.type })),\n      internalTabRef\n    )\n\n    return () => {\n      let slot = { selected: selected.value, disabled: props.disabled ?? false }\n      let { id, ...theirProps } = props\n      let ourProps = {\n        ref: internalTabRef,\n        onKeydown: handleKeyDown,\n        onMousedown: handleMouseDown,\n        onClick: handleSelection,\n        id,\n        role: 'tab',\n        type: type.value,\n        'aria-controls': dom(api.panels.value[myIndex.value])?.id,\n        'aria-selected': selected.value,\n        tabIndex: selected.value ? 0 : -1,\n        disabled: props.disabled ? true : undefined,\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        name: 'Tab',\n      })\n    }\n  },\n})\n\n// ---\n\nexport let TabPanels = defineComponent({\n  name: 'TabPanels',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n  },\n  setup(props, { slots, attrs }) {\n    let api = useTabsContext('TabPanels')\n\n    return () => {\n      let slot = { selectedIndex: api.selectedIndex.value }\n\n      return render({\n        theirProps: props,\n        ourProps: {},\n        slot,\n        attrs,\n        slots,\n        name: 'TabPanels',\n      })\n    }\n  },\n})\n\nexport let TabPanel = defineComponent({\n  name: 'TabPanel',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    static: { type: Boolean, default: false },\n    unmount: { type: Boolean, default: true },\n    id: { type: String, default: () => `headlessui-tabs-panel-${useId()}` },\n    tabIndex: { type: Number, default: 0 },\n  },\n  setup(props, { attrs, slots, expose }) {\n    let api = useTabsContext('TabPanel')\n\n    let internalPanelRef = ref<HTMLElement | null>(null)\n\n    expose({ el: internalPanelRef, $el: internalPanelRef })\n\n    onMounted(() => api.registerPanel(internalPanelRef))\n    onUnmounted(() => api.unregisterPanel(internalPanelRef))\n\n    let SSRContext = inject(TabsSSRContext)!\n    let mySSRIndex = computed(() => {\n      if (SSRContext.value) {\n        let mySSRIndex = SSRContext.value.panels.indexOf(props.id)\n        if (mySSRIndex === -1) return SSRContext.value.panels.push(props.id) - 1\n        return mySSRIndex\n      }\n\n      return -1\n    })\n\n    let myIndex = computed(() => {\n      let myIndex = api.panels.value.indexOf(internalPanelRef)\n      if (myIndex === -1) return mySSRIndex.value\n      return myIndex\n    })\n    let selected = computed(() => myIndex.value === api.selectedIndex.value)\n\n    return () => {\n      let slot = { selected: selected.value }\n      let { id, tabIndex, ...theirProps } = props\n      let ourProps = {\n        ref: internalPanelRef,\n        id,\n        role: 'tabpanel',\n        'aria-labelledby': dom(api.tabs.value[myIndex.value])?.id,\n        tabIndex: selected.value ? tabIndex : -1,\n      }\n\n      if (!selected.value && props.unmount && !props.static) {\n        return h(Hidden, { as: 'span', 'aria-hidden': true, ...ourProps })\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot,\n        attrs,\n        slots,\n        features: Features.Static | Features.RenderStrategy,\n        visible: selected.value,\n        name: 'TabPanel',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/__snapshots__/transition.test.ts.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Events should fire events for all the stages 1`] = `\n\"Render 1:\n    -  <!---->\n    +  <div\n    +    class=\\\\\"enter enter-from\\\\\"\n    +  >\n    +    <span>\n    +      Hello!\n    +    </span>\n    +  </div>\n\nRender 2:\n    -  class=\\\\\"enter enter-from\\\\\"\n    +  class=\\\\\"enter enter-to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter enter-to\\\\\"\n    +  class=\\\\\"\\\\\"\n\nRender 4:\n    -  class=\\\\\"\\\\\"\n    +  class=\\\\\"leave leave-from\\\\\"\n\nRender 5:\n    -  class=\\\\\"leave leave-from\\\\\"\n    +  class=\\\\\"leave leave-to\\\\\"\n\nRender 6: Transition took at least 75ms (yes)\n    -  <div\n    -    class=\\\\\"leave leave-to\\\\\"\n    -  >\n    -    <span>\n    -      Hello!\n    -    </span>\n    -  </div>\n    +  <!---->\"\n`;\n\nexports[`Setup API nested should be possible to change the underlying DOM tag of the Transition component and TransitionChild components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <article>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </article>\n</div>\n`;\n\nexports[`Setup API nested should be possible to change the underlying DOM tag of the TransitionChild components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </div>\n</div>\n`;\n\nexports[`Setup API nested should be possible to nest transition components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <div>\n      Sidebar\n    </div>\n    <div>\n      Content\n    </div>\n  </div>\n</div>\n`;\n\nexports[`Setup API nested should be possible to use render props on the Transition and TransitionChild components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <article>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </article>\n</div>\n`;\n\nexports[`Setup API nested should be possible to use render props on the TransitionChild components 1`] = `\n<div\n  class=\"My Page\"\n>\n  <div>\n    <aside>\n      Sidebar\n    </aside>\n    <section>\n      Content\n    </section>\n  </div>\n</div>\n`;\n\nexports[`Setup API shallow should be possible to change the underlying DOM tag 1`] = `\n<a>\n   Children \n</a>\n`;\n\nexports[`Setup API shallow should passthrough all the props (that we do not use internally) 1`] = `\n<div\n  class=\"text-blue-400\"\n  id=\"root\"\n>\n   Children \n</div>\n`;\n\nexports[`Setup API shallow should passthrough all the props (that we do not use internally) even when using an \\`as\\` prop 1`] = `\n<a\n  class=\"text-blue-400\"\n  href=\"/\"\n>\n   Children \n</a>\n`;\n\nexports[`Setup API shallow should render a div and its children by default 1`] = `\n<div>\n  Children\n</div>\n`;\n\nexports[`Setup API shallow should render another component if the \\`as\\` prop is used and its children by default 1`] = `\n<a>\n   Children \n</a>\n`;\n\nexports[`Setup API shallow should render nothing when the show prop is false 1`] = `<!---->`;\n\nexports[`Setup API transition classes should be possible to passthrough the transition classes 1`] = `\n<div>\n   Children \n</div>\n`;\n\nexports[`Setup API transition classes should be possible to passthrough the transition classes and immediately apply the enter transitions when appear is set to true 1`] = `\n<div\n  class=\"enter enter-from\"\n>\n   Children \n</div>\n`;\n\nexports[`Transitions nested transitions should not unmount the whole tree when some children are still transitioning 1`] = `\n\"Render 1:\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave-fast leave-from\\\\\"\n    +  >\n    ---\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave-slow leave-from\\\\\"\n    +  >\n\nRender 2:\n    -  class=\\\\\"leave-fast leave-from\\\\\"\n    +  class=\\\\\"leave-fast leave-to\\\\\"\n    ---\n    -  class=\\\\\"leave-slow leave-from\\\\\"\n    +  class=\\\\\"leave-slow leave-to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  <div\n    -    class=\\\\\"leave-fast leave-to\\\\\"\n    -  >\n    -     I am fast \n    -  </div>\n    +  <!---->\n\nRender 4: Transition took at least 100ms (yes)\n    -  <div>\n    ---\n    -    <div\n    -      class=\\\\\"leave-slow leave-to\\\\\"\n    -    >\n    -       I am slow \n    -    </div>\n    -  </div>\"\n`;\n\nexports[`Transitions nested transitions should not unmount the whole tree when some children are still transitioning 2`] = `\n\"Render 1:\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave-fast leave-from\\\\\"\n    +  >\n    ---\n    -    <div>\n    +    <div\n    +      class=\\\\\"leave-slow\\\\\"\n    +    >\n    ---\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave-slow leave-from\\\\\"\n    +  >\n\nRender 2:\n    -  class=\\\\\"leave-fast leave-from\\\\\"\n    +  class=\\\\\"leave-fast leave-to\\\\\"\n    ---\n    -  class=\\\\\"leave-slow leave-from\\\\\"\n    +  class=\\\\\"leave-slow leave-to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  <div\n    -    class=\\\\\"leave-fast leave-to\\\\\"\n    -  >\n    -    <span>\n    -      I am fast\n    -    </span>\n    -    <div\n    -      class=\\\\\"leave-slow\\\\\"\n    -    >\n    -       I am my own root component and I don't talk to the parent \n    -    </div>\n    -  </div>\n    +  <!---->\n\nRender 4: Transition took at least 100ms (yes)\n    -  <div>\n    ---\n    -    <div\n    -      class=\\\\\"leave-slow leave-to\\\\\"\n    -    >\n    -       I am slow \n    -    </div>\n    -  </div>\"\n`;\n\nexports[`Transitions shallow transitions should transition in and out completely (render strategy = hidden) 1`] = `\n\"Render 1:\n    -  hidden=\\\\\"\\\\\"\n    -  style=\\\\\"display: none;\\\\\"\n    +  class=\\\\\"enter enter-from\\\\\"\n\nRender 2:\n    -  class=\\\\\"enter enter-from\\\\\"\n    +  class=\\\\\"enter enter-to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter enter-to\\\\\"\n    +  class=\\\\\"\\\\\"\n\nRender 4:\n    -  class=\\\\\"\\\\\"\n    +  class=\\\\\"leave leave-from\\\\\"\n\nRender 5:\n    -  class=\\\\\"leave leave-from\\\\\"\n    +  class=\\\\\"leave leave-to\\\\\"\n\nRender 6: Transition took at least 75ms (yes)\n    -  class=\\\\\"leave leave-to\\\\\"\n    +  class=\\\\\"\\\\\"\n    +  hidden=\\\\\"\\\\\"\n    +  style=\\\\\"display: none;\\\\\"\n\nRender 7:\n    -  class=\\\\\"\\\\\"\n    -  hidden=\\\\\"\\\\\"\n    -  style=\\\\\"display: none;\\\\\"\n    +  class=\\\\\"enter enter-from\\\\\"\n\nRender 8:\n    -  class=\\\\\"enter enter-from\\\\\"\n    +  class=\\\\\"enter enter-to\\\\\"\n\nRender 9: Transition took at least 75ms (yes)\n    -  class=\\\\\"enter enter-to\\\\\"\n    +  class=\\\\\"\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition in and out completely 1`] = `\n\"Render 1:\n    -  <!---->\n    +  <div\n    +    class=\\\\\"enter enter-from\\\\\"\n    +  >\n    +    <span>\n    +      Hello!\n    +    </span>\n    +  </div>\n\nRender 2:\n    -  class=\\\\\"enter enter-from\\\\\"\n    +  class=\\\\\"enter enter-to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter enter-to\\\\\"\n    +  class=\\\\\"\\\\\"\n\nRender 4:\n    -  class=\\\\\"\\\\\"\n    +  class=\\\\\"leave leave-from\\\\\"\n\nRender 5:\n    -  class=\\\\\"leave leave-from\\\\\"\n    +  class=\\\\\"leave leave-to\\\\\"\n\nRender 6: Transition took at least 75ms (yes)\n    -  <div\n    -    class=\\\\\"leave leave-to\\\\\"\n    -  >\n    -    <span>\n    -      Hello!\n    -    </span>\n    -  </div>\n    +  <!---->\"\n`;\n\nexports[`Transitions shallow transitions should transition in completely (duration defined in milliseconds) 1`] = `\n\"Render 1:\n    -  <!---->\n    +  <div\n    +    class=\\\\\"enter from\\\\\"\n    +  >\n    +    <span>\n    +      Hello!\n    +    </span>\n    +  </div>\n\nRender 2:\n    -  class=\\\\\"enter from\\\\\"\n    +  class=\\\\\"enter to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter to\\\\\"\n    +  class=\\\\\"\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition in completely (duration defined in seconds) 1`] = `\n\"Render 1:\n    -  <!---->\n    +  <div\n    +    class=\\\\\"enter from\\\\\"\n    +  >\n    +    <span>\n    +      Hello!\n    +    </span>\n    +  </div>\n\nRender 2:\n    -  class=\\\\\"enter from\\\\\"\n    +  class=\\\\\"enter to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter to\\\\\"\n    +  class=\\\\\"\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition in completely (duration defined in seconds) in (render strategy = hidden) 1`] = `\n\"Render 1:\n    -  hidden=\\\\\"\\\\\"\n    -  style=\\\\\"display: none;\\\\\"\n    +  class=\\\\\"enter from\\\\\"\n\nRender 2:\n    -  class=\\\\\"enter from\\\\\"\n    +  class=\\\\\"enter to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter to\\\\\"\n    +  class=\\\\\"\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition in completely 1`] = `\n\"Render 1:\n    -  <!---->\n    +  <div\n    +    class=\\\\\"enter from\\\\\"\n    +  >\n    +    <span>\n    +      Hello!\n    +    </span>\n    +  </div>\n\nRender 2:\n    -  class=\\\\\"enter from\\\\\"\n    +  class=\\\\\"enter to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"enter to\\\\\"\n    +  class=\\\\\"\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition out completely (render strategy = hidden) 1`] = `\n\"Render 1:\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave from\\\\\"\n    +  >\n\nRender 2:\n    -  class=\\\\\"leave from\\\\\"\n    +  class=\\\\\"leave to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  class=\\\\\"leave to\\\\\"\n    +  class=\\\\\"\\\\\"\n    +  hidden=\\\\\"\\\\\"\n    +  style=\\\\\"display: none;\\\\\"\"\n`;\n\nexports[`Transitions shallow transitions should transition out completely 1`] = `\n\"Render 1:\n    -  <div>\n    +  <div\n    +    class=\\\\\"leave from\\\\\"\n    +  >\n\nRender 2:\n    -  class=\\\\\"leave from\\\\\"\n    +  class=\\\\\"leave to\\\\\"\n\nRender 3: Transition took at least 50ms (yes)\n    -  <div\n    -    class=\\\\\"leave to\\\\\"\n    -  >\n    -    <span>\n    -      Hello!\n    -    </span>\n    -  </div>\n    +  <!---->\"\n`;\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/transition.ssr.test.ts",
    "content": "import { defineComponent } from 'vue'\nimport { html } from '../../test-utils/html'\nimport { renderSSR } from '../../test-utils/ssr'\nimport * as Transition from './transition'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n})\n\ndescribe('Rendering', () => {\n  describe('SSR', () => {\n    it('A transition without appear=true does not insert classes during SSR', async () => {\n      let result = await renderSSR(\n        defineComponent({\n          components: Transition,\n          template: html`\n            <TransitionRoot\n              as=\"template\"\n              :show=\"true\"\n              enter=\"enter\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n            >\n              <div class=\"inner\"></div>\n            </TransitionRoot>\n          `,\n        })\n      )\n\n      let div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner')\n\n      // If we don't await then we get the same for SSR and hydration\n      // but we want to investigate what effects this has on our other transition tests too\n      await result.hydrate()\n\n      div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner enter enter-from')\n    })\n\n    it('should not overwrite className of children when as=Fragment', async () => {\n      let result = await renderSSR(\n        defineComponent({\n          components: Transition,\n          template: html`\n            <TransitionRoot\n              as=\"template\"\n              :show=\"true\"\n              :appear=\"true\"\n              enter=\"enter\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n            >\n              <div class=\"inner\"></div>\n            </TransitionRoot>\n          `,\n        })\n      )\n\n      let div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner enter enter-from')\n\n      await result.hydrate()\n\n      div = document.querySelector('.inner')\n\n      expect(div).not.toBeNull()\n      expect(div?.className).toBe('inner enter enter-from')\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/transition.test.ts",
    "content": "import { defineComponent, onMounted, ref } from 'vue'\nimport { executeTimeline } from '../../test-utils/execute-timeline'\nimport { html } from '../../test-utils/html'\nimport { suppressConsoleLogs } from '../../test-utils/suppress-console-logs'\nimport { createRenderTemplate, fireEvent } from '../../test-utils/vue-testing-library'\nimport { TransitionChild, TransitionRoot } from './transition'\n\njest.mock('../../hooks/use-id')\n\nafterAll(() => jest.restoreAllMocks())\n\nconst renderTemplate = createRenderTemplate({ TransitionRoot, TransitionChild })\n\nfunction getByTestId(id: string) {\n  return document.querySelector(`[data-testid=\"${id}\"]`)! as HTMLElement\n}\n\nlet styles: HTMLElement[] = []\nafterEach(() => {\n  for (let style of styles.splice(0)) {\n    style.parentElement?.removeChild(style)\n  }\n})\n\nfunction withStyles(css: string) {\n  let style = document.createElement('style')\n  style.type = 'text/css'\n  style.innerHTML = css\n  document.head.appendChild(style)\n  styles.push(style)\n}\n\nit('should render without crashing', () => {\n  renderTemplate({\n    template: html`\n      <TransitionRoot :show=\"true\">\n        <div class=\"hello\">Children</div>\n      </TransitionRoot>\n    `,\n  })\n})\n\nit('should be possible to render a Transition without children', () => {\n  renderTemplate({\n    template: html` <TransitionRoot :show=\"true\" class=\"transition\" /> `,\n  })\n  expect(document.getElementsByClassName('transition')).not.toBeNull()\n})\n\nit(\n  'should yell at us when we forget the required show prop',\n  suppressConsoleLogs(() => {\n    expect.assertions(1)\n\n    renderTemplate({\n      template: html`\n        <TransitionRoot>\n          <div class=\"hello\">Children</div>\n        </TransitionRoot>\n      `,\n      errorCaptured(err) {\n        expect(err as Error).toEqual(\n          new Error('A <Transition /> is used but it is missing a `:show=\"true | false\"` prop.')\n        )\n\n        return false\n      },\n    })\n  })\n)\n\ndescribe('Setup API', () => {\n  describe('shallow', () => {\n    it('should render a div and its children by default', () => {\n      let { container } = renderTemplate({\n        template: html`<TransitionRoot :show=\"true\">Children</TransitionRoot>`,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should passthrough all the props (that we do not use internally)', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <TransitionRoot :show=\"true\" id=\"root\" class=\"text-blue-400\"> Children </TransitionRoot>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should render another component if the `as` prop is used and its children by default', () => {\n      let { container } = renderTemplate({\n        template: html` <TransitionRoot :show=\"true\" as=\"a\"> Children </TransitionRoot> `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should passthrough all the props (that we do not use internally) even when using an `as` prop', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <TransitionRoot :show=\"true\" as=\"a\" href=\"/\" class=\"text-blue-400\">\n            Children\n          </TransitionRoot>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should render nothing when the show prop is false', () => {\n      let { container } = renderTemplate({\n        template: html` <TransitionRoot :show=\"false\">Children</TransitionRoot> `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag', () => {\n      let { container } = renderTemplate({\n        template: html` <TransitionRoot :show=\"true\" as=\"a\"> Children </TransitionRoot> `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n  })\n\n  describe('nested', () => {\n    it(\n      'should yell at us when we forget to wrap the `<TransitionChild />` in a parent <Transition /> component',\n      suppressConsoleLogs(() => {\n        expect.assertions(1)\n\n        renderTemplate({\n          template: html`\n            <div class=\"My Page\">\n              <TransitionChild>Oops</TransitionChild>\n            </div>\n          `,\n          errorCaptured(err) {\n            expect(err as Error).toEqual(\n              new Error(\n                'A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.'\n              )\n            )\n            return false\n          },\n        })\n      })\n    )\n\n    it('should be possible to render a TransitionChild without children', () => {\n      renderTemplate({\n        template: html`\n          <TransitionRoot :show=\"true\">\n            <TransitionChild class=\"transition\" />\n          </TransitionRoot>\n        `,\n      })\n      expect(document.getElementsByClassName('transition')).not.toBeNull()\n    })\n\n    it('should be possible to nest transition components', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <div class=\"My Page\">\n            <TransitionRoot :show=\"true\">\n              <TransitionChild>Sidebar</TransitionChild>\n              <TransitionChild>Content</TransitionChild>\n            </TransitionRoot>\n          </div>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag of the TransitionChild components', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <div class=\"My Page\">\n            <TransitionRoot :show=\"true\">\n              <TransitionChild as=\"aside\">Sidebar</TransitionChild>\n              <TransitionChild as=\"section\">Content</TransitionChild>\n            </TransitionRoot>\n          </div>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to change the underlying DOM tag of the Transition component and TransitionChild components', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <div class=\"My Page\">\n            <TransitionRoot :show=\"true\" as=\"article\">\n              <TransitionChild as=\"aside\">Sidebar</TransitionChild>\n              <TransitionChild as=\"section\">Content</TransitionChild>\n            </TransitionRoot>\n          </div>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to use render props on the TransitionChild components', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <div class=\"My Page\">\n            <TransitionRoot :show=\"true\">\n              <TransitionChild as=\"template\" v-slot=\"\"><aside>Sidebar</aside></TransitionChild>\n              <TransitionChild as=\"template\" v-slot=\"\"><section>Content</section></TransitionChild>\n            </TransitionRoot>\n          </div>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to use render props on the Transition and TransitionChild components', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <div class=\"My Page\">\n            <TransitionRoot :show=\"true\" as=\"template\">\n              <article>\n                <TransitionChild as=\"template\" v-slot=\"\">\n                  <aside>Sidebar</aside>\n                </TransitionChild>\n                <TransitionChild as=\"template\" v-slot=\"\">\n                  <section>Content</section>\n                </TransitionChild>\n              </article>\n            </TransitionRoot>\n          </div>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it(\n      'should yell at us when we forgot to forward the ref on one of the TransitionChild components',\n      suppressConsoleLogs(() => {\n        expect.hasAssertions()\n\n        let Dummy = defineComponent({\n          setup() {\n            return () => null\n          },\n        })\n\n        renderTemplate({\n          components: { TransitionRoot, TransitionChild, Dummy },\n          template: html`\n            <div class=\"My Page\">\n              <TransitionRoot :show=\"true\">\n                <TransitionChild as=\"template\"><Dummy>Sidebar</Dummy></TransitionChild>\n                <TransitionChild as=\"template\"><Dummy>Content</Dummy></TransitionChild>\n              </TransitionRoot>\n            </div>\n          `,\n          errorCaptured(err) {\n            expect(err as Error).toEqual(\n              new Error('Did you forget to passthrough the `ref` to the actual DOM node?')\n            )\n            return false\n          },\n        })\n      })\n    )\n  })\n\n  describe('transition classes', () => {\n    it('should be possible to passthrough the transition classes', () => {\n      let { container } = renderTemplate({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot\n            :show=\"true\"\n            enter=\"enter\"\n            enterFrom=\"enter-from\"\n            enterTo=\"enter-to\"\n            leave=\"leave\"\n            leaveFrom=\"leave-from\"\n            leaveTo=\"leave-to\"\n          >\n            Children\n          </TransitionRoot>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n\n    it('should be possible to passthrough the transition classes and immediately apply the enter transitions when appear is set to true', () => {\n      let { container } = renderTemplate({\n        template: html`\n          <TransitionRoot\n            :show=\"true\"\n            :appear=\"true\"\n            enter=\"enter\"\n            enterFrom=\"enter-from\"\n            enterTo=\"enter-to\"\n            leave=\"leave\"\n            leaveFrom=\"leave-from\"\n            leaveTo=\"leave-to\"\n          >\n            Children\n          </TransitionRoot>\n        `,\n      })\n\n      expect(container.firstChild).toMatchSnapshot()\n    })\n  })\n})\n\ndescribe('Transitions', () => {\n  describe('shallow transitions', () => {\n    it('should transition in completely (duration defined in milliseconds)', async () => {\n      let enterDuration = 50\n\n      withStyles(`\n        .enter { transition-duration: ${enterDuration}ms; }\n        .from { opacity: 0%; }\n        .to { opacity: 100%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot :show=\"show\" enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n            <span>Hello!</span>\n          </TransitionRoot>\n\n          <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n        `,\n        setup() {\n          let show = ref(false)\n          return { show }\n        },\n      })\n\n      let timeline = await executeTimeline(Example, [\n        // Toggle to show\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    it('should transition in completely (duration defined in seconds)', async () => {\n      let enterDuration = 50\n\n      withStyles(`\n        .enter { transition-duration: ${enterDuration / 1000}s; }\n        .from { opacity: 0%; }\n        .to { opacity: 100%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot :show=\"show\" enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n            <span>Hello!</span>\n          </TransitionRoot>\n\n          <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n        `,\n        setup() {\n          let show = ref(false)\n          return { show }\n        },\n      })\n\n      let timeline = await executeTimeline(Example, [\n        // Toggle to show\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    it('should transition in completely (duration defined in seconds) in (render strategy = hidden)', async () => {\n      let enterDuration = 50\n\n      withStyles(`\n        .enter { transition-duration: ${enterDuration / 1000}s; }\n        .from { opacity: 0%; }\n        .to { opacity: 100%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot :show=\"show\" :unmount=\"false\" enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n            <span>Hello!</span>\n          </TransitionRoot>\n\n          <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n        `,\n        setup() {\n          let show = ref(false)\n          return { show }\n        },\n      })\n\n      let timeline = await executeTimeline(Example, [\n        // Toggle to show\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    it('should transition in completely', async () => {\n      let enterDuration = 50\n\n      withStyles(`\n        .enter { transition-duration: ${enterDuration}ms; }\n        .from { opacity: 0%; }\n        .to { opacity: 100%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot :show=\"show\" enter=\"enter\" enterFrom=\"from\" enterTo=\"to\">\n            <span>Hello!</span>\n          </TransitionRoot>\n\n          <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n        `,\n        setup() {\n          let show = ref(false)\n          return { show }\n        },\n      })\n\n      let timeline = await executeTimeline(Example, [\n        // Toggle to show\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n    })\n\n    it(\n      'should transition out completely',\n      suppressConsoleLogs(async () => {\n        let leaveDuration = 50\n\n        withStyles(`\n          .leave { transition-duration: ${leaveDuration}ms; }\n          .from { opacity: 0%; }\n          .to { opacity: 100%; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot },\n          template: html`\n            <TransitionRoot :show=\"show\" leave=\"leave\" leaveFrom=\"from\" leaveTo=\"to\">\n              <span>Hello!</span>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(true)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    it(\n      'should transition out completely (render strategy = hidden)',\n      suppressConsoleLogs(async () => {\n        let leaveDuration = 50\n\n        withStyles(`\n          .leave { transition-duration: ${leaveDuration}ms; }\n          .from { opacity: 0%; }\n          .to { opacity: 100%; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot },\n          template: html`\n            <TransitionRoot\n              :show=\"show\"\n              :unmount=\"false\"\n              leave=\"leave\"\n              leaveFrom=\"from\"\n              leaveTo=\"to\"\n            >\n              <span>Hello!</span>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(true)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    it(\n      'should transition in and out completely',\n      suppressConsoleLogs(async () => {\n        let enterDuration = 50\n        let leaveDuration = 75\n\n        withStyles(`\n          .enter { transition-duration: ${enterDuration}ms; }\n          .enter-from { opacity: 0%; }\n          .enter-to { opacity: 100%; }\n\n          .leave { transition-duration: ${leaveDuration}ms; }\n          .leave-from { opacity: 100%; }\n          .leave-to { opacity: 0%; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot },\n          template: html`\n            <TransitionRoot\n              :show=\"show\"\n              enter=\"enter\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            >\n              <span>Hello!</span>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(false)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to show\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(enterDuration)\n          },\n\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    it(\n      'should transition in and out completely (render strategy = hidden)',\n      suppressConsoleLogs(async () => {\n        let enterDuration = 50\n        let leaveDuration = 75\n\n        withStyles(`\n          .enter { transition-duration: ${enterDuration}ms; }\n          .enter-from { opacity: 0%; }\n          .enter-to { opacity: 100%; }\n\n          .leave { transition-duration: ${leaveDuration}ms; }\n          .leave-from { opacity: 100%; }\n          .leave-to { opacity: 0%; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot },\n          template: html`\n            <TransitionRoot\n              :show=\"show\"\n              :unmount=\"false\"\n              enter=\"enter\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            >\n              <span>Hello!</span>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(false)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to show\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(enterDuration)\n          },\n\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n\n          // Toggle to show\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return executeTimeline.fullTransition(leaveDuration)\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n  })\n\n  describe('nested transitions', () => {\n    it(\n      'should not unmount the whole tree when some children are still transitioning',\n      suppressConsoleLogs(async () => {\n        let slowLeaveDuration = 150\n        let fastLeaveDuration = 50\n\n        withStyles(`\n          .leave-slow { transition-duration: ${slowLeaveDuration}ms; }\n          .leave-from { opacity: 100%; }\n          .leave-to { opacity: 0%; }\n\n          .leave-fast { transition-duration: ${fastLeaveDuration}ms; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot, TransitionChild },\n          template: html`\n            <TransitionRoot :show=\"show\">\n              <TransitionChild leave=\"leave-fast\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                I am fast\n              </TransitionChild>\n              <TransitionChild leave=\"leave-slow\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                I am slow\n              </TransitionChild>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(true)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return [\n              null, // Initial render\n              null, // Setup leave classes\n              fastLeaveDuration, // Done with fast leave\n              slowLeaveDuration - fastLeaveDuration, // Done with slow leave (which starts at the same time, but it is compared with previous render snapshot so we have to subtract those)\n            ]\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n\n    it(\n      'should not unmount the whole tree when some children are still transitioning',\n      suppressConsoleLogs(async () => {\n        let slowLeaveDuration = 150\n        let fastLeaveDuration = 50\n\n        withStyles(`\n          .leave-slow { transition-duration: ${slowLeaveDuration}ms; }\n          .leave-from { opacity: 100%; }\n          .leave-to { opacity: 0%; }\n\n          .leave-fast { transition-duration: ${fastLeaveDuration}ms; }\n        `)\n\n        let Example = defineComponent({\n          components: { TransitionRoot, TransitionChild },\n          template: html`\n            <TransitionRoot :show=\"show\">\n              <TransitionChild leave=\"leave-fast\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                <span>I am fast</span>\n                <TransitionRoot :show=\"show\" leave=\"leave-slow\">\n                  I am my own root component and I don't talk to the parent\n                </TransitionRoot>\n              </TransitionChild>\n              <TransitionChild leave=\"leave-slow\" leaveFrom=\"leave-from\" leaveTo=\"leave-to\">\n                I am slow\n              </TransitionChild>\n            </TransitionRoot>\n\n            <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n          `,\n          setup() {\n            let show = ref(true)\n            return { show }\n          },\n        })\n\n        let timeline = await executeTimeline(Example, [\n          // Toggle to hide\n          () => {\n            fireEvent.click(getByTestId('toggle'))\n            return [\n              null, // Initial render\n              null, // Setup leave classes\n              fastLeaveDuration, // Done with fast leave\n              slowLeaveDuration - fastLeaveDuration, // Done with slow leave (which starts at the same time, but it is compared with previous render snapshot so we have to subtract those)\n            ]\n          },\n        ])\n\n        expect(timeline).toMatchSnapshot()\n      })\n    )\n  })\n})\n\ndescribe('Events', () => {\n  it(\n    'should fire events for all the stages',\n    suppressConsoleLogs(async () => {\n      let eventHandler = jest.fn()\n      let enterDuration = 50\n      let leaveDuration = 75\n\n      withStyles(`\n        .enter { transition-duration: ${enterDuration}ms; }\n        .enter-from { opacity: 0%; }\n        .enter-to { opacity: 100%; }\n\n        .leave { transition-duration: ${leaveDuration}ms; }\n        .leave-from { opacity: 100%; }\n        .leave-to { opacity: 0%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot },\n        template: html`\n          <TransitionRoot\n            :show=\"show\"\n            @beforeEnter=\"eventHandler('beforeEnter', Date.now() - start)\"\n            @afterEnter=\"eventHandler('afterEnter', Date.now() - start)\"\n            @beforeLeave=\"eventHandler('beforeLeave', Date.now() - start)\"\n            @afterLeave=\"eventHandler('afterLeave', Date.now() - start)\"\n            enter=\"enter\"\n            enterFrom=\"enter-from\"\n            enterTo=\"enter-to\"\n            leave=\"leave\"\n            leaveFrom=\"leave-from\"\n            leaveTo=\"leave-to\"\n          >\n            <span>Hello!</span>\n          </TransitionRoot>\n\n          <button data-testid=\"toggle\" @click=\"show = !show\">Toggle</button>\n        `,\n        setup() {\n          let show = ref(false)\n          let start = ref(Date.now())\n\n          onMounted(() => (start.value = Date.now()))\n\n          return { show, start, eventHandler }\n        },\n      })\n\n      let timeline = await executeTimeline(Example, [\n        // Toggle to show\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(enterDuration)\n        },\n        // Toggle to hide\n        () => {\n          fireEvent.click(getByTestId('toggle'))\n          return executeTimeline.fullTransition(leaveDuration)\n        },\n      ])\n\n      expect(timeline).toMatchSnapshot()\n\n      expect(eventHandler).toHaveBeenCalledTimes(4)\n      expect(eventHandler.mock.calls.map(([name]) => name)).toEqual([\n        // Order is important here\n        'beforeEnter',\n        'afterEnter',\n        'beforeLeave',\n        'afterLeave',\n      ])\n\n      let enterHookDiff = eventHandler.mock.calls[1][1] - eventHandler.mock.calls[0][1]\n      expect(enterHookDiff).toBeGreaterThanOrEqual(enterDuration)\n      expect(enterHookDiff).toBeLessThanOrEqual(enterDuration * 3)\n\n      let leaveHookDiff = eventHandler.mock.calls[3][1] - eventHandler.mock.calls[2][1]\n      expect(leaveHookDiff).toBeGreaterThanOrEqual(leaveDuration)\n      expect(leaveHookDiff).toBeLessThanOrEqual(leaveDuration * 3)\n    })\n  )\n\n  it(\n    'should fire only one event for a given component change',\n    suppressConsoleLogs(async () => {\n      let eventHandler = jest.fn()\n      let enterDuration = 50\n      let leaveDuration = 75\n\n      withStyles(`\n        .enter-1 { transition-duration: ${enterDuration * 1}ms; }\n        .enter-2 { transition-duration: ${enterDuration * 2}ms; }\n        .enter-from { opacity: 0%; }\n        .enter-to { opacity: 100%; }\n\n        .leave-1 { transition-duration: ${leaveDuration * 1}ms; }\n        .leave-2 { transition-duration: ${leaveDuration * 2}ms; }\n        .leave-from { opacity: 100%; }\n        .leave-to { opacity: 0%; }\n      `)\n\n      let Example = defineComponent({\n        components: { TransitionRoot, TransitionChild },\n        template: html`\n          <TransitionRoot\n            :show=\"show\"\n            as=\"div\"\n            @beforeEnter=\"eventHandler('beforeEnter', Date.now() - start)\"\n            @afterEnter=\"eventHandler('afterEnter', Date.now() - start)\"\n            @beforeLeave=\"eventHandler('beforeLeave', Date.now() - start)\"\n            @afterLeave=\"eventHandler('afterLeave', Date.now() - start)\"\n            enter=\"enter-2\"\n            enterFrom=\"enter-from\"\n            enterTo=\"enter-to\"\n            leave=\"leave-2\"\n            leaveFrom=\"leave-from\"\n            leaveTo=\"leave-to\"\n          >\n            <TransitionChild\n              enter=\"enter-1\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave-1\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            />\n            <TransitionChild\n              enter=\"enter-1\"\n              enterFrom=\"enter-from\"\n              enterTo=\"enter-to\"\n              leave=\"leave-1\"\n              leaveFrom=\"leave-from\"\n              leaveTo=\"leave-to\"\n            >\n              <button data-testid=\"hide\" @click=\"show = false\" @click=\"hide\">Hide</button>\n            </TransitionChild>\n          </TransitionRoot>\n\n          <button data-testid=\"show\" @click=\"show = true\">Show</button>\n        `,\n        setup() {\n          let show = ref(false)\n          let start = ref(Date.now())\n\n          onMounted(() => (start.value = Date.now()))\n\n          return { show, start, eventHandler }\n        },\n      })\n\n      renderTemplate(Example)\n\n      fireEvent.click(getByTestId('show'))\n\n      await new Promise((resolve) => setTimeout(resolve, 1000))\n\n      fireEvent.click(getByTestId('hide'))\n\n      await new Promise((resolve) => setTimeout(resolve, 1000))\n\n      expect(eventHandler).toHaveBeenCalledTimes(4)\n      expect(eventHandler.mock.calls.map(([name]) => name)).toEqual([\n        // Order is important here\n        'beforeEnter',\n        'afterEnter',\n        'beforeLeave',\n        'afterLeave',\n      ])\n    })\n  )\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/transition.ts",
    "content": "import {\n  computed,\n  defineComponent,\n  h,\n  inject,\n  normalizeClass,\n  onMounted,\n  onUnmounted,\n  provide,\n  ref,\n  watch,\n  watchEffect,\n  type ConcreteComponent,\n  type InjectionKey,\n  type Ref,\n} from 'vue'\nimport { useId } from '../../hooks/use-id'\nimport {\n  State,\n  hasOpenClosed,\n  useOpenClosed,\n  useOpenClosedProvider,\n} from '../../internal/open-closed'\nimport { dom } from '../../utils/dom'\nimport { env } from '../../utils/env'\nimport { match } from '../../utils/match'\nimport { Features, RenderStrategy, omit, render } from '../../utils/render'\nimport { Reason, transition } from './utils/transition'\n\ntype ID = ReturnType<typeof useId>\n\n/**\n * Split class lists by whitespace\n *\n * We can't check for just spaces as all whitespace characters are\n * invalid in a class name, so we have to split on ANY whitespace.\n */\nfunction splitClasses(classes: string = '') {\n  return classes.split(/\\s+/).filter((className) => className.length > 1)\n}\n\ninterface TransitionContextValues {\n  show: Ref<boolean>\n  appear: Ref<boolean>\n}\nlet TransitionContext = Symbol('TransitionContext') as InjectionKey<TransitionContextValues | null>\n\nenum TreeStates {\n  Visible = 'visible',\n  Hidden = 'hidden',\n}\n\nfunction hasTransitionContext() {\n  return inject(TransitionContext, null) !== null\n}\n\nfunction useTransitionContext() {\n  let context = inject(TransitionContext, null)\n\n  if (context === null) {\n    throw new Error('A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.')\n  }\n\n  return context\n}\n\nfunction useParentNesting() {\n  let context = inject(NestingContext, null)\n\n  if (context === null) {\n    throw new Error('A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.')\n  }\n\n  return context\n}\n\ninterface NestingContextValues {\n  children: Ref<{ id: ID; state: TreeStates }[]>\n  register: (id: ID) => () => void\n  unregister: (id: ID, strategy?: RenderStrategy) => void\n}\n\nlet NestingContext = Symbol('NestingContext') as InjectionKey<NestingContextValues | null>\n\nfunction hasChildren(\n  bag: NestingContextValues['children'] | { children: NestingContextValues['children'] }\n): boolean {\n  if ('children' in bag) return hasChildren(bag.children)\n  return bag.value.filter(({ state }) => state === TreeStates.Visible).length > 0\n}\n\nfunction useNesting(done?: () => void) {\n  let transitionableChildren = ref<NestingContextValues['children']['value']>([])\n\n  let mounted = ref(false)\n  onMounted(() => (mounted.value = true))\n  onUnmounted(() => (mounted.value = false))\n\n  function unregister(childId: ID, strategy = RenderStrategy.Hidden) {\n    let idx = transitionableChildren.value.findIndex(({ id }) => id === childId)\n    if (idx === -1) return\n\n    match(strategy, {\n      [RenderStrategy.Unmount]() {\n        transitionableChildren.value.splice(idx, 1)\n      },\n      [RenderStrategy.Hidden]() {\n        transitionableChildren.value[idx].state = TreeStates.Hidden\n      },\n    })\n\n    if (!hasChildren(transitionableChildren) && mounted.value) {\n      done?.()\n    }\n  }\n\n  function register(childId: ID) {\n    let child = transitionableChildren.value.find(({ id }) => id === childId)\n    if (!child) {\n      transitionableChildren.value.push({ id: childId, state: TreeStates.Visible })\n    } else if (child.state !== TreeStates.Visible) {\n      child.state = TreeStates.Visible\n    }\n\n    return () => unregister(childId, RenderStrategy.Unmount)\n  }\n\n  return {\n    children: transitionableChildren,\n    register,\n    unregister,\n  }\n}\n\n// ---\n\nlet TransitionChildRenderFeatures = Features.RenderStrategy\n\nexport let TransitionChild = defineComponent({\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    show: { type: [Boolean], default: null },\n    unmount: { type: [Boolean], default: true },\n    appear: { type: [Boolean], default: false },\n    enter: { type: [String], default: '' },\n    enterFrom: { type: [String], default: '' },\n    enterTo: { type: [String], default: '' },\n    entered: { type: [String], default: '' },\n    leave: { type: [String], default: '' },\n    leaveFrom: { type: [String], default: '' },\n    leaveTo: { type: [String], default: '' },\n  },\n  emits: {\n    beforeEnter: () => true,\n    afterEnter: () => true,\n    beforeLeave: () => true,\n    afterLeave: () => true,\n  },\n  setup(props, { emit, attrs, slots, expose }) {\n    let transitionStateFlags = ref(0)\n\n    function beforeEnter() {\n      transitionStateFlags.value |= State.Opening\n      emit('beforeEnter')\n    }\n\n    function afterEnter() {\n      transitionStateFlags.value &= ~State.Opening\n      emit('afterEnter')\n    }\n\n    function beforeLeave() {\n      transitionStateFlags.value |= State.Closing\n      emit('beforeLeave')\n    }\n\n    function afterLeave() {\n      transitionStateFlags.value &= ~State.Closing\n      emit('afterLeave')\n    }\n\n    if (!hasTransitionContext() && hasOpenClosed()) {\n      return () =>\n        h(\n          TransitionRoot,\n          {\n            ...props,\n            onBeforeEnter: beforeEnter,\n            onAfterEnter: afterEnter,\n            onBeforeLeave: beforeLeave,\n            onAfterLeave: afterLeave,\n          },\n          slots\n        )\n    }\n\n    let container = ref<HTMLElement | null>(null)\n    let strategy = computed(() => (props.unmount ? RenderStrategy.Unmount : RenderStrategy.Hidden))\n\n    expose({ el: container, $el: container })\n\n    let { show, appear } = useTransitionContext()\n    let { register, unregister } = useParentNesting()\n\n    let state = ref(show.value ? TreeStates.Visible : TreeStates.Hidden)\n    let initial = { value: true }\n\n    let id = useId()\n\n    let isTransitioning = { value: false }\n\n    let nesting = useNesting(() => {\n      // When all children have been unmounted we can only hide ourselves if and only if we are not\n      // transitioning ourselves. Otherwise we would unmount before the transitions are finished.\n      if (!isTransitioning.value && state.value !== TreeStates.Hidden) {\n        state.value = TreeStates.Hidden\n        unregister(id)\n        afterLeave()\n      }\n    })\n\n    onMounted(() => {\n      let unregister = register(id)\n      onUnmounted(unregister)\n    })\n\n    watchEffect(() => {\n      // If we are in another mode than the Hidden mode then ignore\n      if (strategy.value !== RenderStrategy.Hidden) return\n      if (!id) return\n\n      // Make sure that we are visible\n      if (show.value && state.value !== TreeStates.Visible) {\n        state.value = TreeStates.Visible\n        return\n      }\n\n      match(state.value, {\n        [TreeStates.Hidden]: () => unregister(id),\n        [TreeStates.Visible]: () => register(id),\n      })\n    })\n\n    let enterClasses = splitClasses(props.enter)\n    let enterFromClasses = splitClasses(props.enterFrom)\n    let enterToClasses = splitClasses(props.enterTo)\n\n    let enteredClasses = splitClasses(props.entered)\n\n    let leaveClasses = splitClasses(props.leave)\n    let leaveFromClasses = splitClasses(props.leaveFrom)\n    let leaveToClasses = splitClasses(props.leaveTo)\n\n    onMounted(() => {\n      watchEffect(() => {\n        if (state.value === TreeStates.Visible) {\n          let domElement = dom(container)\n          // When you return `null` from a component, the actual DOM reference will\n          // be an empty comment... This means that we can never check for the DOM\n          // node to be `null`. So instead we check for an empty comment.\n          let isEmptyDOMNode = domElement instanceof Comment && domElement.data === ''\n          if (isEmptyDOMNode) {\n            throw new Error('Did you forget to passthrough the `ref` to the actual DOM node?')\n          }\n        }\n      })\n    })\n\n    function executeTransition(onInvalidate: (cb: () => void) => void) {\n      // Skipping initial transition\n      let skip = initial.value && !appear.value\n\n      let node = dom(container)\n      if (!node || !(node instanceof HTMLElement)) return\n      if (skip) return\n\n      isTransitioning.value = true\n\n      if (show.value) beforeEnter()\n      if (!show.value) beforeLeave()\n\n      onInvalidate(\n        show.value\n          ? transition(\n              node,\n              enterClasses,\n              enterFromClasses,\n              enterToClasses,\n              enteredClasses,\n              (reason) => {\n                isTransitioning.value = false\n                if (reason === Reason.Finished) afterEnter()\n              }\n            )\n          : transition(\n              node,\n              leaveClasses,\n              leaveFromClasses,\n              leaveToClasses,\n              enteredClasses,\n              (reason) => {\n                isTransitioning.value = false\n\n                if (reason !== Reason.Finished) return\n\n                // When we don't have children anymore we can safely unregister from the parent and hide\n                // ourselves.\n                if (!hasChildren(nesting)) {\n                  state.value = TreeStates.Hidden\n                  unregister(id)\n                  afterLeave()\n                }\n              }\n            )\n      )\n    }\n\n    onMounted(() => {\n      watch(\n        [show],\n        (_oldValues, _newValues, onInvalidate) => {\n          executeTransition(onInvalidate)\n          initial.value = false\n        },\n        { immediate: true }\n      )\n    })\n\n    provide(NestingContext, nesting)\n    useOpenClosedProvider(\n      computed(\n        () =>\n          match(state.value, {\n            [TreeStates.Visible]: State.Open,\n            [TreeStates.Hidden]: State.Closed,\n          }) | transitionStateFlags.value\n      )\n    )\n\n    return () => {\n      let {\n        appear: _appear,\n        show: _show,\n\n        // Class names\n        enter,\n        enterFrom,\n        enterTo,\n        entered,\n        leave,\n        leaveFrom,\n        leaveTo,\n        ...rest\n      } = props\n\n      let ourProps = { ref: container }\n      let theirProps = {\n        ...rest,\n        ...(appear.value && show.value && env.isServer\n          ? {\n              // Already apply the `enter` and `enterFrom` on the server if required\n              class: normalizeClass([\n                attrs.class,\n                // @ts-expect-error not explicitly defined\n                rest.class,\n                ...enterClasses,\n                ...enterFromClasses,\n              ]),\n            }\n          : {}),\n      }\n\n      return render({\n        theirProps,\n        ourProps,\n        slot: {},\n        slots,\n        attrs,\n        features: TransitionChildRenderFeatures,\n        visible: state.value === TreeStates.Visible,\n        name: 'TransitionChild',\n      })\n    }\n  },\n})\n\n// ---\n\n// This exists to work around typescript circular inference problem\nlet _TransitionChild = TransitionChild as ConcreteComponent\n\nexport let TransitionRoot = defineComponent({\n  inheritAttrs: false,\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    show: { type: [Boolean], default: null },\n    unmount: { type: [Boolean], default: true },\n    appear: { type: [Boolean], default: false },\n    enter: { type: [String], default: '' },\n    enterFrom: { type: [String], default: '' },\n    enterTo: { type: [String], default: '' },\n    entered: { type: [String], default: '' },\n    leave: { type: [String], default: '' },\n    leaveFrom: { type: [String], default: '' },\n    leaveTo: { type: [String], default: '' },\n  },\n  emits: {\n    beforeEnter: () => true,\n    afterEnter: () => true,\n    beforeLeave: () => true,\n    afterLeave: () => true,\n  },\n  setup(props, { emit, attrs, slots }) {\n    let usesOpenClosedState = useOpenClosed()\n\n    let show = computed(() => {\n      if (props.show === null && usesOpenClosedState !== null) {\n        return (usesOpenClosedState.value & State.Open) === State.Open\n      }\n\n      return props.show\n    })\n\n    watchEffect(() => {\n      if (![true, false].includes(show.value)) {\n        throw new Error('A <Transition /> is used but it is missing a `:show=\"true | false\"` prop.')\n      }\n    })\n\n    let state = ref(show.value ? TreeStates.Visible : TreeStates.Hidden)\n\n    let nestingBag = useNesting(() => {\n      state.value = TreeStates.Hidden\n    })\n\n    let initial = ref(true)\n    let transitionBag = {\n      show,\n      appear: computed(() => props.appear || !initial.value),\n    }\n\n    onMounted(() => {\n      watchEffect(() => {\n        initial.value = false\n\n        if (show.value) {\n          state.value = TreeStates.Visible\n        } else if (!hasChildren(nestingBag)) {\n          state.value = TreeStates.Hidden\n        }\n      })\n    })\n\n    provide(NestingContext, nestingBag)\n    provide(TransitionContext, transitionBag)\n\n    return () => {\n      let theirProps = omit(props, [\n        'show',\n        'appear',\n        'unmount',\n        'onBeforeEnter',\n        'onBeforeLeave',\n        'onAfterEnter',\n        'onAfterLeave',\n      ])\n      let sharedProps = { unmount: props.unmount }\n\n      return render({\n        ourProps: {\n          ...sharedProps,\n          as: 'template',\n        },\n        theirProps: {},\n        slot: {},\n        slots: {\n          ...slots,\n          default: () => [\n            h(\n              _TransitionChild,\n              {\n                onBeforeEnter: () => emit('beforeEnter'),\n                onAfterEnter: () => emit('afterEnter'),\n                onBeforeLeave: () => emit('beforeLeave'),\n                onAfterLeave: () => emit('afterLeave'),\n                ...attrs,\n                ...sharedProps,\n                ...theirProps,\n              },\n              slots.default\n            ),\n          ],\n        },\n        attrs: {},\n        features: TransitionChildRenderFeatures,\n        visible: state.value === TreeStates.Visible,\n        name: 'Transition',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/utils/transition.test.ts",
    "content": "import { reportChanges } from '../../../test-utils/report-dom-node-changes'\nimport { disposables } from '../../../utils/disposables'\nimport { Reason, transition } from './transition'\n\nbeforeEach(() => {\n  document.body.innerHTML = ''\n})\n\nit('should be possible to transition', async () => {\n  let d = disposables()\n\n  let snapshots: { content: string; recordedAt: bigint }[] = []\n  let element = document.createElement('div')\n  document.body.appendChild(element)\n\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      (content) => {\n        snapshots.push({\n          content,\n          recordedAt: process.hrtime.bigint(),\n        })\n      }\n    )\n  )\n\n  await new Promise((resolve) => {\n    transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)\n  })\n\n  await new Promise((resolve) => d.nextFrame(resolve))\n\n  // Initial render:\n  expect(snapshots[0].content).toEqual('<div></div>')\n\n  // Start of transition\n  expect(snapshots[1].content).toEqual('<div class=\"enter enterFrom\"></div>')\n\n  // NOTE: There is no `enter enterTo`, because we didn't define a duration. Therefore it is not\n  // necessary to put the classes on the element and immediately remove them.\n\n  // Cleanup phase\n  expect(snapshots[2].content).toEqual('<div class=\"entered\"></div>')\n\n  d.dispose()\n})\n\nit('should wait the correct amount of time to finish a transition', async () => {\n  let d = disposables()\n\n  let snapshots: { content: string; recordedAt: bigint }[] = []\n  let element = document.createElement('div')\n  document.body.appendChild(element)\n\n  let duration = 20\n\n  element.style.transitionDuration = `${duration}ms`\n\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      (content) => {\n        snapshots.push({\n          content,\n          recordedAt: process.hrtime.bigint(),\n        })\n      }\n    )\n  )\n\n  let reason = await new Promise((resolve) => {\n    transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)\n  })\n\n  await new Promise((resolve) => d.nextFrame(resolve))\n  expect(reason).toBe(Reason.Finished)\n\n  // Initial render:\n  expect(snapshots[0].content).toEqual(`<div style=\"transition-duration: ${duration}ms;\"></div>`)\n\n  // Start of transition\n  expect(snapshots[1].content).toEqual(\n    `<div style=\"transition-duration: ${duration}ms;\" class=\"enter enterFrom\"></div>`\n  )\n\n  expect(snapshots[2].content).toEqual(\n    `<div style=\"transition-duration: ${duration}ms;\" class=\"enter enterTo\"></div>`\n  )\n\n  let estimatedDuration = Number(\n    (snapshots[snapshots.length - 1].recordedAt - snapshots[snapshots.length - 2].recordedAt) /\n      BigInt(1e6)\n  )\n\n  expect(estimatedDuration).toBeWithinRenderFrame(duration)\n\n  // Cleanup phase\n  expect(snapshots[3].content).toEqual(\n    `<div style=\"transition-duration: ${duration}ms;\" class=\"entered\"></div>`\n  )\n})\n\nit('should keep the delay time into account', async () => {\n  let d = disposables()\n\n  let snapshots: { content: string; recordedAt: bigint }[] = []\n  let element = document.createElement('div')\n  document.body.appendChild(element)\n\n  let duration = 20\n  let delayDuration = 100\n\n  element.style.transitionDuration = `${duration}ms`\n  element.style.transitionDelay = `${delayDuration}ms`\n\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      (content) => {\n        snapshots.push({\n          content,\n          recordedAt: process.hrtime.bigint(),\n        })\n      }\n    )\n  )\n\n  let reason = await new Promise((resolve) => {\n    transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)\n  })\n\n  await new Promise((resolve) => d.nextFrame(resolve))\n  expect(reason).toBe(Reason.Finished)\n\n  let estimatedDuration = Number(\n    (snapshots[snapshots.length - 1].recordedAt - snapshots[snapshots.length - 2].recordedAt) /\n      BigInt(1e6)\n  )\n\n  expect(estimatedDuration).toBeWithinRenderFrame(duration + delayDuration)\n})\n\nit('should be possible to cancel a transition at any time', async () => {\n  let d = disposables()\n\n  let snapshots: {\n    content: string\n    recordedAt: bigint\n    relativeTime: number\n  }[] = []\n  let element = document.createElement('div')\n  document.body.appendChild(element)\n\n  // This duration is so overkill, however it will demonstrate that we can cancel transitions.\n  let duration = 5000\n\n  element.style.transitionDuration = `${duration}ms`\n\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      (content) => {\n        let recordedAt = process.hrtime.bigint()\n        let total = snapshots.length\n\n        snapshots.push({\n          content,\n          recordedAt,\n          relativeTime:\n            total === 0 ? 0 : Number((recordedAt - snapshots[total - 1].recordedAt) / BigInt(1e6)),\n        })\n      }\n    )\n  )\n\n  expect.assertions(2)\n\n  // Setup the transition\n  let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], (reason) => {\n    expect(reason).toBe(Reason.Cancelled)\n  })\n\n  // Wait for a bit\n  await new Promise((resolve) => setTimeout(resolve, 20))\n\n  // Cancel the transition\n  cancel()\n  await new Promise((resolve) => d.nextFrame(resolve))\n\n  expect(snapshots.map((snapshot) => snapshot.content).join('\\n')).not.toContain('enterTo')\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/components/transitions/utils/transition.ts",
    "content": "import { disposables } from '../../../utils/disposables'\nimport { once } from '../../../utils/once'\n\nfunction addClasses(node: HTMLElement, ...classes: string[]) {\n  node && classes.length > 0 && node.classList.add(...classes)\n}\n\nfunction removeClasses(node: HTMLElement, ...classes: string[]) {\n  node && classes.length > 0 && node.classList.remove(...classes)\n}\n\nexport enum Reason {\n  Finished = 'finished',\n  Cancelled = 'cancelled',\n}\n\nfunction waitForTransition(node: HTMLElement, done: (reason: Reason) => void) {\n  let d = disposables()\n\n  if (!node) return d.dispose\n\n  // Safari returns a comma separated list of values, so let's sort them and take the highest value.\n  let { transitionDuration, transitionDelay } = getComputedStyle(node)\n\n  let [durationMs, delaysMs] = [transitionDuration, transitionDelay].map((value) => {\n    let [resolvedValue = 0] = value\n      .split(',')\n      // Remove falseys we can't work with\n      .filter(Boolean)\n      // Values are returned as `0.3s` or `75ms`\n      .map((v) => (v.includes('ms') ? parseFloat(v) : parseFloat(v) * 1000))\n      .sort((a, z) => z - a)\n\n    return resolvedValue\n  })\n\n  // Waiting for the transition to end. We could use the `transitionend` event, however when no\n  // actual transition/duration is defined then the `transitionend` event is not fired.\n  //\n  // TODO: Downside is, when you slow down transitions via devtools this timeout is still using the\n  // full 100% speed instead of the 25% or 10%.\n  if (durationMs !== 0) {\n    d.setTimeout(() => done(Reason.Finished), durationMs + delaysMs)\n  } else {\n    // No transition is happening, so we should cleanup already. Otherwise we have to wait until we\n    // get disposed.\n    done(Reason.Finished)\n  }\n\n  // If we get disposed before the timeout runs we should cleanup anyway\n  d.add(() => done(Reason.Cancelled))\n\n  return d.dispose\n}\n\nexport function transition(\n  node: HTMLElement,\n  base: string[],\n  from: string[],\n  to: string[],\n  entered: string[],\n  done?: (reason: Reason) => void\n) {\n  let d = disposables()\n  let _done = done !== undefined ? once(done) : () => {}\n\n  removeClasses(node, ...entered)\n  addClasses(node, ...base, ...from)\n\n  d.nextFrame(() => {\n    removeClasses(node, ...from)\n    addClasses(node, ...to)\n\n    d.add(\n      waitForTransition(node, (reason) => {\n        removeClasses(node, ...to, ...base)\n        addClasses(node, ...entered)\n        return _done(reason)\n      })\n    )\n  })\n\n  // Once we get disposed, we should ensure that we cleanup after ourselves. In case of an unmount,\n  // the node itself will be nullified and will be a no-op. In case of a full transition the classes\n  // are already removed which is also a no-op. However if you go from enter -> leave mid-transition\n  // then we have some leftovers that should be cleaned.\n  d.add(() => removeClasses(node, ...base, ...from, ...to, ...entered))\n\n  // When we get disposed early, then we should also call the done method but switch the reason.\n  d.add(() => _done(Reason.Cancelled))\n\n  return d.dispose\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/__mocks__/use-id.ts",
    "content": "beforeEach(() => {\n  id = 0\n})\n\nlet id = 0\nfunction generateId() {\n  return ++id\n}\n\nexport function useId() {\n  return generateId()\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/document-overflow/adjust-scrollbar-padding.ts",
    "content": "import type { ScrollLockStep } from './overflow-store'\n\nexport function adjustScrollbarPadding(): ScrollLockStep {\n  let scrollbarWidthBefore: number\n\n  return {\n    before({ doc }) {\n      let documentElement = doc.documentElement\n      let ownerWindow = doc.defaultView ?? window\n\n      scrollbarWidthBefore = Math.max(0, ownerWindow.innerWidth - documentElement.clientWidth)\n    },\n\n    after({ doc, d }) {\n      let documentElement = doc.documentElement\n\n      // Account for the change in scrollbar width\n      // NOTE: This is a bit of a hack, but it's the only way to do this\n      let scrollbarWidthAfter = Math.max(\n        0,\n        documentElement.clientWidth - documentElement.offsetWidth\n      )\n      let scrollbarWidth = Math.max(0, scrollbarWidthBefore - scrollbarWidthAfter)\n\n      d.style(documentElement, 'paddingRight', `${scrollbarWidth}px`)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/document-overflow/handle-ios-locking.ts",
    "content": "import { disposables } from '../../utils/disposables'\nimport { isIOS } from '../../utils/platform'\nimport type { ScrollLockStep } from './overflow-store'\n\ninterface ContainerMetadata {\n  containers: (() => HTMLElement[])[]\n}\n\nexport function handleIOSLocking(): ScrollLockStep<ContainerMetadata> {\n  if (!isIOS()) {\n    return {}\n  }\n\n  return {\n    before({ doc, d, meta }) {\n      function inAllowedContainer(el: HTMLElement) {\n        return meta.containers\n          .flatMap((resolve) => resolve())\n          .some((container) => container.contains(el))\n      }\n\n      d.microTask(() => {\n        // We need to be able to offset the body with the current scroll position. However, if you\n        // have `scroll-behavior: smooth` set, then changing the scrollTop in any way shape or form\n        // will trigger a \"smooth\" scroll and the new position would be incorrect.\n        //\n        // This is why we are forcing the `scroll-behaviour: auto` here, and then restoring it later.\n        // We have to be a bit careful, because removing `scroll-behavior: auto` back to\n        // `scroll-behavior: smooth` can start triggering smooth scrolling. Delaying this by a\n        // microTask will guarantee that everything is done such that both enter/exit of the Dialog is\n        // not using smooth scrolling.\n        if (window.getComputedStyle(doc.documentElement).scrollBehavior !== 'auto') {\n          let _d = disposables()\n          _d.style(doc.documentElement, 'scrollBehavior', 'auto')\n          d.add(() => d.microTask(() => _d.dispose()))\n        }\n\n        // Keep track of the current scroll position so that we can restore the scroll position if\n        // it has changed in the meantime.\n        let scrollPosition = window.scrollY ?? window.pageYOffset\n\n        // Relatively hacky, but if you click a link like `<a href=\"#foo\">` in the Dialog, and there\n        // exists an element on the page (outside of the Dialog) with that id, then the browser will\n        // scroll to that position. However, this is not the case if the element we want to scroll to\n        // is higher and the browser needs to scroll up, but it doesn't do that.\n        //\n        // Let's try and capture that element and store it, so that we can later scroll to it once the\n        // Dialog closes.\n        let scrollToElement: HTMLElement | null = null\n        d.addEventListener(\n          doc,\n          'click',\n          (e) => {\n            if (!(e.target instanceof HTMLElement)) {\n              return\n            }\n\n            try {\n              let anchor = e.target.closest('a')\n              if (!anchor) return\n              let { hash } = new URL(anchor.href)\n              let el = doc.querySelector(hash)\n              if (el && !inAllowedContainer(el as HTMLElement)) {\n                scrollToElement = el as HTMLElement\n              }\n            } catch (err) {}\n          },\n          true\n        )\n\n        // Rely on overscrollBehavior to prevent scrolling outside of the Dialog.\n        d.addEventListener(doc, 'touchstart', (e) => {\n          if (e.target instanceof HTMLElement) {\n            if (inAllowedContainer(e.target as HTMLElement)) {\n              // Find the root of the allowed containers\n              let rootContainer = e.target\n              while (\n                rootContainer.parentElement &&\n                inAllowedContainer(rootContainer.parentElement)\n              ) {\n                rootContainer = rootContainer.parentElement!\n              }\n\n              d.style(rootContainer, 'overscrollBehavior', 'contain')\n            } else {\n              d.style(e.target, 'touchAction', 'none')\n            }\n          }\n        })\n\n        d.addEventListener(\n          doc,\n          'touchmove',\n          (e) => {\n            // Check if we are scrolling inside any of the allowed containers, if not let's cancel the event!\n            if (e.target instanceof HTMLElement) {\n              // Some inputs like `<input type=range>` use touch events to\n              // allow interaction. We should not prevent this event.\n              if (e.target.tagName === 'INPUT') {\n                return\n              }\n\n              if (inAllowedContainer(e.target as HTMLElement)) {\n                // Even if we are in an allowed container, on iOS the main page can still scroll, we\n                // have to make sure that we `event.preventDefault()` this event to prevent that.\n                //\n                // However, if we happen to scroll on an element that is overflowing, or any of its\n                // parents are overflowing, then we should not call `event.preventDefault()` because\n                // otherwise we are preventing the user from scrolling inside that container which\n                // is not what we want.\n                let scrollableParent = e.target\n                while (\n                  scrollableParent.parentElement &&\n                  // Assumption: We are always used in a Headless UI Portal. Once we reach the\n                  // portal itself, we can stop crawling up the tree.\n                  scrollableParent.dataset.headlessuiPortal !== ''\n                ) {\n                  // Check if the scrollable container is overflowing or not.\n                  //\n                  // NOTE: we could check the `overflow`, `overflow-y` and `overflow-x` properties\n                  // but when there is no overflow happening then the `overscrollBehavior` doesn't\n                  // seem to work and the main page will still scroll. So instead we check if the\n                  // scrollable container is overflowing or not and use that heuristic instead.\n                  if (\n                    scrollableParent.scrollHeight > scrollableParent.clientHeight ||\n                    scrollableParent.scrollWidth > scrollableParent.clientWidth\n                  ) {\n                    break\n                  }\n\n                  scrollableParent = scrollableParent.parentElement\n                }\n\n                // We crawled up the tree until the beginning of the Portal, let's prevent the\n                // event if this is the case. If not, then we are in a container where we are\n                // allowed to scroll so we don't have to prevent the event.\n                if (scrollableParent.dataset.headlessuiPortal === '') {\n                  e.preventDefault()\n                }\n              }\n\n              // We are not in an allowed container, so let's prevent the event.\n              else {\n                e.preventDefault()\n              }\n            }\n          },\n          { passive: false }\n        )\n\n        // Restore scroll position if a scrollToElement was captured.\n        d.add(() => {\n          let newScrollPosition = window.scrollY ?? window.pageYOffset\n\n          // If the scroll position changed, then we can restore it to the previous value. This will\n          // happen if you focus an input field and the browser scrolls for you.\n          if (scrollPosition !== newScrollPosition) {\n            window.scrollTo(0, scrollPosition)\n          }\n\n          // If we captured an element that should be scrolled to, then we can try to do that if the\n          // element is still connected (aka, still in the DOM).\n          if (scrollToElement && scrollToElement.isConnected) {\n            scrollToElement.scrollIntoView({ block: 'nearest' })\n            scrollToElement = null\n          }\n        })\n      })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/document-overflow/overflow-store.ts",
    "content": "import { disposables, type Disposables } from '../../utils/disposables'\nimport { createStore } from '../../utils/store'\nimport { adjustScrollbarPadding } from './adjust-scrollbar-padding'\nimport { handleIOSLocking } from './handle-ios-locking'\nimport { preventScroll } from './prevent-scroll'\n\ninterface DocEntry {\n  doc: Document\n  count: number\n  d: Disposables\n  meta: Set<MetaFn>\n}\n\nfunction buildMeta(fns: Iterable<MetaFn>) {\n  let tmp = {}\n  for (let fn of fns) {\n    Object.assign(tmp, fn(tmp))\n  }\n  return tmp\n}\n\nexport type MetaFn = (meta: Record<string, any>) => Record<string, any>\n\nexport interface Context<MetaType extends Record<string, any> = any> {\n  doc: Document\n  d: Disposables\n  meta: MetaType\n}\n\nexport interface ScrollLockStep<MetaType extends Record<string, any> = any> {\n  before?(ctx: Context<MetaType>): void\n  after?(ctx: Context<MetaType>): void\n}\n\nexport let overflows = createStore(() => new Map<Document, DocEntry>(), {\n  PUSH(doc: Document, meta: MetaFn) {\n    let entry = this.get(doc) ?? {\n      doc,\n      count: 0,\n      d: disposables(),\n      meta: new Set(),\n    }\n\n    entry.count++\n    entry.meta.add(meta)\n    this.set(doc, entry)\n\n    return this\n  },\n\n  POP(doc: Document, meta: MetaFn) {\n    let entry = this.get(doc)\n    if (entry) {\n      entry.count--\n      entry.meta.delete(meta)\n    }\n\n    return this\n  },\n\n  SCROLL_PREVENT({ doc, d, meta }: DocEntry) {\n    let ctx = {\n      doc,\n      d,\n      meta: buildMeta(meta),\n    }\n\n    let steps: ScrollLockStep<any>[] = [\n      handleIOSLocking(),\n      adjustScrollbarPadding(),\n      preventScroll(),\n    ]\n\n    // Run all `before` actions together\n    steps.forEach(({ before }) => before?.(ctx))\n\n    // Run all `after` actions together\n    steps.forEach(({ after }) => after?.(ctx))\n  },\n\n  SCROLL_ALLOW({ d }: DocEntry) {\n    d.dispose()\n  },\n\n  TEARDOWN({ doc }: DocEntry) {\n    this.delete(doc)\n  },\n})\n\n// Update the document overflow state when the store changes\n// This MUST happen outside of react for this to work properly.\noverflows.subscribe(() => {\n  let docs = overflows.getSnapshot()\n\n  let styles = new Map<Document, string | undefined>()\n\n  // Read data from all the documents\n  for (let [doc] of docs) {\n    styles.set(doc, doc.documentElement.style.overflow)\n  }\n\n  // Write data to all the documents\n  for (let entry of docs.values()) {\n    let isHidden = styles.get(entry.doc) === 'hidden'\n    let isLocked = entry.count !== 0\n    let willChange = (isLocked && !isHidden) || (!isLocked && isHidden)\n\n    if (willChange) {\n      overflows.dispatch(entry.count > 0 ? 'SCROLL_PREVENT' : 'SCROLL_ALLOW', entry)\n    }\n\n    // We have to clean up after ourselves so we don't leak memory\n    // Using a WeakMap would be ideal, but it's not iterable\n    if (entry.count === 0) {\n      overflows.dispatch('TEARDOWN', entry)\n    }\n  }\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/document-overflow/prevent-scroll.ts",
    "content": "import type { ScrollLockStep } from './overflow-store'\n\nexport function preventScroll(): ScrollLockStep {\n  return {\n    before({ doc, d }) {\n      d.style(doc.documentElement, 'overflow', 'hidden')\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/document-overflow/use-document-overflow.ts",
    "content": "import { computed, watch, type Ref } from 'vue'\nimport { useStore } from '../../hooks/use-store'\nimport { overflows, type MetaFn } from './overflow-store'\n\nexport function useDocumentOverflowLockedEffect(\n  doc: Ref<Document | null>,\n  shouldBeLocked: Ref<boolean>,\n  meta: MetaFn\n) {\n  let store = useStore(overflows)\n  let locked = computed(() => {\n    let entry = doc.value ? store.value.get(doc.value) : undefined\n    return entry ? entry.count > 0 : false\n  })\n\n  watch(\n    [doc, shouldBeLocked],\n    ([doc, shouldBeLocked], [oldDoc], onInvalidate) => {\n      if (!doc || !shouldBeLocked) {\n        return\n      }\n\n      // Prevent the document from scrolling\n      overflows.dispatch('PUSH', doc, meta)\n\n      // Allow document to scroll\n      let didRunCleanup = false\n      onInvalidate(() => {\n        if (didRunCleanup) return\n        overflows.dispatch('POP', oldDoc ?? doc, meta)\n\n        // This shouldn't be necessary, but it is.\n        // Seems like a Vue bug.\n        didRunCleanup = true\n      })\n    },\n    {\n      immediate: true,\n    }\n  )\n\n  return locked\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-controllable.ts",
    "content": "import { computed, ref, type ComputedRef, type UnwrapRef } from 'vue'\n\nexport function useControllable<T>(\n  controlledValue: ComputedRef<T | undefined>,\n  onChange?: (value: T) => void,\n  defaultValue?: ComputedRef<T>\n) {\n  let internalValue = ref(defaultValue?.value)\n  let isControlled = computed(() => controlledValue.value !== undefined)\n\n  return [\n    computed(() => (isControlled.value ? controlledValue.value : internalValue.value)),\n    function (value: unknown) {\n      if (isControlled.value) {\n        return onChange?.(value as T)\n      } else {\n        internalValue.value = value as UnwrapRef<T>\n        return onChange?.(value as T)\n      }\n    },\n  ] as const\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-disposables.ts",
    "content": "import { onUnmounted } from 'vue'\nimport { disposables } from '../utils/disposables'\n\n/**\n * The `useDisposables` hook returns a `disposables` object that is disposed\n * when the component is unmounted.\n */\nexport function useDisposables() {\n  let d = disposables()\n  onUnmounted(() => d.dispose())\n  return d\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-document-event.ts",
    "content": "import { watchEffect, type Ref } from 'vue'\nimport { env } from '../utils/env'\n\nexport function useDocumentEvent<TType extends keyof DocumentEventMap>(\n  enabled: Ref<boolean>,\n  type: TType,\n  listener: (this: Document, ev: DocumentEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  if (env.isServer) return\n\n  watchEffect((onInvalidate) => {\n    if (!enabled.value) return\n\n    document.addEventListener(type, listener, options)\n    onInvalidate(() => document.removeEventListener(type, listener, options))\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-event-listener.ts",
    "content": "import { watchEffect } from 'vue'\nimport { env } from '../utils/env'\n\nexport function useEventListener<TType extends keyof WindowEventMap>(\n  element: HTMLElement | Document | Window | EventTarget | null | undefined,\n  type: TType,\n  listener: (event: WindowEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  if (env.isServer) return\n\n  watchEffect((onInvalidate) => {\n    element = element ?? window\n\n    element.addEventListener(type, listener as any, options)\n    onInvalidate(() => element!.removeEventListener(type, listener as any, options))\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-frame-debounce.ts",
    "content": "import { useDisposables } from './use-disposables'\n\n/**\n * Schedule some task in the next frame.\n *\n * - If you call the returned function multiple times, only the last task will\n *   be executed.\n * - If the component is unmounted, the task will be cancelled.\n */\nexport function useFrameDebounce() {\n  let d = useDisposables()\n\n  return (cb: () => void) => {\n    d.dispose()\n    d.nextFrame(cb)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-id.ts",
    "content": "let id = 0\nfunction generateId() {\n  return ++id\n}\n\nexport function useId() {\n  return generateId()\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-inert.test.ts",
    "content": "import { computed, defineComponent, nextTick, ref, type ComponentOptionsWithoutProps } from 'vue'\nimport { assertInert, assertNotInert, getByText } from '../test-utils/accessibility-assertions'\nimport { html } from '../test-utils/html'\nimport { click } from '../test-utils/interactions'\nimport { render } from '../test-utils/vue-testing-library'\nimport { dom } from '../utils/dom'\nimport { useInert } from './use-inert'\n\nbeforeAll(() => {\n  jest.spyOn(window, 'requestAnimationFrame').mockImplementation(setImmediate as any)\n  jest.spyOn(window, 'cancelAnimationFrame').mockImplementation(clearImmediate as any)\n  jest.spyOn(global.console, 'error').mockImplementation(jest.fn())\n})\n\nafterAll(() => jest.restoreAllMocks())\n\nfunction renderTemplate(input: string | ComponentOptionsWithoutProps) {\n  let defaultComponents = {}\n\n  if (typeof input === 'string') {\n    return render(defineComponent({ template: input, components: defaultComponents }), {\n      attachTo: document.body,\n    })\n  }\n\n  return render(\n    defineComponent(\n      Object.assign({}, input, {\n        components: { ...defaultComponents, ...input.components },\n      }) as Parameters<typeof defineComponent>[0]\n    ),\n    { attachTo: document.body }\n  )\n}\n\nlet Before = defineComponent({\n  name: 'Before',\n  template: html` <div>before</div> `,\n})\n\nlet After = defineComponent({\n  name: 'After',\n  template: html` <div>after</div> `,\n})\n\nit('should be possible to inert an element', async () => {\n  renderTemplate({\n    template: html`\n      <Before />\n      <Example />\n      <After />\n    `,\n    components: {\n      Before,\n      After,\n      Example: defineComponent({\n        name: 'Example',\n        template: html`\n          <div ref=\"container\" id=\"main\">\n            <button @click=\"enabled = !enabled\">toggle</button>\n          </div>\n        `,\n        setup() {\n          let container = ref(null)\n          let enabled = ref(true)\n\n          useInert(container, enabled)\n\n          return { container, enabled }\n        },\n      }),\n    },\n  })\n\n  await new Promise<void>(nextTick)\n\n  // Verify that `main` is inert\n  assertInert(document.getElementById('main'))\n\n  // Verify that the others are not inert\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n\n  // Restore\n  await click(getByText('toggle'))\n\n  // Verify that nothing is inert\n  assertNotInert(document.getElementById('main'))\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n})\n\nit('should be possible to inert an element', async () => {\n  renderTemplate({\n    template: html`\n      <Before />\n      <Example />\n      <After />\n    `,\n    components: {\n      Before,\n      After,\n      Example: defineComponent({\n        name: 'Example',\n        template: html`\n          <div ref=\"container\" id=\"main\">\n            <button @click=\"enabled = !enabled\">toggle</button>\n          </div>\n        `,\n        setup() {\n          let container = ref(null)\n          let enabled = ref(false)\n\n          useInert(container, enabled)\n\n          return { container, enabled }\n        },\n      }),\n    },\n  })\n\n  await new Promise<void>(nextTick)\n\n  assertNotInert(document.getElementById('main'))\n  assertNotInert(getByText('before'))\n  assertNotInert(getByText('after'))\n})\n\nit('should mark the element as not inert anymore, once all references are gone', async () => {\n  renderTemplate({\n    template: html`\n      <div id=\"parent\">\n        <Example>A</Example>\n        <Example>B</Example>\n      </div>\n    `,\n    components: {\n      Example: defineComponent({\n        name: 'Example',\n        template: html`\n          <div ref=\"container\">\n            <button @click=\"enabled = !enabled\">\n              <slot></slot>\n            </button>\n          </div>\n        `,\n        setup() {\n          let container = ref<HTMLElement | null>(null)\n          let enabled = ref(false)\n\n          let resolveParentContainer = computed(() => dom(container)?.parentElement ?? null)\n          useInert(resolveParentContainer, enabled)\n\n          return { container, enabled }\n        },\n      }),\n    },\n  })\n\n  await new Promise<void>(nextTick)\n\n  // Parent should not be inert yet\n  assertNotInert(document.getElementById('parent'))\n\n  // Toggle A\n  await click(getByText('A'))\n\n  // Parent should be inert\n  assertInert(document.getElementById('parent'))\n\n  // Toggle B\n  await click(getByText('B'))\n\n  // Parent should still be inert\n  assertInert(document.getElementById('parent'))\n\n  // Toggle A\n  await click(getByText('A'))\n\n  // Parent should still be inert (because B is still enabled)\n  assertInert(document.getElementById('parent'))\n\n  // Toggle B\n  await click(getByText('B'))\n\n  // Parent should not be inert because both A and B are disabled\n  assertNotInert(document.getElementById('parent'))\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-inert.ts",
    "content": "import { ref, watchEffect, type Ref } from 'vue'\nimport { dom } from '../utils/dom'\n\nlet originals = new Map<HTMLElement, { 'aria-hidden': string | null; inert: boolean }>()\nlet counts = new Map<HTMLElement, number>()\n\nexport function useInert<TElement extends HTMLElement>(\n  node: Ref<TElement | null>,\n  enabled: Ref<boolean> = ref(true)\n) {\n  watchEffect((onInvalidate) => {\n    if (!enabled.value) return\n\n    let element = dom(node)\n    if (!element) return\n\n    onInvalidate(function cleanup() {\n      if (!element) return // Should never happen\n\n      // Decrease counts\n      let count = counts.get(element) ?? 1 // Should always exist\n      if (count === 1) counts.delete(element) // We are the last one, so we can delete the count\n      else counts.set(element, count - 1) // We are not the last one\n\n      // Not the last one, so we don't restore the original values (yet)\n      if (count !== 1) return\n\n      let original = originals.get(element)\n      if (!original) return // Should never happen\n\n      // Restore original values\n      if (original['aria-hidden'] === null) element.removeAttribute('aria-hidden')\n      else element.setAttribute('aria-hidden', original['aria-hidden'])\n      element.inert = original.inert\n\n      // Remove tracking of original values\n      originals.delete(element)\n    })\n\n    // Increase count\n    let count = counts.get(element) ?? 0\n    counts.set(element, count + 1)\n\n    // Already marked as inert, no need to do it again\n    if (count !== 0) return\n\n    // Keep track of previous values, so that we can restore them when we are done\n    originals.set(element, {\n      'aria-hidden': element.getAttribute('aria-hidden'),\n      inert: element.inert,\n    })\n\n    // Mark as inert\n    element.setAttribute('aria-hidden', 'true')\n    element.inert = true\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-outside-click.ts",
    "content": "import { computed, ref, type ComputedRef, type Ref } from 'vue'\nimport { dom, isHTMLIframeElement, isHTMLorSVGElement } from '../utils/dom'\nimport { FocusableMode, isFocusableElement } from '../utils/focus-management'\nimport { isMobile } from '../utils/platform'\nimport { useDocumentEvent } from './use-document-event'\nimport { useWindowEvent } from './use-window-event'\n\ntype Container = Ref<HTMLElement | null> | HTMLElement | null\ntype ContainerCollection = Container[] | Set<Container>\ntype ContainerInput = Container | ContainerCollection\n\n// If the user moves their finger by ${MOVE_THRESHOLD_PX} pixels or more, we'll\n// assume that they are scrolling and not clicking. This will prevent the click\n// from being triggered when the user is scrolling.\n//\n// This also allows you to \"cancel\" the click by moving your finger more than\n// the threshold in pixels in any direction.\nconst MOVE_THRESHOLD_PX = 30\n\nexport function useOutsideClick(\n  containers: ContainerInput | (() => ContainerInput),\n  cb: (\n    event: MouseEvent | PointerEvent | FocusEvent | TouchEvent,\n    target: HTMLOrSVGElement & Element\n  ) => void,\n  enabled: ComputedRef<boolean> = computed(() => true)\n) {\n  function handleOutsideClick<E extends MouseEvent | PointerEvent | FocusEvent | TouchEvent>(\n    event: E,\n    resolveTarget: (event: E) => (HTMLOrSVGElement & Element) | null\n  ) {\n    // Check whether the event got prevented already. This can happen if you use the\n    // useOutsideClick hook in both a Dialog and a Menu and the inner Menu \"cancels\" the default\n    // behaviour so that only the Menu closes and not the Dialog (yet)\n    if (event.defaultPrevented) return\n\n    let target = resolveTarget(event)\n\n    if (target === null) {\n      return\n    }\n\n    // Ignore if the target doesn't exist in the DOM anymore\n    if (!target.getRootNode().contains(target)) return\n\n    let _containers = (function resolve(containers): ContainerCollection {\n      if (typeof containers === 'function') {\n        return resolve(containers())\n      }\n\n      if (Array.isArray(containers)) {\n        return containers\n      }\n\n      if (containers instanceof Set) {\n        return containers\n      }\n\n      return [containers]\n    })(containers)\n\n    // Ignore if the target exists in one of the containers\n    for (let container of _containers) {\n      if (container === null) continue\n      let domNode = container instanceof HTMLElement ? container : dom(container)\n      if (domNode?.contains(target)) {\n        return\n      }\n\n      // If the click crossed a shadow boundary, we need to check if the container\n      // is inside the tree by using `composedPath` to \"pierce\" the shadow boundary\n      if (event.composed && event.composedPath().includes(domNode as EventTarget)) {\n        return\n      }\n    }\n\n    // This allows us to check whether the event was defaultPrevented when you are nesting this\n    // inside a `<Dialog />` for example.\n    if (\n      // This check allows us to know whether or not we clicked on a \"focusable\" element like a\n      // button or an input. This is a backwards compatibility check so that you can open a <Menu\n      // /> and click on another <Menu /> which should close Menu A and open Menu B. We might\n      // revisit that so that you will require 2 clicks instead.\n      !isFocusableElement(target, FocusableMode.Loose) &&\n      // This could be improved, but the `Combobox.Button` adds tabIndex={-1} to make it\n      // unfocusable via the keyboard so that tabbing to the next item from the input doesn't\n      // first go to the button.\n      target.tabIndex !== -1\n    ) {\n      event.preventDefault()\n    }\n\n    return cb(event, target)\n  }\n\n  let initialClickTarget = ref<EventTarget | null>(null)\n\n  useDocumentEvent(\n    enabled,\n    'pointerdown',\n    (event) => {\n      initialClickTarget.value = event.composedPath?.()?.[0] || event.target\n    },\n    true\n  )\n\n  useDocumentEvent(\n    enabled,\n    'mousedown',\n    (event) => {\n      initialClickTarget.value = event.composedPath?.()?.[0] || event.target\n    },\n    true\n  )\n\n  useDocumentEvent(\n    enabled,\n    'click',\n    (event) => {\n      if (isMobile()) {\n        return\n      }\n\n      if (!initialClickTarget.value) {\n        return\n      }\n\n      handleOutsideClick(event, () => {\n        return initialClickTarget.value as HTMLElement\n      })\n\n      initialClickTarget.value = null\n    },\n\n    // We will use the `capture` phase so that layers in between with `event.stopPropagation()`\n    // don't \"cancel\" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`\n    // is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,\n    // the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.\n    true\n  )\n\n  let startPosition = { x: 0, y: 0 }\n  useDocumentEvent(\n    enabled,\n    'touchstart',\n    (event) => {\n      startPosition.x = event.touches[0].clientX\n      startPosition.y = event.touches[0].clientY\n    },\n    true\n  )\n\n  useDocumentEvent(\n    enabled,\n    'touchend',\n    (event) => {\n      // If the user moves their finger by ${MOVE_THRESHOLD_PX} pixels or more,\n      // we'll assume that they are scrolling and not clicking.\n      let endPosition = { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY }\n      if (\n        Math.abs(endPosition.x - startPosition.x) >= MOVE_THRESHOLD_PX ||\n        Math.abs(endPosition.y - startPosition.y) >= MOVE_THRESHOLD_PX\n      ) {\n        return\n      }\n\n      return handleOutsideClick(event, () => {\n        if (isHTMLorSVGElement(event.target)) {\n          return event.target\n        }\n        return null\n      })\n    },\n\n    // We will use the `capture` phase so that layers in between with `event.stopPropagation()`\n    // don't \"cancel\" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`\n    // is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,\n    // the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.\n    true\n  )\n\n  // When content inside an iframe is clicked `window` will receive a blur event\n  // This can happen when an iframe _inside_ a window is clicked\n  // Or, if headless UI is *in* the iframe, when a content in a window containing that iframe is clicked\n\n  // In this case we care only about the first case so we check to see if the active element is the iframe\n  // If so this was because of a click, focus, or other interaction with the child iframe\n  // and we can consider it an \"outside click\"\n  useWindowEvent(\n    enabled,\n    'blur',\n    (event) => {\n      return handleOutsideClick(event, () => {\n        return isHTMLIframeElement(window.document.activeElement)\n          ? window.document.activeElement\n          : null\n      })\n    },\n    true\n  )\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-resolve-button-type.ts",
    "content": "import { onMounted, ref, watchEffect, type ComponentPublicInstance, type Ref } from 'vue'\nimport { dom } from '../utils/dom'\n\nfunction resolveType(type: unknown, as: string | object) {\n  if (type) return type\n\n  let tag = as ?? 'button'\n  if (typeof tag === 'string' && tag.toLowerCase() === 'button') return 'button'\n\n  return undefined\n}\n\nexport function useResolveButtonType(\n  data: Ref<{ as: string | object; type?: unknown }>,\n  refElement: Ref<HTMLElement | ComponentPublicInstance | null>\n) {\n  let type = ref(resolveType(data.value.type, data.value.as))\n\n  onMounted(() => {\n    type.value = resolveType(data.value.type, data.value.as)\n  })\n\n  watchEffect(() => {\n    if (type.value) return\n    if (!dom(refElement)) return\n\n    if (dom(refElement) instanceof HTMLButtonElement && !dom(refElement)?.hasAttribute('type')) {\n      type.value = 'button'\n    }\n  })\n\n  return type\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-root-containers.ts",
    "content": "import { h, ref, type Ref } from 'vue'\nimport { Hidden, Features as HiddenFeatures } from '../internal/hidden'\nimport { dom } from '../utils/dom'\nimport { getOwnerDocument } from '../utils/owner'\n\nexport function useRootContainers({\n  defaultContainers = [],\n  portals,\n  mainTreeNodeRef: _mainTreeNodeRef,\n}: {\n  defaultContainers?: (HTMLElement | null | Ref<HTMLElement | null>)[]\n  portals?: Ref<HTMLElement[]>\n  mainTreeNodeRef?: Ref<HTMLElement | null>\n} = {}) {\n  // Reference to a node in the \"main\" tree, not in the portalled Dialog tree.\n  let mainTreeNodeRef = ref<HTMLElement | null>(null)\n  let ownerDocument = getOwnerDocument(mainTreeNodeRef)\n\n  function resolveContainers() {\n    let containers: HTMLElement[] = []\n\n    // Resolve default containers\n    for (let container of defaultContainers) {\n      if (container === null) continue\n      if (container instanceof HTMLElement) {\n        containers.push(container)\n      } else if ('value' in container && container.value instanceof HTMLElement) {\n        containers.push(container.value)\n      }\n    }\n\n    // Resolve portal containers\n    if (portals?.value) {\n      for (let portal of portals.value) {\n        containers.push(portal)\n      }\n    }\n\n    // Resolve third party (root) containers\n    for (let container of ownerDocument?.querySelectorAll('html > *, body > *') ?? []) {\n      if (container === document.body) continue // Skip `<body>`\n      if (container === document.head) continue // Skip `<head>`\n      if (!(container instanceof HTMLElement)) continue // Skip non-HTMLElements\n      if (container.id === 'headlessui-portal-root') continue // Skip the Headless UI portal root\n      if (container.contains(dom(mainTreeNodeRef))) continue // Skip if it is the main app\n      if (container.contains((dom(mainTreeNodeRef)?.getRootNode() as ShadowRoot)?.host)) continue // Skip if it is the main app (and the component is inside a shadow root)\n      if (containers.some((defaultContainer) => container.contains(defaultContainer))) continue // Skip if the current container is part of a container we've already seen (e.g.: default container / portal)\n\n      containers.push(container)\n    }\n\n    return containers\n  }\n\n  return {\n    resolveContainers,\n    contains(element: HTMLElement) {\n      return resolveContainers().some((container) => container.contains(element))\n    },\n    mainTreeNodeRef,\n    MainTreeNode() {\n      if (_mainTreeNodeRef != null) return null\n      return h(Hidden, { features: HiddenFeatures.Hidden, ref: mainTreeNodeRef })\n    },\n  }\n}\n\nexport function useMainTreeNode() {\n  let mainTreeNodeRef = ref<HTMLElement | null>(null)\n\n  return {\n    mainTreeNodeRef,\n    MainTreeNode() {\n      return h(Hidden, { features: HiddenFeatures.Hidden, ref: mainTreeNodeRef })\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-store.ts",
    "content": "import { onUnmounted, shallowRef } from 'vue'\nimport type { Store } from '../utils/store'\n\nexport function useStore<T>(store: Store<T, any>) {\n  let state = shallowRef(store.getSnapshot())\n\n  onUnmounted(\n    store.subscribe(() => {\n      state.value = store.getSnapshot()\n    })\n  )\n\n  return state\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-tab-direction.ts",
    "content": "import { ref } from 'vue'\nimport { useWindowEvent } from './use-window-event'\n\nexport enum Direction {\n  Forwards,\n  Backwards,\n}\n\nexport function useTabDirection() {\n  let direction = ref(Direction.Forwards)\n  let enabled = ref(true)\n\n  useWindowEvent(enabled, 'keydown', (event) => {\n    if (event.key === 'Tab') {\n      direction.value = event.shiftKey ? Direction.Backwards : Direction.Forwards\n    }\n  })\n\n  return direction\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-text-value.ts",
    "content": "import { ref, type Ref } from 'vue'\nimport { dom } from '../utils/dom'\nimport { getTextValue } from '../utils/get-text-value'\n\nexport function useTextValue(element: Ref<HTMLElement | null>) {\n  let cacheKey = ref<string>('')\n  let cacheValue = ref<string>('')\n\n  return () => {\n    let el = dom(element)\n    if (!el) return ''\n\n    // Check for a cached version\n    let currentKey = el.innerText\n    if (cacheKey.value === currentKey) {\n      return cacheValue.value\n    }\n\n    // Calculate the value\n    let value = getTextValue(el).trim().toLowerCase()\n    cacheKey.value = currentKey\n    cacheValue.value = value\n    return value\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-tracked-pointer.ts",
    "content": "import { ref } from 'vue'\n\ntype PointerPosition = [x: number, y: number]\n\nfunction eventToPosition(evt: PointerEvent): PointerPosition {\n  return [evt.screenX, evt.screenY]\n}\n\nexport function useTrackedPointer() {\n  let lastPos = ref<PointerPosition>([-1, -1])\n\n  return {\n    wasMoved(evt: PointerEvent) {\n      // FIXME: Remove this once we use browser testing in all the relevant places.\n      // NOTE: This is replaced with a compile-time define during the build process\n      // This hack exists to work around a few failing tests caused by our inability to \"move\" the virtual pointer in JSDOM pointer events.\n      if (typeof process !== 'undefined' && process.env.TEST_BYPASS_TRACKED_POINTER) {\n        return true\n      }\n\n      let newPos = eventToPosition(evt)\n\n      if (lastPos.value[0] === newPos[0] && lastPos.value[1] === newPos[1]) {\n        return false\n      }\n\n      lastPos.value = newPos\n      return true\n    },\n\n    update(evt: PointerEvent) {\n      lastPos.value = eventToPosition(evt)\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-tree-walker.ts",
    "content": "import { watchEffect, type ComputedRef } from 'vue'\nimport { getOwnerDocument } from '../utils/owner'\n\ntype AcceptNode = (\n  node: HTMLElement\n) =>\n  | typeof NodeFilter.FILTER_ACCEPT\n  | typeof NodeFilter.FILTER_SKIP\n  | typeof NodeFilter.FILTER_REJECT\n\nexport function useTreeWalker({\n  container,\n  accept,\n  walk,\n  enabled,\n}: {\n  container: ComputedRef<HTMLElement | null>\n  accept: AcceptNode\n  walk(node: HTMLElement): void\n  enabled?: ComputedRef<boolean>\n}) {\n  watchEffect(() => {\n    let root = container.value\n    if (!root) return\n    if (enabled !== undefined && !enabled.value) return\n    let ownerDocument = getOwnerDocument(container)\n    if (!ownerDocument) return\n\n    let acceptNode = Object.assign((node: HTMLElement) => accept(node), { acceptNode: accept })\n    let walker = ownerDocument.createTreeWalker(\n      root,\n      NodeFilter.SHOW_ELEMENT,\n      acceptNode,\n      // @ts-expect-error This `false` is a simple small fix for older browsers\n      false\n    )\n\n    while (walker.nextNode()) walk(walker.currentNode as HTMLElement)\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/hooks/use-window-event.ts",
    "content": "import { watchEffect, type Ref } from 'vue'\nimport { env } from '../utils/env'\n\nexport function useWindowEvent<TType extends keyof WindowEventMap>(\n  enabled: Ref<boolean>,\n  type: TType,\n  listener: (this: Window, ev: WindowEventMap[TType]) => any,\n  options?: boolean | AddEventListenerOptions\n) {\n  if (env.isServer) return\n\n  watchEffect((onInvalidate) => {\n    if (!enabled.value) return\n\n    window.addEventListener(type, listener, options)\n    onInvalidate(() => window.removeEventListener(type, listener, options))\n  })\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/index.test.ts",
    "content": "import * as HeadlessUI from './index'\n\n/**\n * Looks a bit of a silly test, however this ensures that we don't accidentally expose something to\n * the outside world that we didn't want!\n */\nit('should expose the correct components', () => {\n  expect(Object.keys(HeadlessUI).sort((a, z) => a.localeCompare(z))).toEqual([\n    // Combobox\n    'Combobox',\n    'ComboboxButton',\n    'ComboboxInput',\n    'ComboboxLabel',\n    'ComboboxOption',\n    'ComboboxOptions',\n\n    // Dialog\n    'Dialog',\n    'DialogBackdrop',\n    'DialogDescription',\n    'DialogOverlay',\n    'DialogPanel',\n    'DialogTitle',\n\n    // Disclosure\n    'Disclosure',\n    'DisclosureButton',\n    'DisclosurePanel',\n\n    // FocusTrap\n    'FocusTrap',\n\n    // Listbox\n    'Listbox',\n    'ListboxButton',\n    'ListboxLabel',\n    'ListboxOption',\n    'ListboxOptions',\n\n    // Menu\n    'Menu',\n    'MenuButton',\n    'MenuItem',\n    'MenuItems',\n\n    // Popover\n    'Popover',\n    'PopoverButton',\n    'PopoverGroup',\n    'PopoverOverlay',\n    'PopoverPanel',\n\n    // Portal\n    'Portal',\n    'PortalGroup',\n\n    // RadioGroup\n    'RadioGroup',\n    'RadioGroupDescription',\n    'RadioGroupLabel',\n    'RadioGroupOption',\n\n    // Switch\n    'Switch',\n    'SwitchDescription',\n    'SwitchGroup',\n    'SwitchLabel',\n\n    // Tabs\n    'Tab',\n    'TabGroup',\n    'TabList',\n    'TabPanel',\n    'TabPanels',\n\n    // Transition\n    'TransitionChild',\n    'TransitionRoot',\n  ])\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/index.ts",
    "content": "export * from './components/combobox/combobox'\nexport * from './components/dialog/dialog'\nexport * from './components/disclosure/disclosure'\nexport * from './components/focus-trap/focus-trap'\nexport * from './components/listbox/listbox'\nexport * from './components/menu/menu'\nexport * from './components/popover/popover'\nexport { Portal, PortalGroup } from './components/portal/portal'\nexport * from './components/radio-group/radio-group'\nexport * from './components/switch/switch'\nexport * from './components/tabs/tabs'\nexport * from './components/transitions/transition'\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/dom-containers.ts",
    "content": "export function contains(containers: Set<HTMLElement>, element: HTMLElement) {\n  for (let container of containers) {\n    if (container.contains(element)) return true\n  }\n\n  return false\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/focus-sentinel.ts",
    "content": "import { defineComponent, h, ref } from 'vue'\nimport { Features, Hidden } from './hidden'\n\nexport let FocusSentinel = defineComponent({\n  props: {\n    onFocus: {\n      type: Function,\n      required: true,\n    },\n  },\n  setup(props) {\n    let enabled = ref(true)\n\n    return () => {\n      if (!enabled.value) return null\n\n      return h(Hidden, {\n        as: 'button',\n        type: 'button',\n        features: Features.Focusable,\n        onFocus(event: FocusEvent) {\n          event.preventDefault()\n          let frame: ReturnType<typeof requestAnimationFrame>\n\n          let tries = 50\n          function forwardFocus() {\n            // Prevent infinite loops\n            if (tries-- <= 0) {\n              if (frame) cancelAnimationFrame(frame)\n              return\n            }\n\n            // Try to move focus to the correct element. This depends on the implementation\n            // of `onFocus` of course since it would be different for each place we use it in.\n            if (props.onFocus?.()) {\n              enabled.value = false\n              cancelAnimationFrame(frame)\n              return\n            }\n\n            // Retry\n            frame = requestAnimationFrame(forwardFocus)\n          }\n\n          frame = requestAnimationFrame(forwardFocus)\n        },\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/hidden.ts",
    "content": "import { defineComponent, type PropType } from 'vue'\nimport { render } from '../utils/render'\n\nexport enum Features {\n  // The default, no features.\n  None = 1 << 0,\n\n  // Whether the element should be focusable or not.\n  Focusable = 1 << 1,\n\n  // Whether it should be completely hidden, even to assistive technologies.\n  Hidden = 1 << 2,\n}\n\nexport let Hidden = defineComponent({\n  name: 'Hidden',\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    features: { type: Number as PropType<Features>, default: Features.None },\n  },\n  setup(props, { slots, attrs }) {\n    return () => {\n      let { features, ...theirProps } = props\n      let ourProps = {\n        'aria-hidden':\n          (features & Features.Focusable) === Features.Focusable\n            ? true\n            : // @ts-ignore\n              theirProps['aria-hidden'] ?? undefined,\n        hidden: (features & Features.Hidden) === Features.Hidden ? true : undefined,\n        style: {\n          position: 'fixed',\n          top: 1,\n          left: 1,\n          width: 1,\n          height: 0,\n          padding: 0,\n          margin: -1,\n          overflow: 'hidden',\n          clip: 'rect(0, 0, 0, 0)',\n          whiteSpace: 'nowrap',\n          borderWidth: '0',\n          ...((features & Features.Hidden) === Features.Hidden &&\n            !((features & Features.Focusable) === Features.Focusable) && { display: 'none' }),\n        },\n      }\n\n      return render({\n        ourProps,\n        theirProps,\n        slot: {},\n        attrs,\n        slots,\n        name: 'Hidden',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/open-closed.ts",
    "content": "import { inject, provide, type InjectionKey, type Ref } from 'vue'\n\nlet Context = Symbol('Context') as InjectionKey<Ref<State>>\n\nexport enum State {\n  Open = 1 << 0,\n  Closed = 1 << 1,\n  Closing = 1 << 2,\n  Opening = 1 << 3,\n}\n\nexport function hasOpenClosed() {\n  return useOpenClosed() !== null\n}\n\nexport function useOpenClosed() {\n  return inject(Context, null)\n}\n\nexport function useOpenClosedProvider(value: Ref<State>) {\n  provide(Context, value)\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/portal-force-root.ts",
    "content": "import { defineComponent, inject, provide, type InjectionKey } from 'vue'\nimport { render } from '../utils/render'\n\nlet ForcePortalRootContext = Symbol('ForcePortalRootContext') as InjectionKey<Boolean>\n\nexport function usePortalRoot() {\n  return inject(ForcePortalRootContext, false)\n}\n\nexport let ForcePortalRoot = defineComponent({\n  name: 'ForcePortalRoot',\n  props: {\n    as: { type: [Object, String], default: 'template' },\n    force: { type: Boolean, default: false },\n  },\n  setup(props, { slots, attrs }) {\n    provide(ForcePortalRootContext, props.force)\n\n    return () => {\n      let { force, ...theirProps } = props\n      return render({\n        theirProps,\n        ourProps: {},\n        slot: {},\n        slots,\n        attrs,\n        name: 'ForcePortalRoot',\n      })\n    }\n  },\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/internal/stack-context.ts",
    "content": "import { inject, onMounted, onUnmounted, provide, watch, type InjectionKey, type Ref } from 'vue'\n\ntype OnUpdate = (message: StackMessage, type: string, element: Ref<HTMLElement | null>) => void\n\nlet StackContext = Symbol('StackContext') as InjectionKey<OnUpdate>\n\nexport enum StackMessage {\n  Add,\n  Remove,\n}\n\nexport function useStackContext() {\n  return inject(StackContext, () => {})\n}\n\nexport function useStackProvider({\n  type,\n  enabled,\n  element,\n  onUpdate,\n}: {\n  type: string\n  enabled: Ref<boolean | undefined>\n  element: Ref<HTMLElement | null>\n  onUpdate?: OnUpdate\n}) {\n  let parentUpdate = useStackContext()\n\n  function notify(...args: Parameters<OnUpdate>) {\n    // Notify our layer\n    onUpdate?.(...args)\n\n    // Notify the parent\n    parentUpdate(...args)\n  }\n\n  onMounted(() => {\n    watch(\n      enabled,\n      (isEnabled, oldIsEnabled) => {\n        if (isEnabled) {\n          notify(StackMessage.Add, type, element)\n        } else if (oldIsEnabled === true) {\n          notify(StackMessage.Remove, type, element)\n        }\n      },\n      { immediate: true, flush: 'sync' }\n    )\n  })\n\n  onUnmounted(() => {\n    if (enabled.value) {\n      notify(StackMessage.Remove, type, element)\n    }\n  })\n\n  provide(StackContext, notify)\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/keyboard.ts",
    "content": "// TODO: This must already exist somewhere, right? 🤔\n// Ref: https://www.w3.org/TR/uievents-key/#named-key-attribute-values\nexport enum Keys {\n  Space = ' ',\n  Enter = 'Enter',\n  Escape = 'Escape',\n  Backspace = 'Backspace',\n  Delete = 'Delete',\n\n  ArrowLeft = 'ArrowLeft',\n  ArrowUp = 'ArrowUp',\n  ArrowRight = 'ArrowRight',\n  ArrowDown = 'ArrowDown',\n\n  Home = 'Home',\n  End = 'End',\n\n  PageUp = 'PageUp',\n  PageDown = 'PageDown',\n\n  Tab = 'Tab',\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/mouse.ts",
    "content": "export enum MouseButton {\n  Left = 0,\n  Right = 2,\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts",
    "content": "import { FocusableMode, isFocusableElement } from '../utils/focus-management'\n\nfunction assertNever(x: never): never {\n  throw new Error('Unexpected object: ' + x)\n}\n\n// ---\n\nexport function getMenuButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-menu-button-\"]')\n}\n\nexport function getMenuButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getMenu(): HTMLElement | null {\n  return document.querySelector('[role=\"menu\"]')\n}\n\nexport function getMenus(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"menu\"]'))\n}\n\nexport function getMenuItems(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"menuitem\"]'))\n}\n\n// ---\n\nexport enum MenuState {\n  /** The menu is visible to the user. */\n  Visible,\n\n  /** The menu is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The menu is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport function assertMenuButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: MenuState\n  },\n  button = getMenuButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case MenuState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case MenuState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case MenuState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuButton)\n    throw err\n  }\n}\n\nexport function assertMenuButtonLinkedWithMenu(button = getMenuButton(), menu = getMenu()) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (menu === null) return expect(menu).not.toBe(null)\n\n    // Ensure link between button & menu is correct\n    expect(button).toHaveAttribute('aria-controls', menu.getAttribute('id'))\n    expect(menu).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuButtonLinkedWithMenu)\n    throw err\n  }\n}\n\nexport function assertMenuLinkedWithMenuItem(item: HTMLElement | null, menu = getMenu()) {\n  try {\n    if (menu === null) return expect(menu).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between menu & menu item is correct\n    expect(menu).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuLinkedWithMenuItem)\n    throw err\n  }\n}\n\nexport function assertNoActiveMenuItem(menu = getMenu()) {\n  try {\n    if (menu === null) return expect(menu).not.toBe(null)\n\n    // Ensure we don't have an active menu\n    expect(menu).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveMenuItem)\n    throw err\n  }\n}\n\nexport function assertMenu(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: MenuState\n  },\n  menu = getMenu()\n) {\n  try {\n    switch (options.state) {\n      case MenuState.InvisibleHidden:\n        if (menu === null) return expect(menu).not.toBe(null)\n\n        assertHidden(menu)\n\n        expect(menu).toHaveAttribute('aria-labelledby')\n        expect(menu).toHaveAttribute('role', 'menu')\n\n        if (options.textContent) expect(menu).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(menu).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case MenuState.Visible:\n        if (menu === null) return expect(menu).not.toBe(null)\n\n        assertVisible(menu)\n\n        expect(menu).toHaveAttribute('aria-labelledby')\n        expect(menu).toHaveAttribute('role', 'menu')\n\n        if (options.textContent) expect(menu).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(menu).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case MenuState.InvisibleUnmounted:\n        expect(menu).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenu)\n    throw err\n  }\n}\n\nexport function assertMenuItem(\n  item: HTMLElement | null,\n  options?: { tag?: string; attributes?: Record<string, string | null> }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'menuitem')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure menu button has the following attributes\n    if (options) {\n      for (let attributeName in options.attributes) {\n        expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n      }\n\n      if (options.tag) {\n        expect(item.tagName.toLowerCase()).toBe(options.tag)\n      }\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertMenuItem)\n    throw err\n  }\n}\n\n// ---\n\nexport function getComboboxLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-combobox-label\"]')\n}\n\nexport function getComboboxButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-combobox-button-\"]')\n}\n\nexport function getComboboxButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getComboboxInput(): HTMLInputElement | null {\n  return document.querySelector('[role=\"combobox\"]')\n}\n\nexport function getCombobox(): HTMLElement | null {\n  return document.querySelector('[role=\"listbox\"]')\n}\n\nexport function getComboboxInputs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"combobox\"]'))\n}\n\nexport function getComboboxes(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"listbox\"]'))\n}\n\nexport function getComboboxOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"option\"]'))\n}\n\n// ---\n\nexport enum ComboboxState {\n  /** The combobox is visible to the user. */\n  Visible,\n\n  /** The combobox is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The combobox is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport enum ComboboxMode {\n  /** The combobox is in the `single` mode. */\n  Single,\n\n  /** The combobox is in the `multiple` mode. */\n  Multiple,\n}\n\nexport function assertCombobox(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n    mode?: ComboboxMode\n  },\n  combobox = getComboboxInput(),\n  listbox = getListbox()\n) {\n  try {\n    switch (options.state) {\n      case ComboboxState.InvisibleHidden:\n        if (combobox === null) return expect(combobox).not.toBe(null)\n\n        assertHidden(combobox)\n\n        expect(combobox).toHaveAttribute('role', 'combobox')\n\n        if (options.textContent) expect(combobox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(combobox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.Visible:\n        if (combobox === null) return expect(combobox).not.toBe(null)\n\n        assertVisible(combobox)\n\n        expect(combobox).toHaveAttribute('role', 'combobox')\n\n        if (options.mode && options.mode === ComboboxMode.Multiple) {\n          expect(listbox).toHaveAttribute('aria-multiselectable', 'true')\n        }\n\n        if (options.textContent) expect(combobox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(combobox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(combobox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxInput(\n  options: {\n    attributes?: Record<string, string | null>\n    state: ComboboxState\n  },\n  input = getComboboxInput()\n) {\n  try {\n    if (input === null) return expect(input).not.toBe(null)\n\n    // Ensure combobox input has these properties\n    expect(input).toHaveAttribute('id')\n\n    switch (options.state) {\n      case ComboboxState.Visible:\n        expect(input).toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ComboboxState.InvisibleHidden:\n        expect(input).toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(input).not.toHaveAttribute('aria-controls')\n        expect(input).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    // Ensure combobox input has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(input).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxInput)\n    throw err\n  }\n}\n\nexport function assertComboboxList(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n  },\n  listbox = getCombobox()\n) {\n  try {\n    switch (options.state) {\n      case ComboboxState.InvisibleHidden:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertHidden(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.Visible:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertVisible(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(listbox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ComboboxState\n  },\n  button = getComboboxButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case ComboboxState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ComboboxState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ComboboxState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxButton)\n    throw err\n  }\n}\n\nexport function assertComboboxLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    tag?: string\n    textContent?: string\n  },\n  label = getComboboxLabel()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(label).toHaveAttribute('id')\n\n    if (options.textContent) {\n      expect(label).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(label.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxLabel)\n    throw err\n  }\n}\n\nexport function assertComboboxButtonLinkedWithCombobox(\n  button = getComboboxButton(),\n  combobox = getCombobox()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    // Ensure link between button & combobox is correct\n    expect(button).toHaveAttribute('aria-controls', combobox.getAttribute('id'))\n    expect(combobox).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxButtonLinkedWithCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxLabelLinkedWithCombobox(\n  label = getComboboxLabel(),\n  combobox = getComboboxInput()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    expect(combobox).toHaveAttribute('aria-labelledby', label.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxLabelLinkedWithCombobox)\n    throw err\n  }\n}\n\nexport function assertComboboxButtonLinkedWithComboboxLabel(\n  button = getComboboxButton(),\n  label = getComboboxLabel()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure link between button & label is correct\n    expect(button).toHaveAttribute('aria-labelledby', `${label.id} ${button.id}`)\n  } catch (err) {\n    if (err instanceof Error)\n      Error.captureStackTrace(err, assertComboboxButtonLinkedWithComboboxLabel)\n    throw err\n  }\n}\n\nexport function assertActiveComboboxOption(\n  item: HTMLElement | null,\n  combobox = getComboboxInput()\n) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between combobox & combobox item is correct\n    expect(combobox).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNotActiveComboboxOption(\n  item: HTMLElement | null,\n  combobox = getComboboxInput()\n) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between combobox & combobox item does not exist\n    expect(combobox).not.toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNoActiveComboboxOption(combobox = getComboboxInput()) {\n  try {\n    if (combobox === null) return expect(combobox).not.toBe(null)\n\n    // Ensure we don't have an active combobox\n    expect(combobox).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveComboboxOption)\n    throw err\n  }\n}\n\nexport function assertNoSelectedComboboxOption(items = getComboboxOptions()) {\n  try {\n    for (let item of items) expect(item).toHaveAttribute('aria-selected', 'false')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoSelectedComboboxOption)\n    throw err\n  }\n}\n\nexport function assertComboboxOption(\n  item: HTMLElement | null,\n  options?: {\n    tag?: string\n    attributes?: Record<string, string | null>\n    selected?: boolean\n  }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'option')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure combobox button has the following attributes\n    if (!options) return\n\n    for (let attributeName in options.attributes) {\n      expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n\n    if (options.tag) {\n      expect(item.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.selected != null) {\n      return expect(item).toHaveAttribute('aria-selected', options.selected ? 'true' : 'false')\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertComboboxOption)\n    throw err\n  }\n}\n\n// ---\n\nexport function getListboxLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-listbox-label\"]')\n}\n\nexport function getListboxButton(): HTMLElement | null {\n  return document.querySelector('button,[role=\"button\"],[id^=\"headlessui-listbox-button-\"]')\n}\n\nexport function getListboxButtons(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('button,[role=\"button\"]'))\n}\n\nexport function getListbox(): HTMLElement | null {\n  return document.querySelector('[role=\"listbox\"]')\n}\n\nexport function getListboxes(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"listbox\"]'))\n}\n\nexport function getListboxOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"option\"]'))\n}\n\n// ---\n\nexport enum ListboxState {\n  /** The listbox is visible to the user. */\n  Visible,\n\n  /** The listbox is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The listbox is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\nexport enum ListboxMode {\n  /** The listbox is in the `single` mode. */\n  Single,\n\n  /** The listbox is in the `multiple` mode. */\n  Multiple,\n}\n\nexport function assertListbox(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ListboxState\n    mode?: ListboxMode\n    orientation?: 'horizontal' | 'vertical'\n  },\n  listbox = getListbox()\n) {\n  let { orientation = 'vertical' } = options\n\n  try {\n    switch (options.state) {\n      case ListboxState.InvisibleHidden:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertHidden(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('aria-orientation', orientation)\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ListboxState.Visible:\n        if (listbox === null) return expect(listbox).not.toBe(null)\n\n        assertVisible(listbox)\n\n        expect(listbox).toHaveAttribute('aria-labelledby')\n        expect(listbox).toHaveAttribute('aria-orientation', orientation)\n        expect(listbox).toHaveAttribute('role', 'listbox')\n\n        if (options.mode && options.mode === ListboxMode.Multiple) {\n          expect(listbox).toHaveAttribute('aria-multiselectable', 'true')\n        }\n\n        if (options.textContent) expect(listbox).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(listbox).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case ListboxState.InvisibleUnmounted:\n        expect(listbox).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListbox)\n    throw err\n  }\n}\n\nexport function assertListboxButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: ListboxState\n  },\n  button = getListboxButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(button).toHaveAttribute('id')\n    expect(button).toHaveAttribute('aria-haspopup')\n\n    switch (options.state) {\n      case ListboxState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case ListboxState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case ListboxState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxButton)\n    throw err\n  }\n}\n\nexport function assertListboxLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    tag?: string\n    textContent?: string\n  },\n  label = getListboxLabel()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure menu button have these properties\n    expect(label).toHaveAttribute('id')\n\n    if (options.textContent) {\n      expect(label).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(label.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    // Ensure menu button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxLabel)\n    throw err\n  }\n}\n\nexport function assertListboxButtonLinkedWithListbox(\n  button = getListboxButton(),\n  listbox = getListbox()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    // Ensure link between button & listbox is correct\n    expect(button).toHaveAttribute('aria-controls', listbox.getAttribute('id'))\n    expect(listbox).toHaveAttribute('aria-labelledby', button.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxButtonLinkedWithListbox)\n    throw err\n  }\n}\n\nexport function assertListboxLabelLinkedWithListbox(\n  label = getListboxLabel(),\n  listbox = getListbox()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    expect(listbox).toHaveAttribute('aria-labelledby', label.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxLabelLinkedWithListbox)\n    throw err\n  }\n}\n\nexport function assertListboxButtonLinkedWithListboxLabel(\n  button = getListboxButton(),\n  label = getListboxLabel()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n    if (label === null) return expect(label).not.toBe(null)\n\n    // Ensure link between button & label is correct\n    expect(button).toHaveAttribute('aria-labelledby', `${label.id} ${button.id}`)\n  } catch (err) {\n    if (err instanceof Error)\n      Error.captureStackTrace(err, assertListboxButtonLinkedWithListboxLabel)\n    throw err\n  }\n}\n\nexport function assertActiveListboxOption(item: HTMLElement | null, listbox = getListbox()) {\n  try {\n    if (listbox === null) return expect(listbox).not.toBe(null)\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Ensure link between listbox & listbox item is correct\n    expect(listbox).toHaveAttribute('aria-activedescendant', item.getAttribute('id'))\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveListboxOption)\n    throw err\n  }\n}\n\nexport function assertNoActiveListboxOption(listbox = getListbox()) {\n  try {\n    if (listbox === null) return expect(listbox).not.toBe(null)\n\n    // Ensure we don't have an active listbox\n    expect(listbox).not.toHaveAttribute('aria-activedescendant')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoActiveListboxOption)\n    throw err\n  }\n}\n\nexport function assertNoSelectedListboxOption(items = getListboxOptions()) {\n  try {\n    for (let item of items) expect(item).toHaveAttribute('aria-selected', 'false')\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNoSelectedListboxOption)\n    throw err\n  }\n}\n\nexport function assertListboxOption(\n  item: HTMLElement | null,\n  options?: {\n    tag?: string\n    attributes?: Record<string, string | null>\n    selected?: boolean\n  }\n) {\n  try {\n    if (item === null) return expect(item).not.toBe(null)\n\n    // Check that some attributes exists, doesn't really matter what the values are at this point in\n    // time, we just require them.\n    expect(item).toHaveAttribute('id')\n\n    // Check that we have the correct values for certain attributes\n    expect(item).toHaveAttribute('role', 'option')\n    if (!item.getAttribute('aria-disabled')) expect(item).toHaveAttribute('tabindex', '-1')\n\n    // Ensure listbox button has the following attributes\n    if (!options) return\n\n    for (let attributeName in options.attributes) {\n      expect(item).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n\n    if (options.tag) {\n      expect(item.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.selected != null) {\n      return expect(item).toHaveAttribute('aria-selected', options.selected ? 'true' : 'false')\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertListboxOption)\n    throw err\n  }\n}\n\n// ---\n\nexport function getSwitch(): HTMLElement | null {\n  return document.querySelector('[role=\"switch\"]')\n}\n\nexport function getSwitchLabel(): HTMLElement | null {\n  return document.querySelector('label,[id^=\"headlessui-switch-label\"]')\n}\n\n// ---\n\nexport enum SwitchState {\n  On,\n  Off,\n}\n\nexport function assertSwitch(\n  options: {\n    state: SwitchState\n    tag?: string\n    textContent?: string\n    label?: string\n    description?: string\n    attributes?: Record<string, string | null>\n  },\n  switchElement = getSwitch()\n) {\n  try {\n    if (switchElement === null) return expect(switchElement).not.toBe(null)\n\n    expect(switchElement).toHaveAttribute('role', 'switch')\n    let tabIndex = Number(switchElement.getAttribute('tabindex') ?? '0')\n    expect(tabIndex).toBeGreaterThanOrEqual(0)\n\n    if (options.textContent) {\n      expect(switchElement).toHaveTextContent(options.textContent)\n    }\n\n    if (options.tag) {\n      expect(switchElement.tagName.toLowerCase()).toBe(options.tag)\n    }\n\n    if (options.label) {\n      assertLabelValue(switchElement, options.label)\n    }\n\n    if (options.description) {\n      assertDescriptionValue(switchElement, options.description)\n    }\n\n    switch (options.state) {\n      case SwitchState.On:\n        expect(switchElement).toHaveAttribute('aria-checked', 'true')\n        break\n\n      case SwitchState.Off:\n        expect(switchElement).toHaveAttribute('aria-checked', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    // Ensure disclosure button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(switchElement).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertSwitch)\n    throw err\n  }\n}\n\n// ---\n\nexport function getDisclosureButton(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-disclosure-button-\"]')\n}\n\nexport function getDisclosurePanel(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-disclosure-panel-\"]')\n}\n\n// ---\n\nexport enum DisclosureState {\n  /** The disclosure is visible to the user. */\n  Visible,\n\n  /** The disclosure is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The disclosure is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertDisclosureButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DisclosureState\n  },\n  button = getDisclosureButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure disclosure button have these properties\n    expect(button).toHaveAttribute('id')\n\n    switch (options.state) {\n      case DisclosureState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case DisclosureState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case DisclosureState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure disclosure button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDisclosureButton)\n    throw err\n  }\n}\n\nexport function assertDisclosurePanel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DisclosureState\n  },\n  panel = getDisclosurePanel()\n) {\n  try {\n    switch (options.state) {\n      case DisclosureState.InvisibleHidden:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertHidden(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DisclosureState.Visible:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertVisible(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DisclosureState.InvisibleUnmounted:\n        expect(panel).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDisclosurePanel)\n    throw err\n  }\n}\n\n// ---\n\nexport function getPopoverButton(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-button-\"]')\n}\n\nexport function getPopoverPanel(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-panel-\"]')\n}\n\nexport function getPopoverOverlay(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-popover-overlay-\"]')\n}\n\n// ---\n\nexport enum PopoverState {\n  /** The popover is visible to the user. */\n  Visible,\n\n  /** The popover is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The popover is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertPopoverButton(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: PopoverState\n  },\n  button = getPopoverButton()\n) {\n  try {\n    if (button === null) return expect(button).not.toBe(null)\n\n    // Ensure popover button have these properties\n    expect(button).toHaveAttribute('id')\n\n    switch (options.state) {\n      case PopoverState.Visible:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'true')\n        break\n\n      case PopoverState.InvisibleHidden:\n        expect(button).toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      case PopoverState.InvisibleUnmounted:\n        expect(button).not.toHaveAttribute('aria-controls')\n        expect(button).toHaveAttribute('aria-expanded', 'false')\n        break\n\n      default:\n        assertNever(options.state)\n    }\n\n    if (options.textContent) {\n      expect(button).toHaveTextContent(options.textContent)\n    }\n\n    // Ensure popover button has the following attributes\n    for (let attributeName in options.attributes) {\n      expect(button).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertPopoverButton)\n    throw err\n  }\n}\n\nexport function assertPopoverPanel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: PopoverState\n  },\n  panel = getPopoverPanel()\n) {\n  try {\n    switch (options.state) {\n      case PopoverState.InvisibleHidden:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertHidden(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case PopoverState.Visible:\n        if (panel === null) return expect(panel).not.toBe(null)\n\n        assertVisible(panel)\n\n        if (options.textContent) expect(panel).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(panel).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case PopoverState.InvisibleUnmounted:\n        expect(panel).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertPopoverPanel)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertLabelValue(element: HTMLElement | null, value: string) {\n  if (element === null) return expect(element).not.toBe(null)\n\n  if (element.hasAttribute('aria-labelledby')) {\n    let ids = element.getAttribute('aria-labelledby')!.split(' ')\n    expect(ids.map((id) => document.getElementById(id)?.textContent).join(' ')).toEqual(value)\n    return\n  }\n\n  if (element.hasAttribute('aria-label')) {\n    expect(element).toHaveAttribute('aria-label', value)\n    return\n  }\n\n  if (element.hasAttribute('id') && document.querySelectorAll(`[for=\"${element.id}\"]`).length > 0) {\n    expect(document.querySelector(`[for=\"${element.id}\"]`)).toHaveTextContent(value)\n    return\n  }\n\n  expect(element).toHaveTextContent(value)\n}\n\n// ---\n\nexport function assertDescriptionValue(element: HTMLElement | null, value: string) {\n  if (element === null) return expect(element).not.toBe(null)\n\n  let id = element.getAttribute('aria-describedby')!\n  expect(document.getElementById(id)?.textContent).toEqual(value)\n}\n\n// ---\n\nexport function getDialog(): HTMLElement | null {\n  return document.querySelector('[role=\"dialog\"],[role=\"alertdialog\"]')\n}\n\nexport function getDialogs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[role=\"dialog\"],[role=\"alertdialog\"]'))\n}\n\nexport function getDialogTitle(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-dialog-title-\"]')\n}\n\nexport function getDialogDescription(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-description-\"]')\n}\n\nexport function getDialogOverlay(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-dialog-overlay-\"]')\n}\n\nexport function getDialogBackdrop(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-dialog-backdrop-\"]')\n}\n\nexport function getDialogOverlays(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-dialog-overlay-\"]'))\n}\n\n// ---\n\nexport enum DialogState {\n  /** The dialog is visible to the user. */\n  Visible,\n\n  /** The dialog is **not** visible to the user. It's still in the DOM, but it is hidden. */\n  InvisibleHidden,\n\n  /** The dialog is **not** visible to the user. It's not in the DOM, it is unmounted. */\n  InvisibleUnmounted,\n}\n\n// ---\n\nexport function assertDialog(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(dialog)\n\n        expect(dialog).toHaveAttribute('role', options.attributes?.['role'] ?? 'dialog')\n        expect(dialog).not.toHaveAttribute('aria-modal', 'true')\n\n        if (options.textContent) expect(dialog).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(dialog).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(dialog)\n\n        expect(dialog).toHaveAttribute('role', options.attributes?.['role'] ?? 'dialog')\n        expect(dialog).toHaveAttribute('aria-modal', 'true')\n\n        if (options.textContent) expect(dialog).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(dialog).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(dialog).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialog)\n    throw err\n  }\n}\n\nexport function assertDialogTitle(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  title = getDialogTitle(),\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (title === null) return expect(title).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(title)\n\n        expect(title).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-labelledby', title.id)\n\n        if (options.textContent) expect(title).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(title).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (title === null) return expect(title).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(title)\n\n        expect(title).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-labelledby', title.id)\n\n        if (options.textContent) expect(title).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(title).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(title).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialogTitle)\n    throw err\n  }\n}\n\nexport function assertDialogDescription(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  description = getDialogDescription(),\n  dialog = getDialog()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (description === null) return expect(description).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertHidden(description)\n\n        expect(description).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-describedby', description.id)\n\n        if (options.textContent) expect(description).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(description).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (description === null) return expect(description).not.toBe(null)\n        if (dialog === null) return expect(dialog).not.toBe(null)\n\n        assertVisible(description)\n\n        expect(description).toHaveAttribute('id')\n        expect(dialog).toHaveAttribute('aria-describedby', description.id)\n\n        if (options.textContent) expect(description).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(description).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(description).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialogDescription)\n    throw err\n  }\n}\n\nexport function assertDialogOverlay(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n    state: DialogState\n  },\n  overlay = getDialogOverlay()\n) {\n  try {\n    switch (options.state) {\n      case DialogState.InvisibleHidden:\n        if (overlay === null) return expect(overlay).not.toBe(null)\n\n        assertHidden(overlay)\n\n        if (options.textContent) expect(overlay).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(overlay).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.Visible:\n        if (overlay === null) return expect(overlay).not.toBe(null)\n\n        assertVisible(overlay)\n\n        if (options.textContent) expect(overlay).toHaveTextContent(options.textContent)\n\n        for (let attributeName in options.attributes) {\n          expect(overlay).toHaveAttribute(attributeName, options.attributes[attributeName])\n        }\n        break\n\n      case DialogState.InvisibleUnmounted:\n        expect(overlay).toBe(null)\n        break\n\n      default:\n        assertNever(options.state)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertDialogOverlay)\n    throw err\n  }\n}\n\n// ---\n\nexport function getRadioGroup(): HTMLElement | null {\n  return document.querySelector('[role=\"radiogroup\"]')\n}\n\nexport function getRadioGroupLabel(): HTMLElement | null {\n  return document.querySelector('[id^=\"headlessui-label-\"]')\n}\n\nexport function getRadioGroupOptions(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-radiogroup-option-\"]'))\n}\n\n// ---\n\nexport function assertRadioGroupLabel(\n  options: {\n    attributes?: Record<string, string | null>\n    textContent?: string\n  },\n  label = getRadioGroupLabel(),\n  radioGroup = getRadioGroup()\n) {\n  try {\n    if (label === null) return expect(label).not.toBe(null)\n    if (radioGroup === null) return expect(radioGroup).not.toBe(null)\n\n    expect(label).toHaveAttribute('id')\n    expect(radioGroup).toHaveAttribute('aria-labelledby', label.id)\n\n    if (options.textContent) expect(label).toHaveTextContent(options.textContent)\n\n    for (let attributeName in options.attributes) {\n      expect(label).toHaveAttribute(attributeName, options.attributes[attributeName])\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertRadioGroupLabel)\n    throw err\n  }\n}\n\n// ---\n\nexport function getTabList(): HTMLElement | null {\n  return document.querySelector('[role=\"tablist\"]')\n}\n\nexport function getTabs(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-tabs-tab-\"]'))\n}\n\nexport function getPanels(): HTMLElement[] {\n  return Array.from(document.querySelectorAll('[id^=\"headlessui-tabs-panel-\"]'))\n}\n\n// ---\n\nexport function assertTabs(\n  {\n    active,\n    orientation = 'horizontal',\n    tabContents = null,\n    panelContents = null,\n  }: {\n    active: number\n    orientation?: 'vertical' | 'horizontal'\n    tabContents?: string | null\n    panelContents?: string | null\n  },\n  list = getTabList(),\n  tabs = getTabs(),\n  panels = getPanels()\n) {\n  try {\n    if (list === null) return expect(list).not.toBe(null)\n\n    expect(list).toHaveAttribute('role', 'tablist')\n    expect(list).toHaveAttribute('aria-orientation', orientation)\n\n    let activeTab = Array.from(list.querySelectorAll('[id^=\"headlessui-tabs-tab-\"]'))[active]\n    let activePanel = panels.find((panel) => panel.id === activeTab.getAttribute('aria-controls'))\n\n    for (let tab of tabs) {\n      expect(tab).toHaveAttribute('id')\n      expect(tab).toHaveAttribute('role', 'tab')\n      expect(tab).toHaveAttribute('type', 'button')\n\n      if (tab === activeTab) {\n        expect(tab).toHaveAttribute('aria-selected', 'true')\n        expect(tab).toHaveAttribute('tabindex', '0')\n        if (tabContents !== null) {\n          expect(tab.textContent).toBe(tabContents)\n        }\n      } else {\n        expect(tab).toHaveAttribute('aria-selected', 'false')\n        expect(tab).toHaveAttribute('tabindex', '-1')\n      }\n\n      if (tab.hasAttribute('aria-controls')) {\n        let controlsId = tab.getAttribute('aria-controls')!\n        let panel = document.getElementById(controlsId)\n\n        expect(panel).not.toBe(null)\n        expect(panels).toContain(panel)\n        expect(panel).toHaveAttribute('aria-labelledby', tab.id)\n      }\n    }\n\n    for (let panel of panels) {\n      expect(panel).toHaveAttribute('id')\n      expect(panel).toHaveAttribute('role', 'tabpanel')\n\n      let controlledById = panel.getAttribute('aria-labelledby')!\n      let tab = document.getElementById(controlledById)\n\n      expect(tabs).toContain(tab)\n      expect(tab).toHaveAttribute('aria-controls', panel.id)\n\n      if (panel === activePanel) {\n        expect(panel).toHaveAttribute('tabindex', '0')\n        if (tabContents !== null) {\n          expect(panel.textContent).toBe(panelContents)\n        }\n      } else {\n        expect(panel).toHaveAttribute('tabindex', '-1')\n      }\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertTabs)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertActiveElement(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    try {\n      // Jest has a weird bug:\n      //   \"Cannot assign to read only property 'Symbol(impl)' of object '[object DOMImplementation]'\"\n      // when this assertion fails.\n      // Therefore we will catch it when something goes wrong, and just look at the outerHTML string.\n      expect(document.activeElement).toBe(element)\n    } catch (err) {\n      expect(document.activeElement?.outerHTML).toBe(element.outerHTML)\n    }\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertActiveElement)\n    throw err\n  }\n}\n\nexport function assertContainsActiveElement(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    expect(element.contains(document.activeElement)).toBe(true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertContainsActiveElement)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertHidden(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).toHaveAttribute('hidden')\n    expect(element).toHaveStyle({ display: 'none' })\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertHidden)\n    throw err\n  }\n}\n\nexport function assertVisible(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).not.toHaveAttribute('hidden')\n    expect(element).not.toHaveStyle({ display: 'none' })\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertVisible)\n    throw err\n  }\n}\n\n// ---\n\nexport function assertFocusable(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(isFocusableElement(element, FocusableMode.Strict)).toBe(true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertFocusable)\n    throw err\n  }\n}\n\nexport function assertNotFocusable(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(isFocusableElement(element, FocusableMode.Strict)).toBe(false)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotFocusable)\n    throw err\n  }\n}\n\nexport function assertInert(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    expect(element).toHaveAttribute('aria-hidden', 'true')\n    expect(element).toHaveProperty('inert', true)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertInert)\n    throw err\n  }\n}\n\nexport function assertNotInert(element: HTMLElement | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    // NOTE: We can't test that the element doesn't have `aria-hidden`, because this can still be\n    // the case even if they are not inert.\n    expect(element.inert).toBeUndefined()\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, assertNotInert)\n    throw err\n  }\n}\n\n// ---\n\nexport function getByText(text: string): HTMLElement | null {\n  let walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {\n    acceptNode(node: HTMLElement) {\n      if (node.children.length > 0) return NodeFilter.FILTER_SKIP\n      return NodeFilter.FILTER_ACCEPT\n    },\n  })\n\n  while (walker.nextNode()) {\n    if (walker.currentNode.textContent === text) return walker.currentNode as HTMLElement\n  }\n\n  return null\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/execute-timeline.ts",
    "content": "import snapshotDiff from 'snapshot-diff'\nimport { defineComponent } from 'vue'\nimport { disposables } from '../utils/disposables'\nimport { reportChanges } from './report-dom-node-changes'\nimport { render } from './vue-testing-library'\n\nfunction redentSnapshot(input: string) {\n  let minSpaces = Infinity\n  let lines = input.split('\\n')\n  for (let line of lines) {\n    if (line.trim() === '---') continue\n    let spacesInLine = (line.match(/^[+-](\\s+)/g) || []).pop()!.length - 1\n    minSpaces = Math.min(minSpaces, spacesInLine)\n  }\n\n  let replacer = new RegExp(`^([+-])\\\\s{${minSpaces}}(.*)`, 'g')\n\n  return input\n    .split('\\n')\n    .map((line) =>\n      line.trim() === '---' ? line : line.replace(replacer, (_, sign, rest) => `${sign}  ${rest}`)\n    )\n    .join('\\n')\n}\n\nexport async function executeTimeline(\n  element: ReturnType<typeof defineComponent>,\n  steps: ((tools: ReturnType<typeof render>) => (null | number)[])[]\n) {\n  let d = disposables()\n  let snapshots: { content: Node; recordedAt: bigint }[] = []\n\n  //\n  let tools = render(element)\n\n  // Start listening for changes\n  d.add(\n    reportChanges(\n      () => document.body.innerHTML,\n      () => {\n        // This will ensure that any DOM change to the body has been recorded.\n        snapshots.push({\n          content: tools.asFragment(),\n          recordedAt: process.hrtime.bigint(),\n        })\n      }\n    )\n  )\n\n  // We start with a `null` value because we will start with a snapshot even _before_ things start\n  // happening.\n  let timestamps: (null | number)[] = [null]\n\n  //\n  await steps.reduce(async (chain, step) => {\n    await chain\n\n    let durations = await step(tools)\n\n    // Note: The following calls are just in place to ensure that **we** waited long enough for the\n    // transitions to take place. This has no impact on the actual transitions. Above where the\n    // `reportDOMNodeChanges` is used we will actually record all the changes, no matter what\n    // happens here.\n\n    timestamps.push(...durations)\n\n    let totalDuration = durations\n      .filter((duration): duration is number => duration !== null)\n      .reduce((total, current) => total + current, 0)\n\n    // Changes happen in the next frame\n    await new Promise((resolve) => d.nextFrame(resolve))\n\n    // We wait for the amount of the duration\n    await new Promise((resolve) => d.setTimeout(resolve, totalDuration))\n\n    // We wait an additional next frame so that we know that we are done\n    await new Promise((resolve) => d.nextFrame(resolve))\n  }, Promise.resolve())\n\n  if (snapshots.length <= 0) {\n    throw new Error('We could not record any changes')\n  }\n\n  let uniqueSnapshots = snapshots\n    // Only keep the snapshots that are unique. Multiple snapshots of the same\n    // content are a bit useless for us.\n    .filter((snapshot, i) => {\n      if (i === 0) return true\n      return snapshot.content !== snapshots[i - 1].content\n    })\n\n    // Add a relative time compared to the previous snapshot. We recorded everything in\n    // process.hrtime.bigint() which is in nanoseconds, we want it in milliseconds.\n    .map((snapshot, i, all) => ({\n      ...snapshot,\n      relativeToPreviousSnapshot:\n        i === 0 ? 0 : Number((snapshot.recordedAt - all[i - 1].recordedAt) / BigInt(1e6)),\n    }))\n\n  let diffed = uniqueSnapshots\n    .map((call, i) => {\n      // Skip initial render, because there is nothing to compare with\n      if (i === 0) return false\n\n      // The next bit of code is a bit ugly, but mos of the code is just cleaning up some \"noise\"\n      // that we don't need in our test output.\n      return `Render ${i}:${\n        // `This took: ${call.relativeTime}ms`\n        timestamps[i] === null\n          ? ''\n          : ` Transition took at least ${timestamps[i]}ms (${\n              isWithinFrame(call.relativeToPreviousSnapshot, timestamps[i]!)\n                ? 'yes'\n                : `no, it took ${call.relativeToPreviousSnapshot}ms`\n            })`\n      }\\n${redentSnapshot(\n        snapshotDiff(uniqueSnapshots[i - 1].content, call.content, {\n          aAnnotation: '__REMOVE_ME__',\n          bAnnotation: '__REMOVE_ME__',\n          contextLines: 0,\n        })\n          // Just to do some cleanup\n          .replace(/\\n\\n@@([^@@]*)@@/g, '') // Top level @@ signs\n          .replace(/@@([^@@]*)@@/g, '---') // In between @@ signs\n          .replace(/[-+] __REMOVE_ME__\\n/g, '')\n          .replace(/Snapshot Diff:\\n/g, '')\n      )\n        .split('\\n')\n        .map((line) => `    ${line}`)\n        .join('\\n')}`\n    })\n    .filter(Boolean)\n    .join('\\n\\n')\n\n  d.dispose()\n\n  return diffed\n}\n\nexecuteTimeline.fullTransition = (duration: number) => {\n  return [\n    /** Stage 1: Immediately add `base` and `from` classes */\n    null,\n\n    /** Stage 2: Immediately remove `from` classes and add `to` classes */\n    null,\n\n    /** Stage 3: After duration remove `to` and `base` classes */\n    duration,\n  ]\n}\n\nlet state: {\n  before: number\n  fps: number\n  handle: ReturnType<typeof requestAnimationFrame> | null\n} = {\n  before: Date.now(),\n  fps: 0,\n  handle: null,\n}\n\nstate.handle = requestAnimationFrame(function loop() {\n  let now = Date.now()\n  state.fps = Math.round(1000 / (now - state.before))\n  state.before = now\n  state.handle = requestAnimationFrame(loop)\n})\n\nafterAll(() => {\n  if (state.handle) cancelAnimationFrame(state.handle)\n})\n\nfunction isWithinFrame(actual: number, expected: number) {\n  let buffer = state.fps\n\n  let min = expected - buffer\n  let max = expected + buffer\n\n  return actual >= min && actual <= max\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/fake-pointer.ts",
    "content": "export class FakePointer {\n  private x: number = 0\n  private y: number = 0\n\n  constructor(\n    private width: number,\n    private height: number\n  ) {\n    this.width = width\n    this.height = height\n  }\n\n  get options() {\n    return {\n      screenX: this.x,\n      screenY: this.y,\n    }\n  }\n\n  randomize() {\n    this.x = Math.floor(Math.random() * this.width)\n    this.y = Math.floor(Math.random() * this.height)\n  }\n\n  advance(amount: number = 1) {\n    this.x += amount\n\n    if (this.x >= this.width) {\n      this.x %= this.width\n      this.y++\n    }\n\n    if (this.y >= this.height) {\n      this.y %= this.height\n    }\n  }\n\n  /**\n   * JSDOM does not support pointer events.\n   * Because of this when we try to set the pointer position it returns undefined so our checks fail.\n   *\n   * This runs the callback with the TEST_IGNORE_TRACKED_POINTER environment variable set to 1 so we bypass the checks.\n   */\n  bypassingTrackingChecks(callback: () => void) {\n    let original = process.env.TEST_BYPASS_TRACKED_POINTER\n    process.env.TEST_BYPASS_TRACKED_POINTER = '1'\n    callback()\n    process.env.TEST_BYPASS_TRACKED_POINTER = original\n  }\n}\n\n/**\n * A global pointer for use in pointer and mouse event checks\n */\nexport let pointer = new FakePointer(1920, 1080)\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/html.ts",
    "content": "export function jsx(templates: TemplateStringsArray) {\n  return templates.join('')\n}\n\nexport function html(templates: TemplateStringsArray) {\n  return templates.join('')\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/interactions.test.ts",
    "content": "import { defineComponent, h, type ComponentOptionsWithoutProps } from 'vue'\nimport { Keys, shift, type } from './interactions'\nimport { render } from './vue-testing-library'\n\ntype Events = 'onKeydown' | 'onKeyup' | 'onKeypress' | 'onClick' | 'onBlur' | 'onFocus'\nlet events: Events[] = ['onKeydown', 'onKeyup', 'onKeypress', 'onClick', 'onBlur', 'onFocus']\n\nfunction renderTemplate(input: string | ComponentOptionsWithoutProps) {\n  let defaultComponents = {}\n\n  if (typeof input === 'string') {\n    return render(defineComponent({ template: input, components: defaultComponents }))\n  }\n\n  return render(\n    defineComponent(\n      Object.assign({}, input, {\n        components: { ...defaultComponents, ...input.components },\n      }) as Parameters<typeof defineComponent>[0]\n    )\n  )\n}\n\ntype Args = [\n  string | Partial<KeyboardEvent>,\n  (string | Partial<KeyboardEvent | MouseEvent>)[],\n  Set<Events>,\n]\n\nfunction key(input: string | Partial<KeyboardEvent>): Partial<KeyboardEvent> {\n  if (typeof input === 'string') return { key: input }\n  return input\n}\n\nfunction event(\n  input: string | Partial<KeyboardEvent | MouseEvent>,\n  target?: string\n): Partial<KeyboardEvent | MouseEvent> {\n  let e = typeof input === 'string' ? { type: input } : input\n\n  if (target) {\n    Object.defineProperty(e, 'target', {\n      configurable: false,\n      enumerable: true,\n      get() {\n        return document.getElementById(target!)\n      },\n    })\n  }\n\n  return e\n}\n\ndescribe('Keyboard', () => {\n  describe('type', () => {\n    it.each<Args>([\n      // Default - no cancellation\n      ['a', ['keydown', 'keypress', 'keyup'], new Set()],\n      [Keys.Space, ['keydown', 'keypress', 'keyup', 'click'], new Set()],\n      [Keys.Enter, ['keydown', 'keypress', 'click', 'keyup'], new Set()],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set(),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set(),\n      ],\n\n      // Canceling keydown\n      ['a', ['keydown', 'keyup'], new Set<Events>(['onKeydown'])],\n      [Keys.Space, ['keydown', 'keyup'], new Set<Events>(['onKeydown'])],\n      [Keys.Enter, ['keydown', 'keyup'], new Set<Events>(['onKeydown'])],\n      [Keys.Tab, ['keydown', 'keyup'], new Set<Events>(['onKeydown'])],\n      [shift(Keys.Tab), ['keydown', 'keyup'], new Set<Events>(['onKeydown'])],\n\n      // Canceling keypress\n      ['a', ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeypress'])],\n      [Keys.Space, ['keydown', 'keypress', 'keyup', 'click'], new Set<Events>(['onKeypress'])],\n      [Keys.Enter, ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeypress'])],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onKeypress']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onKeypress']),\n      ],\n\n      // Canceling keyup\n      ['a', ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyup'])],\n      [Keys.Space, ['keydown', 'keypress', 'keyup'], new Set<Events>(['onKeyup'])],\n      [Keys.Enter, ['keydown', 'keypress', 'click', 'keyup'], new Set<Events>(['onKeyup'])],\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onKeyup']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onKeyup']),\n      ],\n\n      // Cancelling blur\n      [\n        Keys.Tab,\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'after'),\n          event('keyup', 'after'),\n        ],\n        new Set<Events>(['onBlur']),\n      ],\n      [\n        shift(Keys.Tab),\n        [\n          event('keydown', 'trigger'),\n          event('blur', 'trigger'),\n          event('focus', 'before'),\n          event('keyup', 'before'),\n        ],\n        new Set<Events>(['onBlur']),\n      ],\n    ])('should fire the correct events %#', async (input, result, prevents) => {\n      let fired: (KeyboardEvent | MouseEvent)[] = []\n\n      let state = { readyToCapture: false }\n\n      function createProps(id: string) {\n        return events.reduce<Record<string, string | ((event: any) => void)>>(\n          (props, name) => {\n            props[name] = (event: any) => {\n              if (!state.readyToCapture) return\n              if (prevents.has(name)) event.preventDefault()\n              fired.push(event)\n            }\n            return props\n          },\n          { id }\n        )\n      }\n\n      renderTemplate({\n        template: `\n          <div>\n            <Button id=\"before\">Before</Button>\n            <Button id=\"trigger\">Trigger</Button>\n            <Button id=\"after\">After</Button>\n          </div>\n        `,\n        components: {\n          Button: defineComponent({\n            setup(_props, { slots, attrs }) {\n              return () => {\n                return h('button', createProps(attrs.id as string), slots.default!())\n              }\n            },\n          }),\n        },\n      })\n\n      let trigger = document.getElementById('trigger')\n      trigger?.focus()\n      state.readyToCapture = true\n\n      await type([key(input)])\n\n      let expected = result.map((e) => event(e))\n\n      expect(fired.length).toEqual(result.length)\n\n      for (let [idx, event] of fired.entries()) {\n        for (let key in expected[idx]) {\n          let _key = key as keyof (KeyboardEvent | MouseEvent)\n          expect(event[_key]).toBe(expected[idx][_key])\n        }\n      }\n    })\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/interactions.ts",
    "content": "import { fireEvent } from '@testing-library/dom'\nimport { pointer } from './fake-pointer'\n\nfunction nextFrame(cb: Function): void {\n  setImmediate(() =>\n    setImmediate(() => {\n      cb()\n    })\n  )\n}\n\nexport let Keys: Record<string, Partial<KeyboardEvent>> = {\n  Space: { key: ' ', keyCode: 32, charCode: 32 },\n  Enter: { key: 'Enter', keyCode: 13, charCode: 13 },\n  Escape: { key: 'Escape', keyCode: 27, charCode: 27 },\n  Backspace: { key: 'Backspace', keyCode: 8 },\n\n  ArrowLeft: { key: 'ArrowLeft', keyCode: 37 },\n  ArrowUp: { key: 'ArrowUp', keyCode: 38 },\n  ArrowRight: { key: 'ArrowRight', keyCode: 39 },\n  ArrowDown: { key: 'ArrowDown', keyCode: 40 },\n\n  Home: { key: 'Home', keyCode: 36 },\n  End: { key: 'End', keyCode: 35 },\n\n  PageUp: { key: 'PageUp', keyCode: 33 },\n  PageDown: { key: 'PageDown', keyCode: 34 },\n\n  Tab: { key: 'Tab', keyCode: 9, charCode: 9 },\n}\n\nexport function shift(event: Partial<KeyboardEvent>) {\n  return { ...event, shiftKey: true }\n}\n\nexport function word(input: string): Partial<KeyboardEvent>[] {\n  let result = input.split('').map((key) => ({ key }))\n\n  let element = document.activeElement\n\n  if (element instanceof HTMLInputElement) {\n    fireEvent.change(element, {\n      target: Object.assign({}, element, { value: input }),\n    })\n  }\n\n  return result\n}\n\nlet Default = Symbol()\nlet Ignore = Symbol()\n\nlet cancellations: Record<string | typeof Default, Record<string, Set<string>>> = {\n  [Default]: {\n    keydown: new Set(['keypress']),\n    keypress: new Set([]),\n    keyup: new Set([]),\n  },\n  [Keys.Enter.key!]: {\n    keydown: new Set(['keypress', 'click']),\n    keypress: new Set(['click']),\n    keyup: new Set([]),\n  },\n  [Keys.Space.key!]: {\n    keydown: new Set(['keypress', 'click']),\n    keypress: new Set([]),\n    keyup: new Set(['click']),\n  },\n  [Keys.Tab.key!]: {\n    keydown: new Set(['keypress', 'blur', 'focus']),\n    keypress: new Set([]),\n    keyup: new Set([]),\n  },\n}\n\nlet order: Record<\n  string | typeof Default,\n  ((\n    element: Element,\n    event: Partial<KeyboardEvent | MouseEvent>\n  ) => boolean | typeof Ignore | Element)[]\n> = {\n  [Default]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function input(element, event) {\n      // TODO: This should only fire when the element's value changes\n      return fireEvent.input(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Enter.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function click(element, event) {\n      if (element instanceof HTMLButtonElement) return fireEvent.click(element, event)\n      return Ignore\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Space.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n    function click(element, event) {\n      if (element instanceof HTMLButtonElement) return fireEvent.click(element, event)\n      return Ignore\n    },\n  ],\n  [Keys.Tab.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function blurAndfocus(_element, event) {\n      return focusNext(event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Escape.key!]: [\n    function keydown(element, event) {\n      return fireEvent.keyDown(element, event)\n    },\n    function keypress(element, event) {\n      return fireEvent.keyPress(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n  [Keys.Backspace.key!]: [\n    function keydown(element, event) {\n      if (element instanceof HTMLInputElement) {\n        let ev = Object.assign({}, event, {\n          target: Object.assign({}, event.target, {\n            value: element.value.slice(0, -1),\n          }),\n        })\n\n        fireEvent.keyDown(element, ev)\n        return fireEvent.input(element, ev)\n      }\n\n      return fireEvent.keyDown(element, event)\n    },\n    function keyup(element, event) {\n      return fireEvent.keyUp(element, event)\n    },\n  ],\n}\n\nexport async function type(events: Partial<KeyboardEvent>[], element = document.activeElement) {\n  jest.useFakeTimers()\n\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    for (let event of events) {\n      let skip = new Set()\n      let actions = order[event.key!] ?? order[Default as any]\n      for (let action of actions) {\n        let checks = action.name.split('And')\n        if (checks.some((check) => skip.has(check))) continue\n\n        let result = action(element, {\n          type: action.name,\n          charCode: event.key?.length === 1 ? event.key?.charCodeAt(0) : undefined,\n          ...event,\n        })\n        if (result === Ignore) continue\n        if (result instanceof Element) {\n          element = result\n        }\n\n        let cancelled = !result\n        if (cancelled) {\n          let skippablesForKey = cancellations[event.key!] ?? cancellations[Default as any]\n          let skippables = skippablesForKey?.[action.name] ?? new Set()\n\n          for (let skippable of skippables) skip.add(skippable)\n        }\n      }\n    }\n\n    // We don't want to actually wait in our tests, so let's advance\n    jest.runAllTimers()\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, type)\n    throw err\n  } finally {\n    jest.useRealTimers()\n  }\n}\n\nexport async function press(event: Partial<KeyboardEvent>, element = document.activeElement) {\n  return type([event], element)\n}\n\nexport enum MouseButton {\n  Left = 0,\n  Right = 2,\n}\n\nexport async function click(\n  element: Document | Element | Window | Node | null,\n  button = MouseButton.Left\n) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n    if (element instanceof HTMLButtonElement && element.disabled) return\n\n    let options = { button }\n\n    if (button === MouseButton.Left) {\n      // Cancel in pointerDown cancels mouseDown, mouseUp\n      let cancelled = !fireEvent.pointerDown(element, options)\n      if (!cancelled) {\n        cancelled = !fireEvent.mouseDown(element, options)\n      }\n\n      // Ensure to trigger a `focus` event if the element is focusable, or within a focusable element\n      if (!cancelled) {\n        let next: HTMLElement | null = element as HTMLElement | null\n        while (next !== null) {\n          if (next.matches(focusableSelector)) {\n            next.focus()\n            break\n          }\n          next = next.parentElement\n        }\n      }\n\n      fireEvent.pointerUp(element, options)\n      if (!cancelled) {\n        fireEvent.mouseUp(element, options)\n      }\n      fireEvent.click(element, options)\n    } else if (button === MouseButton.Right) {\n      // Cancel in pointerDown cancels mouseDown, mouseUp\n      let cancelled = !fireEvent.pointerDown(element, options)\n      if (!cancelled) {\n        fireEvent.mouseDown(element, options)\n      }\n\n      // Only in Firefox:\n      fireEvent.pointerUp(element, options)\n      if (!cancelled) {\n        fireEvent.mouseUp(element, options)\n      }\n    }\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, click)\n    throw err\n  }\n}\n\nexport async function focus(element: Document | Element | Window | Node | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    if (element instanceof HTMLElement) {\n      element.focus()\n    } else {\n      fireEvent.focus(element)\n    }\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, focus)\n    throw err\n  }\n}\n\nexport async function blur(element: Document | Element | Window | Node | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    if (element instanceof HTMLElement) {\n      element.blur()\n    } else {\n      fireEvent.blur(element)\n    }\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, blur)\n    throw err\n  }\n}\n\nexport async function mouseEnter(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    pointer.randomize()\n\n    fireEvent.pointerOver(element, pointer.options)\n    fireEvent.pointerEnter(element, pointer.options)\n    fireEvent.mouseOver(element, pointer.options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseEnter)\n    throw err\n  }\n}\n\nexport async function mouseMove(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    pointer.advance()\n\n    pointer.bypassingTrackingChecks(() => {\n      fireEvent.pointerMove(element)\n    })\n\n    fireEvent.mouseMove(element, pointer.options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseMove)\n    throw err\n  }\n}\n\nexport async function mouseLeave(element: Document | Element | Window | null) {\n  try {\n    if (element === null) return expect(element).not.toBe(null)\n\n    pointer.advance()\n\n    pointer.bypassingTrackingChecks(() => {\n      fireEvent.pointerOut(element)\n      fireEvent.pointerLeave(element)\n    })\n\n    fireEvent.mouseOut(element, pointer.options)\n    fireEvent.mouseLeave(element, pointer.options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, mouseLeave)\n    throw err\n  }\n}\n\nexport async function mouseDrag(\n  startingElement: Document | Element | Window | Node | null,\n  endingElement: Document | Element | Window | Node | null\n) {\n  let button = MouseButton.Left\n\n  try {\n    if (startingElement === null) return expect(startingElement).not.toBe(null)\n    if (endingElement === null) return expect(endingElement).not.toBe(null)\n    if (startingElement instanceof HTMLButtonElement && startingElement.disabled) return\n\n    let options = { button }\n\n    // Cancel in pointerDown cancels mouseDown, mouseUp\n    let cancelled = !fireEvent.pointerDown(startingElement, options)\n\n    if (!cancelled) {\n      cancelled = !fireEvent.mouseDown(startingElement, options)\n    }\n\n    // Ensure to trigger a `focus` event if the element is focusable, or within a focusable element\n    if (!cancelled) {\n      let next: HTMLElement | null = startingElement as HTMLElement | null\n      while (next !== null) {\n        if (next.matches(focusableSelector)) {\n          next.focus()\n          break\n        }\n        next = next.parentElement\n      }\n    }\n\n    fireEvent.pointerMove(startingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseMove(startingElement, options)\n    }\n\n    fireEvent.pointerOut(startingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseOut(startingElement, options)\n    }\n\n    // crosses over to the ending element\n\n    fireEvent.pointerOver(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseOver(endingElement, options)\n    }\n\n    fireEvent.pointerMove(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseMove(endingElement, options)\n    }\n\n    fireEvent.pointerUp(endingElement, options)\n    if (!cancelled) {\n      fireEvent.mouseUp(endingElement, options)\n    }\n\n    fireEvent.click(endingElement, options)\n\n    await new Promise(nextFrame)\n  } catch (err) {\n    if (err instanceof Error) Error.captureStackTrace(err, click)\n    throw err\n  }\n}\n\n// ---\n\nfunction focusNext(event: Partial<KeyboardEvent>) {\n  let direction = event.shiftKey ? -1 : +1\n  let focusableElements = getFocusableElements()\n  let total = focusableElements.length\n\n  function innerFocusNext(offset = 0): Element {\n    let currentIdx = focusableElements.indexOf(document.activeElement as HTMLElement)\n    let next = focusableElements[(currentIdx + total + direction + offset) % total] as HTMLElement\n\n    if (next) next?.focus({ preventScroll: true })\n\n    if (next !== document.activeElement) return innerFocusNext(offset + direction)\n    return next\n  }\n\n  return innerFocusNext()\n}\n\n// Credit:\n//  - https://stackoverflow.com/a/30753870\nlet focusableSelector = [\n  '[contentEditable=true]',\n  '[tabindex]',\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'iframe',\n  'input:not([disabled])',\n  'select:not([disabled])',\n  // TODO: Re-enable once we bump JSDOM\n  // 'details:not(:has(> summary))',\n  'details>summary',\n  'textarea:not([disabled])',\n]\n  .map(\n    process.env.NODE_ENV === 'test'\n      ? // TODO: Remove this once JSDOM fixes the issue where an element that is\n        // \"hidden\" can be the document.activeElement, because this is not possible\n        // in real browsers.\n        (selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`\n      : (selector) => `${selector}:not([tabindex='-1'])`\n  )\n  .join(',')\n\nfunction getFocusableElements(container = document.body) {\n  if (!container) return []\n  return Array.from(container.querySelectorAll(focusableSelector))\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/report-dom-node-changes.ts",
    "content": "import { disposables } from '../utils/disposables'\n\nexport function reportChanges<TType>(key: () => TType, onChange: (value: TType) => void) {\n  let d = disposables()\n\n  let previous: TType\n\n  function track() {\n    let next = key()\n    if (previous !== next) {\n      previous = next\n      onChange(next)\n    }\n    d.requestAnimationFrame(track)\n  }\n\n  track()\n\n  return d.dispose\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/ssr.ts",
    "content": "import { createApp, createSSRApp, nextTick } from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { env } from '../utils/env'\n\nexport async function renderSSR(component: any, rootProps: any = {}) {\n  let container = document.createElement('div')\n  document.body.appendChild(container)\n\n  // Render on the server\n  env.set('server')\n  let app = createSSRApp(component, rootProps)\n  let contents = await renderToString(app)\n  container.innerHTML = contents\n\n  return {\n    contents,\n    async hydrate() {\n      let app = createApp(component, rootProps)\n      app.mount(container)\n\n      await nextTick()\n\n      return {\n        contents: container.innerHTML,\n      }\n    },\n  }\n}\n\nexport async function renderHydrate(component: any, rootProps: any = {}) {\n  return renderSSR(component, rootProps).then(({ hydrate }) => hydrate())\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/suppress-console-logs.ts",
    "content": "type FunctionPropertyNames<T> = {\n  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never\n}[keyof T] &\n  string\n\nexport function suppressConsoleLogs<T extends unknown[]>(\n  cb: (...args: T) => void,\n  type: FunctionPropertyNames<typeof globalThis.console> = 'warn'\n) {\n  return (...args: T) => {\n    let spy = jest.spyOn(globalThis.console, type).mockImplementation(jest.fn())\n\n    return new Promise<void>((resolve, reject) => {\n      Promise.resolve(cb(...args)).then(resolve, reject)\n    }).finally(() => spy.mockRestore())\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/test-utils/vue-testing-library.ts",
    "content": "import { fireEvent, logDOM, screen } from '@testing-library/dom'\nimport { mount } from '@vue/test-utils'\nimport { defineComponent, type ComponentOptionsWithoutProps } from 'vue'\n\nlet mountedWrappers = new Set()\n\nfunction resolveContainer(): HTMLElement {\n  let div = document.createElement('div')\n  let baseElement = document.body\n  let container = baseElement.appendChild(div)\n\n  let attachTo = document.createElement('div')\n  container.appendChild(attachTo)\n  return attachTo\n}\n\n// It's not the most elegant type\n// but Props and Emits need to be typed as any and not `{}`\ntype AnyComponent = ReturnType<typeof defineComponent>\n\nexport function createRenderTemplate(defaultComponents: Record<string, AnyComponent>) {\n  return (input: string | ComponentOptionsWithoutProps) => {\n    if (typeof input === 'string') {\n      input = { template: input }\n    }\n\n    let component: ComponentOptionsWithoutProps = Object.assign({}, input, {\n      components: { ...defaultComponents, ...input.components },\n    })\n\n    return render(defineComponent(component))\n  }\n}\n\nexport function render(TestComponent: any, options?: Parameters<typeof mount>[1] | undefined) {\n  let wrapper = mount(TestComponent, {\n    ...options,\n    attachTo: options?.attachTo ?? resolveContainer(),\n  })\n\n  mountedWrappers.add(wrapper)\n\n  return {\n    unmount() {\n      wrapper.unmount()\n    },\n    get container() {\n      return wrapper.element.parentElement!\n    },\n    debug(element = wrapper.element.parentElement!) {\n      logDOM(element)\n    },\n    asFragment() {\n      let template = document.createElement('template')\n      template.innerHTML = wrapper.element.parentElement!.innerHTML\n      return template.content\n    },\n  }\n}\n\nfunction cleanup() {\n  mountedWrappers.forEach(cleanupAtWrapper)\n  document.body.innerHTML = ''\n}\n\nfunction cleanupAtWrapper(wrapper: any) {\n  if (wrapper.element.parentNode && wrapper.element.parentNode.parentNode === document.body) {\n    document.body.removeChild(wrapper.element.parentNode)\n  }\n\n  try {\n    wrapper.unmount()\n  } catch {\n  } finally {\n    mountedWrappers.delete(wrapper)\n  }\n}\n\nif (typeof afterEach === 'function') {\n  afterEach(() => cleanup())\n}\n\nexport { fireEvent, screen }\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/active-element-history.ts",
    "content": "import { onDocumentReady } from './document-ready'\nimport { focusableSelector } from './focus-management'\n\nexport let history: HTMLElement[] = []\nonDocumentReady(() => {\n  function handle(e: Event) {\n    if (!(e.target instanceof HTMLElement)) return\n    if (e.target === document.body) return\n    if (history[0] === e.target) return\n\n    let focusableElement = e.target as HTMLElement\n\n    // Figure out the closest focusable element, this is needed in a situation\n    // where you click on a non-focusable element inside a focusable element.\n    //\n    // E.g.:\n    //\n    // ```html\n    // <button>\n    //   <span>Click me</span>\n    // </button>\n    // ```\n    focusableElement = focusableElement.closest(focusableSelector) as HTMLElement\n\n    history.unshift(focusableElement ?? e.target)\n\n    // Filter out DOM Nodes that don't exist anymore\n    history = history.filter((x) => x != null && x.isConnected)\n    history.splice(10) // Only keep the 10 most recent items\n  }\n\n  window.addEventListener('click', handle, { capture: true })\n  window.addEventListener('mousedown', handle, { capture: true })\n  window.addEventListener('focus', handle, { capture: true })\n\n  document.body.addEventListener('click', handle, { capture: true })\n  document.body.addEventListener('mousedown', handle, { capture: true })\n  document.body.addEventListener('focus', handle, { capture: true })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/calculate-active-index.ts",
    "content": "function assertNever(x: never): never {\n  throw new Error('Unexpected object: ' + x)\n}\n\nexport enum Focus {\n  /** Focus the first non-disabled item. */\n  First,\n\n  /** Focus the previous non-disabled item. */\n  Previous,\n\n  /** Focus the next non-disabled item. */\n  Next,\n\n  /** Focus the last non-disabled item. */\n  Last,\n\n  /** Focus a specific item based on the `id` of the item. */\n  Specific,\n\n  /** Focus no items at all. */\n  Nothing,\n}\n\nexport function calculateActiveIndex<TItem>(\n  action: { focus: Focus.Specific; id: string } | { focus: Exclude<Focus, Focus.Specific> },\n  resolvers: {\n    resolveItems(): TItem[]\n    resolveActiveIndex(): number | null\n    resolveId(item: TItem, index: number, items: TItem[]): string\n    resolveDisabled(item: TItem, index: number, items: TItem[]): boolean\n  }\n) {\n  let items = resolvers.resolveItems()\n  if (items.length <= 0) return null\n\n  let currentActiveIndex = resolvers.resolveActiveIndex()\n  let activeIndex = currentActiveIndex ?? -1\n\n  switch (action.focus) {\n    case Focus.First: {\n      for (let i = 0; i < items.length; ++i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Previous: {\n      // If nothing is active, focus the last relevant item\n      if (activeIndex === -1) activeIndex = items.length\n\n      for (let i = activeIndex - 1; i >= 0; --i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Next: {\n      for (let i = activeIndex + 1; i < items.length; ++i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Last: {\n      for (let i = items.length - 1; i >= 0; --i) {\n        if (!resolvers.resolveDisabled(items[i], i, items)) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Specific: {\n      for (let i = 0; i < items.length; ++i) {\n        if (resolvers.resolveId(items[i], i, items) === action.id) {\n          return i\n        }\n      }\n      return currentActiveIndex\n    }\n\n    case Focus.Nothing:\n      return null\n\n    default:\n      assertNever(action)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/disposables.ts",
    "content": "import { microTask } from './micro-task'\n\nexport type Disposables = ReturnType<typeof disposables>\n\nexport function disposables() {\n  let _disposables: Function[] = []\n\n  let api = {\n    addEventListener<TEventName extends keyof WindowEventMap>(\n      element: HTMLElement | Window | Document,\n      name: TEventName,\n      listener: (event: WindowEventMap[TEventName]) => any,\n      options?: boolean | AddEventListenerOptions\n    ) {\n      element.addEventListener(name, listener as any, options)\n      return api.add(() => element.removeEventListener(name, listener as any, options))\n    },\n\n    requestAnimationFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      let raf = requestAnimationFrame(...args)\n      api.add(() => cancelAnimationFrame(raf))\n    },\n\n    nextFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      api.requestAnimationFrame(() => {\n        api.requestAnimationFrame(...args)\n      })\n    },\n\n    setTimeout(...args: Parameters<typeof setTimeout>) {\n      let timer = setTimeout(...args)\n      api.add(() => clearTimeout(timer))\n    },\n\n    microTask(...args: Parameters<typeof microTask>) {\n      let task = { current: true }\n      microTask(() => {\n        if (task.current) {\n          args[0]()\n        }\n      })\n      return api.add(() => {\n        task.current = false\n      })\n    },\n\n    style(node: HTMLElement, property: string, value: string) {\n      let previous = node.style.getPropertyValue(property)\n      Object.assign(node.style, { [property]: value })\n      return this.add(() => {\n        Object.assign(node.style, { [property]: previous })\n      })\n    },\n\n    group(cb: (d: typeof this) => void) {\n      let d = disposables()\n      cb(d)\n      return this.add(() => d.dispose())\n    },\n\n    add(cb: () => void) {\n      _disposables.push(cb)\n      return () => {\n        let idx = _disposables.indexOf(cb)\n        if (idx >= 0) {\n          for (let dispose of _disposables.splice(idx, 1)) {\n            dispose()\n          }\n        }\n      }\n    },\n\n    dispose() {\n      for (let dispose of _disposables.splice(0)) {\n        dispose()\n      }\n    },\n  }\n\n  return api\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/document-ready.ts",
    "content": "export function onDocumentReady(cb: () => void) {\n  function check() {\n    if (document.readyState === 'loading') return\n    cb()\n    document.removeEventListener('DOMContentLoaded', check)\n  }\n\n  if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n    document.addEventListener('DOMContentLoaded', check)\n    check()\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/dom.ts",
    "content": "import type { ComponentPublicInstance, Ref } from 'vue'\n\ntype AsElement<T extends HTMLElement | ComponentPublicInstance> =\n  | (T extends HTMLElement ? T : HTMLElement)\n  | null\n\nexport function dom<T extends HTMLElement | ComponentPublicInstance>(\n  ref?: Ref<T | null>\n): AsElement<T> | null {\n  if (ref == null) return null\n  if (ref.value == null) return null\n\n  let el = (ref.value as { $el?: T }).$el ?? ref.value\n\n  // In this case we check for `Node` because returning `null` from a\n  // component renders a `Comment` which is a `Node` but not `Element`\n  // The types don't encode this possibility but we handle it here at runtime\n  if (el instanceof Node) {\n    return el as AsElement<T>\n  }\n\n  return null\n}\n\n// Source: https://github.com/tailwindlabs/headlessui/blob/2de2779a1e3a5a02c1684e611daf5a68e8002143/packages/%40headlessui-react/src/utils/dom.ts\n// Normally you can use `element instanceof HTMLElement`, but if you are in\n// different JS Context (e.g.: inside an iframe) then the `HTMLElement` will be\n// a different class and the check will fail.\n//\n// Instead, we will check for certain properties to determine if the element\n// is of a specific type.\n\nexport function isNode(element: unknown): element is Node {\n  if (typeof element !== 'object') return false\n  if (element === null) return false\n  return 'nodeType' in element\n}\n\nexport function isElement(element: unknown): element is Element {\n  return isNode(element) && 'tagName' in element\n}\n\nexport function isHTMLElement(element: unknown): element is HTMLElement {\n  return isElement(element) && 'accessKey' in element\n}\n\n// HTMLOrSVGElement doesn't inherit from HTMLElement or from Element. But this\n// is the type that contains the `tabIndex` property.\n//\n// Once we know that this is an `HTMLOrSVGElement` we also know that it is an\n// `Element` (that contains more information)\nexport function isHTMLorSVGElement(element: unknown): element is HTMLOrSVGElement & Element {\n  return isElement(element) && 'tabIndex' in element\n}\n\nexport function isHTMLIframeElement(element: unknown): element is HTMLIFrameElement {\n  return isHTMLElement(element) && element.nodeName === 'IFRAME'\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/env.ts",
    "content": "type RenderEnv = 'client' | 'server'\n\nclass Env {\n  current: RenderEnv = this.detect()\n  currentId = 0\n\n  set(env: RenderEnv): void {\n    if (this.current === env) return\n\n    this.currentId = 0\n    this.current = env\n  }\n\n  reset(): void {\n    this.set(this.detect())\n  }\n\n  nextId() {\n    return ++this.currentId\n  }\n\n  get isServer(): boolean {\n    return this.current === 'server'\n  }\n\n  get isClient(): boolean {\n    return this.current === 'client'\n  }\n\n  private detect(): RenderEnv {\n    if (typeof window === 'undefined' || typeof document === 'undefined') {\n      return 'server'\n    }\n\n    return 'client'\n  }\n}\n\nexport let env = new Env()\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/focus-management.ts",
    "content": "import { nextTick } from 'vue'\nimport { match } from './match'\nimport { getOwnerDocument } from './owner'\n\n// Credit:\n//  - https://stackoverflow.com/a/30753870\nexport let focusableSelector = [\n  '[contentEditable=true]',\n  '[tabindex]',\n  'a[href]',\n  'area[href]',\n  'button:not([disabled])',\n  'iframe',\n  'input:not([disabled])',\n  'select:not([disabled])',\n  // TODO: Re-enable once we bump JSDOM\n  // 'details:not(:has(> summary))',\n  'details>summary',\n  'textarea:not([disabled])',\n]\n  .map(\n    process.env.NODE_ENV === 'test'\n      ? // TODO: Remove this once JSDOM fixes the issue where an element that is\n        // \"hidden\" can be the document.activeElement, because this is not possible\n        // in real browsers.\n        (selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`\n      : (selector) => `${selector}:not([tabindex='-1'])`\n  )\n  .join(',')\n\nexport enum Focus {\n  /** Focus the first non-disabled element */\n  First = 1 << 0,\n\n  /** Focus the previous non-disabled element */\n  Previous = 1 << 1,\n\n  /** Focus the next non-disabled element */\n  Next = 1 << 2,\n\n  /** Focus the last non-disabled element */\n  Last = 1 << 3,\n\n  /** Wrap tab around */\n  WrapAround = 1 << 4,\n\n  /** Prevent scrolling the focusable elements into view */\n  NoScroll = 1 << 5,\n}\n\nexport enum FocusResult {\n  Error,\n  Overflow,\n  Success,\n  Underflow,\n}\n\nenum Direction {\n  Previous = -1,\n  Next = 1,\n}\n\nexport function getFocusableElements(container: HTMLElement | null = document.body) {\n  if (container == null) return []\n  return Array.from(container.querySelectorAll<HTMLElement>(focusableSelector)).sort(\n    // We want to move `:tabindex=\"0\"` to the end of the list, this is what the browser does as well.\n    (a, z) =>\n      Math.sign((a.tabIndex || Number.MAX_SAFE_INTEGER) - (z.tabIndex || Number.MAX_SAFE_INTEGER))\n  )\n}\n\nexport enum FocusableMode {\n  /** The element itself must be focusable. */\n  Strict,\n\n  /** The element should be inside of a focusable element. */\n  Loose,\n}\n\nexport function isFocusableElement(\n  element: HTMLOrSVGElement & Element,\n  mode: FocusableMode = FocusableMode.Strict\n) {\n  if (element === getOwnerDocument(element)?.body) return false\n\n  return match(mode, {\n    [FocusableMode.Strict]() {\n      return element.matches(focusableSelector)\n    },\n    [FocusableMode.Loose]() {\n      let next: Element | null = element\n\n      while (next !== null) {\n        if (next.matches(focusableSelector)) return true\n        next = next.parentElement\n      }\n\n      return false\n    },\n  })\n}\n\nexport function restoreFocusIfNecessary(element: HTMLElement | null) {\n  let ownerDocument = getOwnerDocument(element)\n  nextTick(() => {\n    if (\n      ownerDocument &&\n      !isFocusableElement(ownerDocument.activeElement as HTMLElement, FocusableMode.Strict)\n    ) {\n      focusElement(element)\n    }\n  })\n}\n\n// The method of triggering an action, this is used to determine how we should\n// restore focus after an action has been performed.\nenum ActivationMethod {\n  /* If the action was triggered by a keyboard event. */\n  Keyboard = 0,\n\n  /* If the action was triggered by a mouse / pointer / ... event.*/\n  Mouse = 1,\n}\n\n// We want to be able to set and remove the `data-headlessui-mouse` attribute on the `html` element.\nif (typeof window !== 'undefined' && typeof document !== 'undefined') {\n  document.addEventListener(\n    'keydown',\n    (event) => {\n      if (event.metaKey || event.altKey || event.ctrlKey) {\n        return\n      }\n\n      document.documentElement.dataset.headlessuiFocusVisible = ''\n    },\n    true\n  )\n\n  document.addEventListener(\n    'click',\n    (event) => {\n      // Event originated from an actual mouse click\n      if (event.detail === ActivationMethod.Mouse) {\n        delete document.documentElement.dataset.headlessuiFocusVisible\n      }\n\n      // Event originated from a keyboard event that triggered the `click` event\n      else if (event.detail === ActivationMethod.Keyboard) {\n        document.documentElement.dataset.headlessuiFocusVisible = ''\n      }\n    },\n    true\n  )\n}\n\nexport function focusElement(element: HTMLElement | null) {\n  element?.focus({ preventScroll: true })\n}\n\n// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select\nlet selectableSelector = ['textarea', 'input'].join(',')\nfunction isSelectableElement(\n  element: Element | null\n): element is HTMLInputElement | HTMLTextAreaElement {\n  return element?.matches?.(selectableSelector) ?? false\n}\n\nexport function sortByDomNode<T>(\n  nodes: T[],\n  resolveKey: (item: T) => HTMLElement | null = (i) => i as unknown as HTMLElement | null\n): T[] {\n  return nodes.slice().sort((aItem, zItem) => {\n    let a = resolveKey(aItem)\n    let z = resolveKey(zItem)\n\n    if (a === null || z === null) return 0\n\n    let position = a.compareDocumentPosition(z)\n\n    if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1\n    if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1\n    return 0\n  })\n}\n\nexport function focusFrom(current: HTMLElement | null, focus: Focus) {\n  return focusIn(getFocusableElements(), focus, { relativeTo: current })\n}\n\nexport function focusIn(\n  container: HTMLElement | HTMLElement[],\n  focus: Focus,\n  {\n    sorted = true,\n    relativeTo = null,\n    skipElements = [],\n  }: Partial<{ sorted: boolean; relativeTo: HTMLElement | null; skipElements: HTMLElement[] }> = {}\n) {\n  let ownerDocument =\n    (Array.isArray(container)\n      ? container.length > 0\n        ? container[0].ownerDocument\n        : document\n      : container?.ownerDocument) ?? document\n\n  let elements = Array.isArray(container)\n    ? sorted\n      ? sortByDomNode(container)\n      : container\n    : getFocusableElements(container)\n\n  if (skipElements.length > 0 && elements.length > 1) {\n    elements = elements.filter((x) => !skipElements.includes(x))\n  }\n\n  relativeTo = relativeTo ?? (ownerDocument.activeElement as HTMLElement)\n\n  let direction = (() => {\n    if (focus & (Focus.First | Focus.Next)) return Direction.Next\n    if (focus & (Focus.Previous | Focus.Last)) return Direction.Previous\n\n    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')\n  })()\n\n  let startIndex = (() => {\n    if (focus & Focus.First) return 0\n    if (focus & Focus.Previous) return Math.max(0, elements.indexOf(relativeTo)) - 1\n    if (focus & Focus.Next) return Math.max(0, elements.indexOf(relativeTo)) + 1\n    if (focus & Focus.Last) return elements.length - 1\n\n    throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last')\n  })()\n\n  let focusOptions = focus & Focus.NoScroll ? { preventScroll: true } : {}\n\n  let offset = 0\n  let total = elements.length\n  let next = undefined\n  do {\n    // Guard against infinite loops\n    if (offset >= total || offset + total <= 0) return FocusResult.Error\n\n    let nextIdx = startIndex + offset\n\n    if (focus & Focus.WrapAround) {\n      nextIdx = (nextIdx + total) % total\n    } else {\n      if (nextIdx < 0) return FocusResult.Underflow\n      if (nextIdx >= total) return FocusResult.Overflow\n    }\n\n    next = elements[nextIdx]\n\n    // Try the focus the next element, might not work if it is \"hidden\" to the user.\n    next?.focus(focusOptions)\n\n    // Try the next one in line\n    offset += direction\n  } while (next !== ownerDocument.activeElement)\n\n  // By default if you <Tab> to a text input or a textarea, the browser will\n  // select all the text once the focus is inside these DOM Nodes. However,\n  // since we are manually moving focus this behaviour is not happening. This\n  // code will make sure that the text gets selected as-if you did it manually.\n  // Note: We only do this when going forward / backward. Not for the\n  // Focus.First or Focus.Last actions. This is similar to the `autoFocus`\n  // behaviour on an input where the input will get focus but won't be\n  // selected.\n  if (focus & (Focus.Next | Focus.Previous) && isSelectableElement(next)) {\n    next.select()\n  }\n\n  return FocusResult.Success\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/form.test.ts",
    "content": "import { objectToFormEntries } from './form'\n\nit.each([\n  [{ a: 'b' }, [['a', 'b']]],\n  [\n    [1, 2, 3],\n    [\n      ['0', '1'],\n      ['1', '2'],\n      ['2', '3'],\n    ],\n  ],\n  [\n    { id: 1, admin: true, name: { first: 'Jane', last: 'Doe', nickname: { preferred: 'JDoe' } } },\n    [\n      ['id', '1'],\n      ['admin', '1'],\n      ['name[first]', 'Jane'],\n      ['name[last]', 'Doe'],\n      ['name[nickname][preferred]', 'JDoe'],\n    ],\n  ],\n])('should encode an input of %j to an form data output', (input, output) => {\n  expect(objectToFormEntries(input)).toEqual(output)\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/form.ts",
    "content": "type Entries = [string, string][]\n\nexport function objectToFormEntries(\n  source: Record<string, any> = {},\n  parentKey: string | null = null,\n  entries: Entries = []\n): Entries {\n  for (let [key, value] of Object.entries(source)) {\n    append(entries, composeKey(parentKey, key), value)\n  }\n\n  return entries\n}\n\nfunction composeKey(parent: string | null, key: string): string {\n  return parent ? parent + '[' + key + ']' : key\n}\n\nfunction append(entries: Entries, key: string, value: any): void {\n  if (Array.isArray(value)) {\n    for (let [subkey, subvalue] of value.entries()) {\n      append(entries, composeKey(key, subkey.toString()), subvalue)\n    }\n  } else if (value instanceof Date) {\n    entries.push([key, value.toISOString()])\n  } else if (typeof value === 'boolean') {\n    entries.push([key, value ? '1' : '0'])\n  } else if (typeof value === 'string') {\n    entries.push([key, value])\n  } else if (typeof value === 'number') {\n    entries.push([key, `${value}`])\n  } else if (value === null || value === undefined) {\n    entries.push([key, ''])\n  } else {\n    objectToFormEntries(value, key, entries)\n  }\n}\n\nexport function attemptSubmit(elementInForm: HTMLElement) {\n  let form = (elementInForm as any)?.form ?? elementInForm.closest('form')\n  if (!form) return\n\n  for (let element of form.elements) {\n    if (element === elementInForm) continue\n\n    if (\n      (element.tagName === 'INPUT' && element.type === 'submit') ||\n      (element.tagName === 'BUTTON' && element.type === 'submit') ||\n      (element.nodeName === 'INPUT' && element.type === 'image')\n    ) {\n      // If you press `enter` in a normal input[type='text'] field, then the form will submit by\n      // searching for the a submit element and \"click\" it. We could also use the\n      // `form.requestSubmit()` function, but this has a downside where an `event.preventDefault()`\n      // inside a `click` listener on the submit button won't stop the form from submitting.\n      element.click()\n      return\n    }\n  }\n\n  // If we get here, then there is no submit button in the form. We can use the\n  // `form.requestSubmit()` function to submit the form instead. We cannot use `form.submit()`\n  // because then the `submit` event won't be fired and `onSubmit` listeners won't be fired.\n  form.requestSubmit?.()\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/get-text-value.test.ts",
    "content": "import { getTextValue } from './get-text-value'\n\nlet html = String.raw\n\nit('should be possible to get the text value from an element', () => {\n  let element = document.createElement('div')\n  element.innerText = 'Hello World'\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should strip out emojis when receiving the text from the element', () => {\n  let element = document.createElement('div')\n  element.innerText = '🇨🇦 Canada'\n  expect(getTextValue(element)).toEqual('Canada')\n})\n\nit('should strip out hidden elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span hidden>Hello</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should strip out aria-hidden elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span aria-hidden>Hello</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should strip out role=\"img\" elements', () => {\n  let element = document.createElement('div')\n  element.innerHTML = html`<div><span role=\"img\">°</span> world</div>`\n  expect(getTextValue(element)).toEqual('world')\n})\n\nit('should be possible to get the text value from the aria-label', () => {\n  let element = document.createElement('div')\n  element.setAttribute('aria-label', 'Hello World')\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should be possible to get the text value from the aria-label (even if there is content)', () => {\n  let element = document.createElement('div')\n  element.setAttribute('aria-label', 'Hello World')\n  element.innerHTML = 'Hello Universe'\n  element.innerText = 'Hello Universe'\n  expect(getTextValue(element)).toEqual('Hello World')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using `aria-label`)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar\">Contents of foo</div>\n      <div id=\"bar\" aria-label=\"Actual value of bar\">Contents of bar</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Actual value of bar')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using its contents)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar\">Contents of foo</div>\n      <div id=\"bar\">Contents of bar</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Contents of bar')\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using `aria-label`, multiple)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar baz\">Contents of foo</div>\n      <div id=\"bar\" aria-label=\"Actual value of bar\">Contents of bar</div>\n      <div id=\"baz\" aria-label=\"Actual value of baz\">Contents of baz</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual(\n    'Actual value of bar, Actual value of baz'\n  )\n})\n\nit('should be possible to get the text value from the element referenced by aria-labelledby (using its contents, multiple)', () => {\n  document.body.innerHTML = html`\n    <div>\n      <div id=\"foo\" aria-labelledby=\"bar baz\">Contents of foo</div>\n      <div id=\"bar\">Contents of bar</div>\n      <div id=\"baz\">Contents of baz</div>\n    </div>\n  `\n\n  expect(getTextValue(document.querySelector('#foo')!)).toEqual('Contents of bar, Contents of baz')\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/get-text-value.ts",
    "content": "let emojiRegex =\n  /([\\u2700-\\u27BF]|[\\uE000-\\uF8FF]|\\uD83C[\\uDC00-\\uDFFF]|\\uD83D[\\uDC00-\\uDFFF]|[\\u2011-\\u26FF]|\\uD83E[\\uDD10-\\uDDFF])/g\n\nfunction getTextContents(element: HTMLElement): string {\n  // Using innerText instead of textContent because:\n  //\n  // > textContent gets the content of all elements, including <script> and <style> elements. In\n  // > contrast, innerText only shows \"human-readable\" elements.\n  // >\n  // > — https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext\n  let currentInnerText = element.innerText ?? ''\n\n  // Remove all the elements that shouldn't be there.\n  //\n  // [hidden]       — The user doesn't see it\n  // [aria-hidden]  — The screen reader doesn't see it\n  // [role=\"img\"]   — Even if it is text, it is used as an image\n  //\n  // This is probably the slowest part, but if you want complete control over the text value, then\n  // it is better to set an `aria-label` instead.\n  let copy = element.cloneNode(true)\n  if (!(copy instanceof HTMLElement)) {\n    return currentInnerText\n  }\n\n  let dropped = false\n  // Drop the elements that shouldn't be there.\n  for (let child of copy.querySelectorAll('[hidden],[aria-hidden],[role=\"img\"]')) {\n    child.remove()\n    dropped = true\n  }\n\n  // Now that the elements are removed, we can get the innerText such that we can strip the emojis.\n  let value = dropped ? copy.innerText ?? '' : currentInnerText\n\n  // Check if it contains some emojis or not, if so, we need to remove them\n  // because ideally we work with simple text values.\n  //\n  // Ideally we can use the much simpler RegEx: /\\p{Extended_Pictographic}/u\n  // but we can't rely on this yet, so we use the more complex one.\n  if (emojiRegex.test(value)) {\n    value = value.replace(emojiRegex, '')\n  }\n\n  return value\n}\n\nexport function getTextValue(element: HTMLElement): string {\n  // Try to use the `aria-label` first\n  let label = element.getAttribute('aria-label')\n  if (typeof label === 'string') return label.trim()\n\n  // Try to use the `aria-labelledby` second\n  let labelledby = element.getAttribute('aria-labelledby')\n  if (labelledby) {\n    // aria-labelledby can be a space-separated list of IDs, so we need to split them up and\n    // combine them into a single string.\n    let labels = labelledby\n      .split(' ')\n      .map((labelledby) => {\n        let labelEl = document.getElementById(labelledby)\n        if (labelEl) {\n          let label = labelEl.getAttribute('aria-label')\n          // Try to use the `aria-label` first (of the referenced element)\n          if (typeof label === 'string') return label.trim()\n\n          // This time, the `aria-labelledby` isn't used anymore (in Safari), so we just have to\n          // look at the contents itself.\n          return getTextContents(labelEl).trim()\n        }\n\n        return null\n      })\n      .filter(Boolean)\n\n    if (labels.length > 0) return labels.join(', ')\n  }\n\n  // Try to use the text contents of the element itself\n  return getTextContents(element).trim()\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/match.ts",
    "content": "export function match<TValue extends string | number = string, TReturnValue = unknown>(\n  value: TValue,\n  lookup: Record<TValue, TReturnValue | ((...args: any[]) => TReturnValue)>,\n  ...args: any[]\n): TReturnValue {\n  if (value in lookup) {\n    let returnValue = lookup[value]\n    return typeof returnValue === 'function' ? returnValue(...args) : returnValue\n  }\n\n  let error = new Error(\n    `Tried to handle \"${value}\" but there is no handler defined. Only defined handlers are: ${Object.keys(\n      lookup\n    )\n      .map((key) => `\"${key}\"`)\n      .join(', ')}.`\n  )\n  if (Error.captureStackTrace) Error.captureStackTrace(error, match)\n  throw error\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/micro-task.ts",
    "content": "// Polyfill\nexport function microTask(cb: () => void) {\n  if (typeof queueMicrotask === 'function') {\n    queueMicrotask(cb)\n  } else {\n    Promise.resolve()\n      .then(cb)\n      .catch((e) =>\n        setTimeout(() => {\n          throw e\n        })\n      )\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/once.ts",
    "content": "export function once<T>(cb: (...args: T[]) => void) {\n  let state = { called: false }\n\n  return (...args: T[]) => {\n    if (state.called) return\n    state.called = true\n    return cb(...args)\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/owner.ts",
    "content": "import type { Ref } from 'vue'\nimport { dom } from './dom'\nimport { env } from './env'\n\nexport function getOwnerDocument<T extends Element | Ref<Element | null>>(\n  element: T | null | undefined\n): Document | null {\n  if (env.isServer) return null\n  if (!element) return document\n  if ('ownerDocument' in element) return element.ownerDocument\n  if ('value' in element) return dom(element as any)?.ownerDocument ?? document\n\n  return null\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/pipeline.ts",
    "content": "export interface Middleware<ReqType> {\n  (request: ReqType, next: (req: ReqType) => void): void\n}\n\nexport function pipeline<ReqType>(handlers: Middleware<ReqType>[]) {\n  return (request: ReqType, andThen?: (req: ReqType) => void) => {\n    let index = 0\n\n    function next() {\n      let handler = handlers[index++]\n      if (handler) {\n        handler(request, next)\n      } else if (andThen) {\n        andThen(request)\n      }\n    }\n\n    next()\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/platform.ts",
    "content": "// This file contains functions to detect the platform the app is running on. They aren't perfect,\n// and we are making assumptions here. But it's the best we can do for now.\n\nexport function isIOS() {\n  // TODO: This is not a great way to detect iOS, but it's the best I can do for now.\n  // - `window.platform` is deprecated\n  // - `window.userAgentData.platform` is still experimental (https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/platform)\n  // - `window.userAgent` also doesn't contain the required information\n  return (\n    // Check if it is an iPhone\n    /iPhone/gi.test(window.navigator.platform) ||\n    // Check if it is an iPad. iPad reports itself as \"MacIntel\", but we can check if it is a touch\n    // screen. Let's hope that Apple doesn't release a touch screen Mac (or maybe this would then\n    // work as expected 🤔).\n    (/Mac/gi.test(window.navigator.platform) && window.navigator.maxTouchPoints > 0)\n  )\n}\n\nexport function isAndroid() {\n  return /Android/gi.test(window.navigator.userAgent)\n}\n\nexport function isMobile() {\n  return isIOS() || isAndroid()\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/render.test.ts",
    "content": "import { defineComponent } from 'vue'\nimport { html } from '../test-utils/html'\nimport { createRenderTemplate } from '../test-utils/vue-testing-library'\nimport { render } from './render'\n\nlet Dummy = defineComponent({\n  props: {\n    as: { type: [Object, String], default: 'div' },\n    slot: { type: Object, default: () => ({}) },\n  },\n  setup(props, { attrs, slots }) {\n    return () => {\n      let { slot, ...rest } = props\n\n      return render({\n        theirProps: rest,\n        ourProps: {},\n        slots,\n        attrs,\n        slot,\n        name: 'Dummy',\n      })\n    }\n  },\n})\n\nconst renderTemplate = createRenderTemplate({ Dummy })\n\ndescribe('Validation', () => {\n  it('should error when using an as=\"template\" with additional props', () => {\n    expect.hasAssertions()\n\n    renderTemplate({\n      template: html` <Dummy as=\"template\" class=\"abc\">Contents</Dummy> `,\n      errorCaptured(err) {\n        expect(err as Error).toEqual(\n          new Error(\n            [\n              'Passing props on \"template\"!',\n              '',\n              'The current component <Dummy /> is rendering a \"template\".',\n              'However we need to passthrough the following props:',\n              '  - class',\n              '',\n              'You can apply a few solutions:',\n              '  - Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n              '  - Render a single element as the child so that we can forward the props onto that element.',\n            ].join('\\n')\n          )\n        )\n        return false\n      },\n    })\n  })\n\n  it('should forward the props to the first child', () => {\n    renderTemplate({\n      template: html`\n        <Dummy as=\"template\" class=\"abc\">\n          <div id=\"result\">Contents</div>\n        </Dummy>\n      `,\n    })\n\n    expect(document.getElementById('result')).toHaveClass('abc')\n  })\n\n  it('should forward the props via Functional Components', () => {\n    renderTemplate({\n      components: {\n        PassThrough(props, context) {\n          props.as = props.as ?? 'template'\n          return render({\n            theirProps: props,\n            ourProps: {},\n            attrs: context.attrs,\n            slots: context.slots,\n            slot: {},\n            name: 'PassThrough',\n          })\n        },\n      },\n      template: html`\n        <Dummy as=\"template\" class=\"abc\" data-test=\"123\">\n          <PassThrough>\n            <div id=\"result\">Contents</div>\n          </PassThrough>\n        </Dummy>\n      `,\n    })\n\n    expect(document.getElementById('result')).toHaveClass('abc')\n  })\n\n  it('should allow use of <slot> as children', () => {\n    renderTemplate({\n      template: html`\n        <ExampleOuter>\n          <div id=\"result\">Some Content</div>\n        </ExampleOuter>\n      `,\n\n      components: {\n        ExampleOuter: defineComponent({\n          template: html`\n            <ExampleInner>\n              <slot />\n            </ExampleInner>\n          `,\n\n          components: {\n            ExampleInner: defineComponent({\n              components: { Dummy },\n\n              template: html`\n                <Dummy as=\"template\" class=\"foo\" data-test=\"123\">\n                  <Dummy as=\"template\" class=\"bar\" data-test=\"345\">\n                    <slot />\n                  </Dummy>\n                </Dummy>\n              `,\n            }),\n          },\n        }),\n      },\n    })\n\n    expect(document.getElementById('result')).toHaveClass('foo')\n    expect(document.getElementById('result')).toHaveClass('bar')\n\n    // TODO: Is this the expected behavior? Should it actually be `345`?\n    expect(document.getElementById('result')).toHaveAttribute('data-test', '123')\n  })\n})\n\ndescribe('State Data Attributes', () => {\n  it('as=element', () => {\n    renderTemplate({\n      template: html`\n        <Dummy id=\"result\" as=\"div\" :slot=\"{active: true, selected: true}\">\n          <div>test</div>\n        </Dummy>\n      `,\n    })\n\n    expect(document.getElementById('result')).toHaveAttribute(\n      'data-headlessui-state',\n      'active selected'\n    )\n  })\n\n  it('as=template', () => {\n    renderTemplate({\n      template: html`\n        <Dummy as=\"template\" class=\"abc\" :slot=\"{active: true, selected: true}\">\n          <div id=\"result\">test</div>\n        </Dummy>\n      `,\n    })\n\n    expect(document.getElementById('result')).toHaveClass('abc')\n\n    // NOTE: Removing class=\"abc\" causes this assertion to fail\n    expect(document.getElementById('result')).toHaveAttribute(\n      'data-headlessui-state',\n      'active selected'\n    )\n  })\n})\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/render.ts",
    "content": "import { cloneVNode, Fragment, h, type Slots, type VNode } from 'vue'\nimport { match } from './match'\n\nexport enum Features {\n  /** No features at all */\n  None = 0,\n\n  /**\n   * When used, this will allow us to use one of the render strategies.\n   *\n   * **The render strategies are:**\n   *    - **Unmount**   _(Will unmount the component.)_\n   *    - **Hidden**    _(Will hide the component using the [hidden] attribute.)_\n   */\n  RenderStrategy = 1,\n\n  /**\n   * When used, this will allow the user of our component to be in control. This can be used when\n   * you want to transition based on some state.\n   */\n  Static = 2,\n}\n\nexport enum RenderStrategy {\n  Unmount,\n  Hidden,\n}\n\nexport function render({\n  visible = true,\n  features = Features.None,\n  ourProps,\n  theirProps,\n  ...main\n}: {\n  ourProps: Record<string, any>\n  theirProps: Record<string, any>\n  slot: Record<string, any>\n  attrs: Record<string, any>\n  slots: Slots\n  name: string\n} & {\n  features?: Features\n  visible?: boolean\n}) {\n  let props = mergeProps(theirProps, ourProps)\n  let mainWithProps = Object.assign(main, { props })\n\n  // Visible always render\n  if (visible) return _render(mainWithProps)\n\n  if (features & Features.Static) {\n    // When the `static` prop is passed as `true`, then the user is in control, thus we don't care about anything else\n    if (props.static) return _render(mainWithProps)\n  }\n\n  if (features & Features.RenderStrategy) {\n    let strategy = props.unmount ?? true ? RenderStrategy.Unmount : RenderStrategy.Hidden\n\n    return match(strategy, {\n      [RenderStrategy.Unmount]() {\n        return null\n      },\n      [RenderStrategy.Hidden]() {\n        return _render({\n          ...main,\n          props: { ...props, hidden: true, style: { display: 'none' } },\n        })\n      },\n    })\n  }\n\n  // No features enabled, just render\n  return _render(mainWithProps)\n}\n\nfunction _render({\n  props,\n  attrs,\n  slots,\n  slot,\n  name,\n}: {\n  props: Record<string, any>\n  slot: Record<string, any>\n  attrs: Record<string, any>\n  slots: Slots\n  name: string\n}) {\n  let { as, ...incomingProps } = omit(props, ['unmount', 'static'])\n\n  let children = slots.default?.(slot)\n\n  let dataAttributes: Record<string, string> = {}\n  if (slot) {\n    let exposeState = false\n    let states = []\n    for (let [k, v] of Object.entries(slot)) {\n      if (typeof v === 'boolean') {\n        exposeState = true\n      }\n      if (v === true) {\n        states.push(k)\n      }\n    }\n\n    if (exposeState) dataAttributes[`data-headlessui-state`] = states.join(' ')\n  }\n\n  if (as === 'template') {\n    children = flattenFragments(children ?? [])\n\n    if (Object.keys(incomingProps).length > 0 || Object.keys(attrs).length > 0) {\n      let [firstChild, ...other] = children ?? []\n\n      if (!isValidElement(firstChild) || other.length > 0) {\n        throw new Error(\n          [\n            'Passing props on \"template\"!',\n            '',\n            `The current component <${name} /> is rendering a \"template\".`,\n            `However we need to passthrough the following props:`,\n            Object.keys(incomingProps)\n              .concat(Object.keys(attrs))\n              .map((name) => name.trim())\n              .filter((current, idx, all) => all.indexOf(current) === idx)\n              .sort((a, z) => a.localeCompare(z))\n              .map((line) => `  - ${line}`)\n              .join('\\n'),\n            '',\n            'You can apply a few solutions:',\n            [\n              'Add an `as=\"...\"` prop, to ensure that we render an actual element instead of a \"template\".',\n              'Render a single element as the child so that we can forward the props onto that element.',\n            ]\n              .map((line) => `  - ${line}`)\n              .join('\\n'),\n          ].join('\\n')\n        )\n      }\n\n      let mergedProps = mergeProps(firstChild.props ?? {}, incomingProps, dataAttributes)\n      let cloned = cloneVNode(firstChild, mergedProps, true)\n      // Explicitly override props starting with `on`. This is for event handlers, but there are\n      // scenario's where we set them to `undefined` explicitly (when `aria-disabled=\"true\"` is\n      // happening instead of `disabled`). But cloneVNode doesn't like overriding `onXXX` props so\n      // we have to do it manually.\n      for (let prop in mergedProps) {\n        if (prop.startsWith('on')) {\n          cloned.props ||= {}\n          cloned.props[prop] = mergedProps[prop]\n        }\n      }\n      return cloned\n    }\n\n    if (Array.isArray(children) && children.length === 1) {\n      // TODO: Do we need to cloneVNode + dataAttributes here?\n      return children[0]\n    }\n\n    return children\n  }\n\n  return h(as, Object.assign({}, incomingProps, dataAttributes), {\n    default: () => children,\n  })\n}\n\n/**\n * When passed a structure like this:\n * <Example><span>something</span></Example>\n *\n * And Example is defined as:\n * <SomeComponent><slot /></SomeComponent>\n *\n * We need to turn the fragment that <slot> represents into the slot.\n * Luckily by this point it's already rendered into an array of VNodes\n * for us so we can just flatten it directly.\n *\n * We have to do this recursively because there could be multiple\n * levels of Component nesting all with <slot> elements interspersed\n *\n * @param children\n * @returns\n */\nfunction flattenFragments(children: VNode[]): VNode[] {\n  return children.flatMap((child) => {\n    if (child.type === Fragment) {\n      return flattenFragments(child.children as VNode[])\n    }\n\n    return [child]\n  })\n}\n\nfunction mergeProps(...listOfProps: Record<any, any>[]) {\n  if (listOfProps.length === 0) return {}\n  if (listOfProps.length === 1) return listOfProps[0]\n\n  let target: Record<any, any> = {}\n\n  let eventHandlers: Record<\n    string,\n    ((event: { defaultPrevented: boolean }, ...args: any[]) => void | undefined)[]\n  > = {}\n\n  for (let props of listOfProps) {\n    for (let prop in props) {\n      // Collect event handlers\n      if (prop.startsWith('on') && typeof props[prop] === 'function') {\n        eventHandlers[prop] ??= []\n        eventHandlers[prop].push(props[prop])\n      } else {\n        // Override incoming prop\n        target[prop] = props[prop]\n      }\n    }\n  }\n\n  // Do not attach any event handlers when there is a `disabled` or `aria-disabled` prop set.\n  if (target.disabled || target['aria-disabled']) {\n    return Object.assign(\n      target,\n      // Set all event listeners that we collected to `undefined`. This is\n      // important because of the `cloneElement` from above, which merges the\n      // existing and new props, they don't just override therefore we have to\n      // explicitly nullify them.\n      Object.fromEntries(Object.keys(eventHandlers).map((eventName) => [eventName, undefined]))\n    )\n  }\n\n  // Merge event handlers\n  for (let eventName in eventHandlers) {\n    Object.assign(target, {\n      [eventName](event: { defaultPrevented: boolean }, ...args: any[]) {\n        let handlers = eventHandlers[eventName]\n\n        for (let handler of handlers) {\n          if (event instanceof Event && event.defaultPrevented) {\n            return\n          }\n\n          handler(event, ...args)\n        }\n      },\n    })\n  }\n\n  return target\n}\n\nexport function compact<T extends Record<any, any>>(object: T) {\n  let clone = Object.assign({}, object)\n  for (let key in clone) {\n    if (clone[key] === undefined) delete clone[key]\n  }\n  return clone\n}\n\nexport function omit<T extends Record<any, any>, Keys extends keyof T>(\n  object: T,\n  keysToOmit: readonly Keys[] = []\n) {\n  let clone = Object.assign({}, object) as T\n  for (let key of keysToOmit) {\n    if (key in clone) delete clone[key]\n  }\n  return clone as Omit<T, Keys>\n}\n\nfunction isValidElement(input: any): boolean {\n  if (input == null) return false // No children\n  if (typeof input.type === 'string') return true // 'div', 'span', ...\n  if (typeof input.type === 'object') return true // Other components\n  if (typeof input.type === 'function') return true // Built-ins like Transition\n  return false // Comments, strings, ...\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/resolve-prop-value.ts",
    "content": "export function resolvePropValue<TProperty, TBag>(property: TProperty, bag: TBag) {\n  if (property === undefined) return undefined\n  if (typeof property === 'function') return property(bag)\n  return property\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/src/utils/store.ts",
    "content": "type ChangeFn = () => void\ntype UnsubscribeFn = () => void\ntype ActionFn<T> = (this: T, ...args: any[]) => T | void\ntype StoreActions<Key extends string, T> = Record<Key, ActionFn<T>>\n\nexport interface Store<T, ActionKey extends string> {\n  getSnapshot(): T\n  subscribe(onChange: ChangeFn): UnsubscribeFn\n  dispatch(action: ActionKey, ...args: any[]): void\n}\n\nexport function createStore<T, ActionKey extends string>(\n  initial: () => T,\n  actions: StoreActions<ActionKey, T>\n): Store<T, ActionKey> {\n  let state: T = initial()\n\n  let listeners = new Set<ChangeFn>()\n\n  return {\n    getSnapshot() {\n      return state\n    },\n\n    subscribe(onChange) {\n      listeners.add(onChange)\n\n      return () => listeners.delete(onChange)\n    },\n\n    dispatch(key: ActionKey, ...args: any[]) {\n      let newState = actions[key].call(state, ...args)\n      if (newState) {\n        state = newState\n        listeners.forEach((listener) => listener())\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/tsconfig.json",
    "content": "{\n  \"include\": [\"src\", \"types\"],\n  \"compilerOptions\": {\n    \"module\": \"esnext\",\n    \"lib\": [\"dom\", \"esnext\", \"dom.iterable\"],\n    \"importHelpers\": true,\n    \"declaration\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"downlevelIteration\": true,\n    \"moduleResolution\": \"node\",\n    \"esModuleInterop\": true,\n    \"target\": \"ESNext\",\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"verbatimModuleSyntax\": true\n  },\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "packages/@headlessui-vue/types/jest.d.ts",
    "content": "export {}\n\ndeclare global {\n  namespace jest {\n    interface Matchers<R> {\n      toBeWithinRenderFrame(actual: number): R\n    }\n  }\n}\n"
  },
  {
    "path": "playgrounds/react/components/button.tsx",
    "content": "import { ComponentProps, forwardRef, ReactNode } from 'react'\n\nfunction classNames(...classes: (string | false | undefined | null)[]) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport let Button = forwardRef<\n  HTMLButtonElement,\n  ComponentProps<'button'> & { children?: ReactNode }\n>(({ className, ...props }, ref) => (\n  <button\n    ref={ref}\n    type=\"button\"\n    className={classNames(\n      'focus:outline-hidden ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100',\n      className\n    )}\n    {...props}\n  />\n))\n"
  },
  {
    "path": "playgrounds/react/components/input.tsx",
    "content": "import { ComponentProps, forwardRef, ReactNode } from 'react'\n\nfunction classNames(...classes: (string | false | undefined | null)[]) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport let Input = forwardRef<HTMLInputElement, ComponentProps<'input'> & { children?: ReactNode }>(\n  ({ className, ...props }, ref) => (\n    <input\n      ref={ref}\n      type=\"text\"\n      className={classNames(\n        'focus:outline-hidden ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100',\n        className\n      )}\n      {...props}\n    />\n  )\n)\n"
  },
  {
    "path": "playgrounds/react/data.ts",
    "content": "export let countries = [\n  'Afghanistan',\n  'Albania',\n  'Algeria',\n  'American Samoa',\n  'Andorra',\n  'Angola',\n  'Anguilla',\n  'Antarctica',\n  'Antigua and Barbuda',\n  'Argentina',\n  'Armenia',\n  'Aruba',\n  'Australia',\n  'Austria',\n  'Azerbaijan',\n  'Bahamas (the)',\n  'Bahrain',\n  'Bangladesh',\n  'Barbados',\n  'Belarus',\n  'Belgium',\n  'Belize',\n  'Benin',\n  'Bermuda',\n  'Bhutan',\n  'Bolivia (Plurinational State of)',\n  'Bonaire, Sint Eustatius and Saba',\n  'Bosnia and Herzegovina',\n  'Botswana',\n  'Bouvet Island',\n  'Brazil',\n  'British Indian Ocean Territory (the)',\n  'Brunei Darussalam',\n  'Bulgaria',\n  'Burkina Faso',\n  'Burundi',\n  'Cabo Verde',\n  'Cambodia',\n  'Cameroon',\n  'Canada',\n  'Cayman Islands (the)',\n  'Central African Republic (the)',\n  'Chad',\n  'Chile',\n  'China',\n  'Christmas Island',\n  'Cocos (Keeling) Islands (the)',\n  'Colombia',\n  'Comoros (the)',\n  'Congo (the Democratic Republic of the)',\n  'Congo (the)',\n  'Cook Islands (the)',\n  'Costa Rica',\n  'Croatia',\n  'Cuba',\n  'Curaçao',\n  'Cyprus',\n  'Czechia',\n  \"Côte d'Ivoire\",\n  'Denmark',\n  'Djibouti',\n  'Dominica',\n  'Dominican Republic (the)',\n  'Ecuador',\n  'Egypt',\n  'El Salvador',\n  'Equatorial Guinea',\n  'Eritrea',\n  'Estonia',\n  'Eswatini',\n  'Ethiopia',\n  'Falkland Islands (the) [Malvinas]',\n  'Faroe Islands (the)',\n  'Fiji',\n  'Finland',\n  'France',\n  'French Guiana',\n  'French Polynesia',\n  'French Southern Territories (the)',\n  'Gabon',\n  'Gambia (the)',\n  'Georgia',\n  'Germany',\n  'Ghana',\n  'Gibraltar',\n  'Greece',\n  'Greenland',\n  'Grenada',\n  'Guadeloupe',\n  'Guam',\n  'Guatemala',\n  'Guernsey',\n  'Guinea',\n  'Guinea-Bissau',\n  'Guyana',\n  'Haiti',\n  'Heard Island and McDonald Islands',\n  'Holy See (the)',\n  'Honduras',\n  'Hong Kong',\n  'Hungary',\n  'Iceland',\n  'India',\n  'Indonesia',\n  'Iran (Islamic Republic of)',\n  'Iraq',\n  'Ireland',\n  'Isle of Man',\n  'Israel',\n  'Italy',\n  'Jamaica',\n  'Japan',\n  'Jersey',\n  'Jordan',\n  'Kazakhstan',\n  'Kenya',\n  'Kiribati',\n  \"Korea (the Democratic People's Republic of)\",\n  'Korea (the Republic of)',\n  'Kuwait',\n  'Kyrgyzstan',\n  \"Lao People's Democratic Republic (the)\",\n  'Latvia',\n  'Lebanon',\n  'Lesotho',\n  'Liberia',\n  'Libya',\n  'Liechtenstein',\n  'Lithuania',\n  'Luxembourg',\n  'Macao',\n  'Madagascar',\n  'Malawi',\n  'Malaysia',\n  'Maldives',\n  'Mali',\n  'Malta',\n  'Marshall Islands (the)',\n  'Martinique',\n  'Mauritania',\n  'Mauritius',\n  'Mayotte',\n  'Mexico',\n  'Micronesia (Federated States of)',\n  'Moldova (the Republic of)',\n  'Monaco',\n  'Mongolia',\n  'Montenegro',\n  'Montserrat',\n  'Morocco',\n  'Mozambique',\n  'Myanmar',\n  'Namibia',\n  'Nauru',\n  'Nepal',\n  'Netherlands (the)',\n  'New Caledonia',\n  'New Zealand',\n  'Nicaragua',\n  'Niger (the)',\n  'Nigeria',\n  'Niue',\n  'Norfolk Island',\n  'Northern Mariana Islands (the)',\n  'Norway',\n  'Oman',\n  'Pakistan',\n  'Palau',\n  'Palestine, State of',\n  'Panama',\n  'Papua New Guinea',\n  'Paraguay',\n  'Peru',\n  'Philippines (the)',\n  'Pitcairn',\n  'Poland',\n  'Portugal',\n  'Puerto Rico',\n  'Qatar',\n  'Republic of North Macedonia',\n  'Romania',\n  'Russian Federation (the)',\n  'Rwanda',\n  'Réunion',\n  'Saint Barthélemy',\n  'Saint Helena, Ascension and Tristan da Cunha',\n  'Saint Kitts and Nevis',\n  'Saint Lucia',\n  'Saint Martin (French part)',\n  'Saint Pierre and Miquelon',\n  'Saint Vincent and the Grenadines',\n  'Samoa',\n  'San Marino',\n  'Sao Tome and Principe',\n  'Saudi Arabia',\n  'Senegal',\n  'Serbia',\n  'Seychelles',\n  'Sierra Leone',\n  'Singapore',\n  'Sint Maarten (Dutch part)',\n  'Slovakia',\n  'Slovenia',\n  'Solomon Islands',\n  'Somalia',\n  'South Africa',\n  'South Georgia and the South Sandwich Islands',\n  'South Sudan',\n  'Spain',\n  'Sri Lanka',\n  'Sudan (the)',\n  'Suriname',\n  'Svalbard and Jan Mayen',\n  'Sweden',\n  'Switzerland',\n  'Syrian Arab Republic',\n  'Taiwan',\n  'Tajikistan',\n  'Tanzania, United Republic of',\n  'Thailand',\n  'Timor-Leste',\n  'Togo',\n  'Tokelau',\n  'Tonga',\n  'Trinidad and Tobago',\n  'Tunisia',\n  'Turkey',\n  'Turkmenistan',\n  'Turks and Caicos Islands (the)',\n  'Tuvalu',\n  'Uganda',\n  'Ukraine',\n  'United Arab Emirates (the)',\n  'United Kingdom of Great Britain and Northern Ireland (the)',\n  'United States Minor Outlying Islands (the)',\n  'United States of America (the)',\n  'Uruguay',\n  'Uzbekistan',\n  'Vanuatu',\n  'Venezuela (Bolivarian Republic of)',\n  'Viet Nam',\n  'Virgin Islands (British)',\n  'Virgin Islands (U.S.)',\n  'Wallis and Futuna',\n  'Western Sahara',\n  'Yemen',\n  'Zambia',\n  'Zimbabwe',\n  'Åland Islands',\n]\n\nexport let timezones: string[] = Intl.supportedValuesOf('timeZone')\n"
  },
  {
    "path": "playgrounds/react/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.\n"
  },
  {
    "path": "playgrounds/react/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nmodule.exports = {\n  reactStrictMode: false,\n  devIndicators: {\n    buildActivity: false,\n  },\n}\n"
  },
  {
    "path": "playgrounds/react/package.json",
    "content": "{\n  \"name\": \"playground-react\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"prebuild\": \"npm run build --workspace=@headlessui/react && npm run build --workspace=@headlessui/tailwindcss\",\n    \"predev\": \"npm run build --workspace=@headlessui/react && npm run build --workspace=@headlessui/tailwindcss\",\n    \"dev:tailwindcss\": \"npm run watch --workspace=@headlessui/tailwindcss\",\n    \"dev:headlessui\": \"npm run watch --workspace=@headlessui/react\",\n    \"dev:next\": \"next dev\",\n    \"dev\": \"npm-run-all -p dev:*\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint-types\": \"echo\",\n    \"clean\": \"rimraf ./.next\"\n  },\n  \"dependencies\": {\n    \"@headlessui/react\": \"*\",\n    \"@headlessui/tailwindcss\": \"*\",\n    \"@heroicons/react\": \"^1.0.6\",\n    \"@popperjs/core\": \"^2.6.0\",\n    \"@tailwindcss/forms\": \"^0.5.2\",\n    \"@tailwindcss/postcss\": \"^4.1.6\",\n    \"@tailwindcss/typography\": \"^0.5.2\",\n    \"framer-motion\": \"^6.0.0\",\n    \"next\": \"^15.4.4\",\n    \"postcss\": \"^8.4.14\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-flatpickr\": \"^3.10.9\",\n    \"react-hot-toast\": \"2.3.0\",\n    \"tailwindcss\": \"^4.1.6\"\n  },\n  \"devDependencies\": {\n    \"@floating-ui/react\": \"^0.24.8\"\n  },\n  \"resolutions\": {\n    \"next/@swc/helpers\": \"0.4.36\"\n  }\n}\n"
  },
  {
    "path": "playgrounds/react/pages/_app.tsx",
    "content": "import Link from 'next/link'\nimport { useEffect, useState } from 'react'\n\nimport { useRouter } from 'next/router'\nimport './styles.css'\n\nfunction disposables() {\n  let disposables: Function[] = []\n\n  let api = {\n    requestAnimationFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      let raf = requestAnimationFrame(...args)\n      api.add(() => cancelAnimationFrame(raf))\n    },\n\n    nextFrame(...args: Parameters<typeof requestAnimationFrame>) {\n      api.requestAnimationFrame(() => {\n        api.requestAnimationFrame(...args)\n      })\n    },\n\n    setTimeout(...args: Parameters<typeof setTimeout>) {\n      let timer = setTimeout(...args)\n      api.add(() => clearTimeout(timer))\n    },\n\n    add(cb: () => void) {\n      disposables.push(cb)\n    },\n\n    dispose() {\n      for (let dispose of disposables.splice(0)) {\n        dispose()\n      }\n    },\n  }\n\n  return api\n}\n\nexport function useDisposables() {\n  // Using useState instead of useRef so that we can use the initializer function.\n  let [d] = useState(disposables)\n  useEffect(() => () => d.dispose(), [d])\n  return d\n}\n\nenum KeyDisplayMac {\n  ArrowUp = '↑',\n  ArrowDown = '↓',\n  ArrowLeft = '←',\n  ArrowRight = '→',\n  Home = '↖',\n  End = '↘',\n  Alt = '⌥',\n  CapsLock = '⇪',\n  Meta = '⌘',\n  Shift = '⇧',\n  Control = '⌃',\n  Backspace = '⌫',\n  Delete = '⌦',\n  Enter = '↵',\n  Escape = '⎋',\n  Tab = '↹',\n  PageUp = '⇞',\n  PageDown = '⇟',\n  ' ' = '␣',\n}\n\nenum KeyDisplayWindows {\n  ArrowUp = '↑',\n  ArrowDown = '↓',\n  ArrowLeft = '←',\n  ArrowRight = '→',\n  Meta = 'Win',\n  Control = 'Ctrl',\n  Backspace = '⌫',\n  Delete = 'Del',\n  Escape = 'Esc',\n  PageUp = 'PgUp',\n  PageDown = 'PgDn',\n  ' ' = '␣',\n}\n\nfunction tap<T>(value: T, cb: (value: T) => void) {\n  cb(value)\n  return value\n}\n\nfunction useKeyDisplay() {\n  let [mounted, setMounted] = useState(false)\n\n  useEffect(() => {\n    setMounted(true)\n  }, [])\n\n  if (!mounted) return {}\n  let isMac = navigator.userAgent.indexOf('Mac OS X') !== -1\n  return isMac ? KeyDisplayMac : KeyDisplayWindows\n}\n\nfunction KeyCaster() {\n  let [keys, setKeys] = useState<string[]>([])\n  let d = useDisposables()\n  let KeyDisplay = useKeyDisplay()\n\n  useEffect(() => {\n    function handler(event: KeyboardEvent) {\n      setKeys((current) => [\n        event.shiftKey && event.key !== 'Shift'\n          ? KeyDisplay[`Shift${event.key}`] ?? event.key\n          : KeyDisplay[event.key] ?? event.key,\n        ...current,\n      ])\n      d.setTimeout(() => setKeys((current) => tap(current.slice(), (clone) => clone.pop())), 2000)\n    }\n\n    window.addEventListener('keydown', handler, true)\n    return () => window.removeEventListener('keydown', handler, true)\n  }, [d, KeyDisplay])\n\n  if (keys.length <= 0) return null\n\n  return (\n    <div className=\"pointer-events-none fixed bottom-4 right-4 z-50 cursor-default select-none overflow-hidden rounded-md bg-blue-800 px-4 py-2 text-2xl tracking-wide text-blue-100 shadow-sm\">\n      {keys.slice().reverse().join(' ')}\n    </div>\n  )\n}\n\nfunction MyApp({ Component, pageProps }) {\n  let router = useRouter()\n  if (router.query.raw !== undefined) {\n    return <Component {...pageProps} />\n  }\n\n  return (\n    <>\n      <div className=\"flex h-screen flex-col overflow-hidden bg-gray-700 font-sans text-gray-900 antialiased\">\n        <header className=\"relative z-10 flex shrink-0 items-center justify-between border-b border-gray-200 bg-gray-700 px-4 py-4 sm:px-6 lg:px-8\">\n          <Link href=\"/\">\n            <Logo className=\"h-6\" />\n          </Link>\n          <span className=\"font-bold text-white\">(React)</span>\n        </header>\n\n        <KeyCaster />\n\n        <main className=\"flex-1 overflow-auto bg-gray-50\">\n          <Component {...pageProps} />\n        </main>\n      </div>\n    </>\n  )\n}\n\nfunction Logo({ className }) {\n  return (\n    <svg className={className} xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 243 42\">\n      <path\n        fill=\"#fff\"\n        d=\"M65.74 13.663c-2.62 0-4.702.958-5.974 2.95V6.499h-4.163V33.32h4.163V23.051c0-3.908 2.159-5.518 4.896-5.518 2.62 0 4.317 1.533 4.317 4.445V33.32h4.162V21.557c0-4.982-3.083-7.894-7.4-7.894zM79.936 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.674-10.116-6.052 0-10.176 4.407-10.176 10.078 0 5.748 4.124 10.078 10.484 10.078 3.778 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.925 1.341-2.66 2.376-4.972 2.376-3.084 0-5.512-1.533-6.168-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.506 0 4.934 1.418 5.512 4.713H79.898zM113.282 14.161v2.72c-1.465-1.992-3.739-3.218-6.746-3.218-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.162V14.16h-4.162zm-6.09 15.71c-3.469 0-6.091-2.567-6.091-6.13 0-3.564 2.622-6.131 6.091-6.131 3.469 0 6.09 2.567 6.09 6.13 0 3.564-2.621 6.132-6.09 6.132zM136.597 6.498v10.384c-1.465-1.993-3.739-3.219-6.746-3.219-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.163V6.497h-4.163zm-6.09 23.374c-3.469 0-6.09-2.568-6.09-6.131 0-3.564 2.621-6.131 6.09-6.131s6.09 2.567 6.09 6.13c0 3.564-2.621 6.132-6.09 6.132zM144.648 33.32h4.163V5.348h-4.163V33.32zM155.957 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.675-10.116-6.051 0-10.176 4.407-10.176 10.078 0 5.748 4.125 10.078 10.485 10.078 3.777 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.926 1.341-2.66 2.376-4.973 2.376-3.083 0-5.512-1.533-6.167-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.505 0 4.934 1.418 5.512 4.713h-11.332zM177.137 19.45c0-1.38 1.311-2.032 2.814-2.032 1.581 0 2.93.69 3.623 2.184l3.508-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.279-2.759l-3.584 2.07c1.233 2.758 4.008 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.369-4.98-10.369-8.468zM192.774 19.45c0-1.38 1.31-2.032 2.813-2.032 1.581 0 2.93.69 3.624 2.184l3.507-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.278-2.759l-3.585 2.07c1.233 2.758 4.009 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.368-4.98-10.368-8.468zM224.523 28.9c2.889 0 5.027-1.715 5.027-4.53v-8.782h-2.588v8.577c0 1.268-.676 2.219-2.439 2.219s-2.438-.951-2.438-2.22v-8.576h-2.569v8.782c0 2.815 2.138 4.53 5.007 4.53zM232.257 15.588V28.64h2.588V15.588h-2.588z\"\n      />\n      <path\n        fill=\"#fff\"\n        fillRule=\"evenodd\"\n        d=\"M233.817 9.328H220.42c-2.96 0-5.359 2.385-5.359 5.327v13.318c0 2.942 2.399 5.327 5.359 5.327h13.397c2.959 0 5.358-2.385 5.358-5.327V14.655c0-2.942-2.399-5.327-5.358-5.327zM220.42 6.664c-4.439 0-8.038 3.578-8.038 7.99v13.319c0 4.413 3.599 7.99 8.038 7.99h13.397c4.439 0 8.038-3.577 8.038-7.99V14.655c0-4.413-3.599-7.99-8.038-7.99H220.42z\"\n        clipRule=\"evenodd\"\n      />\n      <path\n        fill=\"#fff\"\n        fillRule=\"evenodd\"\n        d=\"M220.42 9.328h13.397c2.959 0 5.358 2.385 5.358 5.327v13.318c0 2.942-2.399 5.327-5.358 5.327H220.42c-2.96 0-5.359-2.385-5.359-5.327V14.655c0-2.942 2.399-5.327 5.359-5.327zm-8.038 5.327c0-4.413 3.599-7.99 8.038-7.99h13.397c4.439 0 8.038 3.577 8.038 7.99v13.318c0 4.413-3.599 7.99-8.038 7.99H220.42c-4.439 0-8.038-3.577-8.038-7.99V14.655z\"\n        clipRule=\"evenodd\"\n      />\n      <path\n        fill=\"url(#prefix__paint0_linear)\"\n        d=\"M8.577 26.097l25.779-8.556c-.514-3.201-.88-5.342-1.307-6.974-.457-1.756-.821-2.226-.965-2.39a5.026 5.026 0 00-1.81-1.306c-.2-.086-.762-.284-2.583-.175-1.924.116-4.453.507-8.455 1.137-4.003.63-6.529 1.035-8.395 1.516-1.766.456-2.239.817-2.403.96a4.999 4.999 0 00-1.315 1.8c-.085.198-.285.757-.175 2.568.116 1.913.51 4.426 1.143 8.405.178 1.114.337 2.113.486 3.015z\"\n      />\n      <path\n        fill=\"url(#prefix__paint1_linear)\"\n        fillRule=\"evenodd\"\n        d=\"M1.47 24.124C.244 16.427-.37 12.58.96 9.49A11.665 11.665 0 014.027 5.29c2.545-2.21 6.416-2.82 14.16-4.039C25.93.031 29.8-.578 32.907.743a11.729 11.729 0 014.225 3.05c2.223 2.53 2.836 6.38 4.063 14.076 1.226 7.698 1.84 11.546.511 14.636a11.666 11.666 0 01-3.069 4.199c-2.545 2.21-6.416 2.82-14.159 4.039-7.743 1.219-11.614 1.828-14.722.508a11.728 11.728 0 01-4.224-3.05C3.31 35.67 2.697 31.82 1.47 24.123zm13.657 13.668c2.074-.125 4.743-.54 8.697-1.163 3.953-.622 6.62-1.047 8.632-1.566 1.949-.502 2.846-.992 3.426-1.496a7.5 7.5 0 001.973-2.7c.302-.703.494-1.703.372-3.7-.125-2.063-.543-4.716-1.17-8.646-.625-3.93-1.053-6.582-1.574-8.582-.506-1.937-.999-2.83-1.505-3.405a7.54 7.54 0 00-2.716-1.961c-.707-.301-1.713-.492-3.723-.371-2.074.125-4.743.54-8.697 1.163-3.953.622-6.62 1.047-8.632 1.565-1.949.503-2.846.993-3.426 1.497a7.5 7.5 0 00-1.972 2.699c-.303.704-.495 1.704-.373 3.701.125 2.062.543 4.716 1.17 8.646.625 3.93 1.053 6.582 1.574 8.581.506 1.938 1 2.83 1.505 3.406a7.54 7.54 0 002.716 1.961c.707.3 1.713.492 3.723.37z\"\n        clipRule=\"evenodd\"\n      />\n      <defs>\n        <linearGradient\n          id=\"prefix__paint0_linear\"\n          x1=\"16.759\"\n          x2=\"23.386\"\n          y1=\"0\"\n          y2=\"41.662\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#66E3FF\" />\n          <stop offset=\"1\" stopColor=\"#7064F9\" />\n        </linearGradient>\n        <linearGradient\n          id=\"prefix__paint1_linear\"\n          x1=\"16.759\"\n          x2=\"23.386\"\n          y1=\"0\"\n          y2=\"41.662\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#66E3FF\" />\n          <stop offset=\"1\" stopColor=\"#7064F9\" />\n        </linearGradient>\n      </defs>\n    </svg>\n  )\n}\n\nexport default MyApp\n"
  },
  {
    "path": "playgrounds/react/pages/_document.tsx",
    "content": "import { Head, Html, Main, NextScript } from 'next/document'\n\nexport default function Document() {\n  return (\n    <Html>\n      <Head>\n        <meta httpEquiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\n        <link rel=\"stylesheet\" href=\"https://rsms.me/inter/inter.css\" />\n\n        <link\n          rel=\"icon\"\n          type=\"image/png\"\n          sizes=\"32x32\"\n          href=\"https://headlessui.dev/favicon-32x32.png\"\n        />\n        <link\n          rel=\"icon\"\n          type=\"image/png\"\n          sizes=\"16x16\"\n          href=\"https://headlessui.dev/favicon-16x16.png\"\n        />\n      </Head>\n      <body>\n        <Main />\n        <NextScript />\n      </body>\n    </Html>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/_error.tsx",
    "content": "import ErrorPage from 'next/error'\nimport Head from 'next/head'\nimport Link from 'next/link'\n\nimport { ExamplesType, resolveAllExamples } from '../utils/resolve-all-examples'\n\nexport async function getStaticProps() {\n  return {\n    props: {\n      examples: await resolveAllExamples('pages'),\n    },\n  }\n}\n\nexport default function Page(props: { examples: false | ExamplesType[] }) {\n  if (props.examples === false) {\n    return <ErrorPage statusCode={404} />\n  }\n\n  return (\n    <>\n      <Head>\n        <title>Examples</title>\n      </Head>\n\n      <div className=\"container mx-auto my-24\">\n        <div className=\"prose\">\n          <h2>Examples</h2>\n          <Examples examples={props.examples} />\n        </div>\n      </div>\n    </>\n  )\n}\n\nexport function Examples(props: { examples: ExamplesType[] }) {\n  return (\n    <ul>\n      {props.examples.map((example) => (\n        <li key={example.path}>\n          {example.children ? (\n            <h3 className=\"text-xl capitalize\">{example.name}</h3>\n          ) : (\n            <Link href={example.path} className=\"capitalize\">\n              {example.name}\n            </Link>\n          )}\n          {example.children && <Examples examples={example.children} />}\n        </li>\n      ))}\n    </ul>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combinations/form.tsx",
    "content": "import { Combobox, Field, Input, Label, Listbox, RadioGroup, Switch } from '@headlessui/react'\nimport { useState } from 'react'\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nfunction Section({ title, children }) {\n  return (\n    <fieldset className=\"rounded-lg border bg-gray-200/20 p-3\">\n      <legend className=\"rounded-md border bg-gray-100 px-2 text-sm uppercase\">{title}</legend>\n      <div className=\"flex flex-col gap-3\">{children}</div>\n    </fieldset>\n  )\n}\n\nlet sizes = ['xs', 'sm', 'md', 'lg', 'xl']\nlet people = [\n  { id: 1, name: { first: 'Alice' } },\n  { id: 2, name: { first: 'Bob' } },\n  { id: 3, name: { first: 'Charlie' } },\n]\nlet locations = ['New York', 'London', 'Paris', 'Berlin']\n\nexport default function App() {\n  let [result, setResult] = useState(() =>\n    typeof window === 'undefined' || typeof document === 'undefined' ? [] : new FormData()\n  )\n  let [query, setQuery] = useState('')\n\n  return (\n    <div className=\"py-8\">\n      <form\n        className=\"mx-auto flex h-full max-w-4xl flex-col items-start justify-center gap-8 rounded-lg border bg-white p-6\"\n        onSubmit={(event) => {\n          event.preventDefault()\n          setResult(new FormData(event.currentTarget))\n        }}\n      >\n        <div className=\"grid w-full grid-cols-[repeat(auto-fill,minmax(350px,1fr))] items-start gap-3\">\n          <Section title=\"Switch\">\n            <Section title=\"Single value\">\n              <Switch.Group as=\"div\" className=\"flex items-center justify-between space-x-4\">\n                <Switch.Label>Enable notifications</Switch.Label>\n\n                <Switch\n                  defaultChecked={true}\n                  name=\"notifications\"\n                  className={({ checked }) =>\n                    classNames(\n                      'focus:outline-hidden relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',\n                      checked ? 'bg-blue-600' : 'bg-gray-200'\n                    )\n                  }\n                >\n                  {({ checked }) => (\n                    <span\n                      className={classNames(\n                        'inline-block h-5 w-5 transform rounded-full bg-white',\n                        checked ? 'translate-x-5' : 'translate-x-0'\n                      )}\n                    />\n                  )}\n                </Switch>\n              </Switch.Group>\n            </Section>\n\n            <Section title=\"Multiple values\">\n              <Switch.Group as=\"div\" className=\"flex items-center justify-between space-x-4\">\n                <Switch.Label>Apple</Switch.Label>\n\n                <Switch\n                  name=\"fruit[]\"\n                  value=\"apple\"\n                  className={({ checked }) =>\n                    classNames(\n                      'focus:outline-hidden relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',\n                      checked ? 'bg-blue-600' : 'bg-gray-200'\n                    )\n                  }\n                >\n                  {({ checked }) => (\n                    <span\n                      className={classNames(\n                        'inline-block h-5 w-5 transform rounded-full bg-white',\n                        checked ? 'translate-x-5' : 'translate-x-0'\n                      )}\n                    />\n                  )}\n                </Switch>\n              </Switch.Group>\n\n              <Switch.Group as=\"div\" className=\"flex items-center justify-between space-x-4\">\n                <Switch.Label>Banana</Switch.Label>\n                <Switch\n                  name=\"fruit[]\"\n                  value=\"banana\"\n                  className={({ checked }) =>\n                    classNames(\n                      'focus:outline-hidden relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',\n                      checked ? 'bg-blue-600' : 'bg-gray-200'\n                    )\n                  }\n                >\n                  {({ checked }) => (\n                    <span\n                      className={classNames(\n                        'inline-block h-5 w-5 transform rounded-full bg-white',\n                        checked ? 'translate-x-5' : 'translate-x-0'\n                      )}\n                    />\n                  )}\n                </Switch>\n              </Switch.Group>\n            </Section>\n          </Section>\n          <Section title=\"Radio Group\">\n            <RadioGroup defaultValue=\"sm\" name=\"size\">\n              <div className=\"flex -space-x-px rounded-md bg-white\">\n                {sizes.map((size) => {\n                  return (\n                    <RadioGroup.Option\n                      key={size}\n                      value={size}\n                      className={({ active }) =>\n                        classNames(\n                          'focus:outline-hidden relative flex w-20 border px-2 py-4 first:rounded-l-md last:rounded-r-md focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',\n                          active ? 'z-10 border-blue-200 bg-blue-50' : 'border-gray-200'\n                        )\n                      }\n                    >\n                      {({ active, checked }) => (\n                        <div className=\"flex w-full items-center justify-between\">\n                          <div className=\"ml-3 flex cursor-pointer flex-col\">\n                            <span\n                              className={classNames(\n                                'block text-sm font-medium leading-5',\n                                active ? 'text-blue-900' : 'text-gray-900'\n                              )}\n                            >\n                              {size}\n                            </span>\n                          </div>\n                          <div>\n                            {checked && (\n                              <svg\n                                xmlns=\"http://www.w3.org/2000/svg\"\n                                fill=\"none\"\n                                viewBox=\"0 0 24 24\"\n                                stroke=\"currentColor\"\n                                className=\"h-5 w-5 text-blue-500\"\n                              >\n                                <path\n                                  strokeLinecap=\"round\"\n                                  strokeLinejoin=\"round\"\n                                  strokeWidth={2}\n                                  d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n                                />\n                              </svg>\n                            )}\n                          </div>\n                        </div>\n                      )}\n                    </RadioGroup.Option>\n                  )\n                })}\n              </div>\n            </RadioGroup>\n          </Section>\n          <Section title=\"Listbox\">\n            <div className=\"w-full space-y-1\">\n              <Field>\n                <Label>Assigned to:</Label>\n                <Listbox name=\"person\" defaultValue={people[1]}>\n                  {({ value }) => (\n                    <>\n                      <div className=\"relative\">\n                        <Listbox.Button as={Button} className=\"w-full\">\n                          <span className=\"block truncate\">{value?.name?.first}</span>\n                          <span className=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                            <svg\n                              className=\"h-5 w-5 text-gray-400\"\n                              viewBox=\"0 0 20 20\"\n                              fill=\"none\"\n                              stroke=\"currentColor\"\n                            >\n                              <path\n                                d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                                strokeWidth=\"1.5\"\n                                strokeLinecap=\"round\"\n                                strokeLinejoin=\"round\"\n                              />\n                            </svg>\n                          </span>\n                        </Listbox.Button>\n\n                        <div className=\"absolute z-10 mt-1 w-full rounded-md bg-white shadow-lg\">\n                          <Listbox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                            {people.map((person) => (\n                              <Listbox.Option\n                                key={person.id}\n                                value={person}\n                                className={({ active }) => {\n                                  return classNames(\n                                    'relative cursor-default select-none py-2 pl-3 pr-9',\n                                    active ? 'bg-blue-600 text-white' : 'text-gray-900'\n                                  )\n                                }}\n                              >\n                                {({ active, selected }) => (\n                                  <>\n                                    <span\n                                      className={classNames(\n                                        'block truncate',\n                                        selected ? 'font-semibold' : 'font-normal'\n                                      )}\n                                    >\n                                      {person.name.first}\n                                    </span>\n                                    {selected && (\n                                      <span\n                                        className={classNames(\n                                          'absolute inset-y-0 right-0 flex items-center pr-4',\n                                          active ? 'text-white' : 'text-blue-600'\n                                        )}\n                                      >\n                                        <svg\n                                          className=\"h-5 w-5\"\n                                          viewBox=\"0 0 20 20\"\n                                          fill=\"currentColor\"\n                                        >\n                                          <path\n                                            fillRule=\"evenodd\"\n                                            d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                            clipRule=\"evenodd\"\n                                          />\n                                        </svg>\n                                      </span>\n                                    )}\n                                  </>\n                                )}\n                              </Listbox.Option>\n                            ))}\n                          </Listbox.Options>\n                        </div>\n                      </div>\n                    </>\n                  )}\n                </Listbox>\n              </Field>\n            </div>\n          </Section>\n          <Section title=\"Combobox\">\n            <div className=\"w-full space-y-1\">\n              <Field>\n                <Label>Location:</Label>\n                <Combobox\n                  name=\"location\"\n                  defaultValue={'New York'}\n                  onChange={(location) => {\n                    setQuery('')\n                  }}\n                >\n                  {({ open, value }) => {\n                    return (\n                      <div className=\"relative\">\n                        <div className=\"flex w-full flex-col\">\n                          <Combobox.Input\n                            onChange={(e) => setQuery(e.target.value)}\n                            className=\"shadow-xs focus:outline-hidden w-full rounded-md rounded-sm border-gray-300 bg-clip-padding px-3 py-1 focus:border-gray-300 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n                            placeholder=\"Search users...\"\n                          />\n                          <div\n                            className={classNames(\n                              'flex border-t',\n                              value && !open ? 'border-transparent' : 'border-gray-200'\n                            )}\n                          >\n                            <div className=\"absolute z-10 mt-1 w-full rounded-md bg-white shadow-lg\">\n                              <Combobox.Options className=\"shadow-2xs max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                                {locations\n                                  .filter((location) =>\n                                    location.toLowerCase().includes(query.toLowerCase())\n                                  )\n                                  .map((location) => (\n                                    <Combobox.Option\n                                      key={location}\n                                      value={location}\n                                      className={({ active }) => {\n                                        return classNames(\n                                          'relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9',\n                                          active ? 'bg-blue-600 text-white' : 'text-gray-900'\n                                        )\n                                      }}\n                                    >\n                                      {({ active, selected }) => (\n                                        <>\n                                          <span\n                                            className={classNames(\n                                              'block truncate',\n                                              selected ? 'font-semibold' : 'font-normal'\n                                            )}\n                                          >\n                                            {location}\n                                          </span>\n                                          {active && (\n                                            <span\n                                              className={classNames(\n                                                'absolute inset-y-0 right-0 flex items-center pr-4',\n                                                active ? 'text-white' : 'text-blue-600'\n                                              )}\n                                            >\n                                              <svg\n                                                className=\"h-5 w-5\"\n                                                viewBox=\"0 0 25 24\"\n                                                fill=\"none\"\n                                              >\n                                                <path\n                                                  d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                                                  stroke=\"currentColor\"\n                                                  strokeWidth=\"1.5\"\n                                                  strokeLinecap=\"round\"\n                                                  strokeLinejoin=\"round\"\n                                                />\n                                              </svg>\n                                            </span>\n                                          )}\n                                        </>\n                                      )}\n                                    </Combobox.Option>\n                                  ))}\n                              </Combobox.Options>\n                            </div>\n                          </div>\n                        </div>\n                      </div>\n                    )\n                  }}\n                </Combobox>\n              </Field>\n            </div>\n          </Section>\n          <Section title=\"Default form controls\">\n            <Field className=\"flex flex-col p-1\">\n              <Label>Label for {'<Input type=\"text\">'}</Label>\n              <Input type=\"text\" />\n            </Field>\n            <Field className=\"flex flex-col p-1\">\n              <Label>\n                I agree to the{' '}\n                <a href=\"https://google.com\" target=\"_blank\" className=\"underline\">\n                  terms and conditions\n                </a>\n              </Label>\n              <Input type=\"checkbox\" />\n            </Field>\n            <Field className=\"flex flex-col p-1\">\n              <Label>Label for {'<Input type=\"radio\">'}</Label>\n              <Input type=\"radio\" />\n            </Field>\n            <Field className=\"flex flex-col p-1\">\n              <Label>Label for {'<Input type=\"file\">'}</Label>\n              <Input type=\"file\" />\n            </Field>\n          </Section>\n        </div>\n\n        <div className=\"space-x-4\">\n          <button className=\"shadow-xs focus:outline-hidden rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 hover:text-gray-500 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5\">\n            Submit\n          </button>\n\n          <button\n            type=\"reset\"\n            className=\"shadow-xs focus:outline-hidden rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 hover:text-gray-500 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5\"\n          >\n            Reset\n          </button>\n        </div>\n\n        <div className=\"w-full border-t py-4\">\n          <span>Form data (entries):</span>\n          <pre className=\"text-sm\">{JSON.stringify([...result.entries()], null, 2)}</pre>\n        </div>\n      </form>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combinations/tabs-in-dialog.tsx",
    "content": "import { Dialog, Tab } from '@headlessui/react'\nimport { useState } from 'react'\nimport { Button } from '../../components/button'\n\nexport default function App() {\n  let [open, setOpen] = useState(false)\n\n  return (\n    <div className=\"p-12\">\n      <Button onClick={() => setOpen(true)}>Open dialog</Button>\n      <Dialog open={open} onClose={setOpen} className=\"fixed inset-0 grid place-content-center\">\n        <div className=\"fixed inset-0 bg-gray-500/70\" />\n        <Dialog.Panel className=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\">\n          <div className=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n            <Tab.Group>\n              <Tab.List className=\"flex gap-4 py-4\">\n                <Tab as={Button}>Tab 1</Tab>\n                <Tab as={Button}>Tab 2</Tab>\n                <Tab as={Button}>Tab 3</Tab>\n              </Tab.List>\n              <Tab.Panels>\n                <Tab.Panel className=\"px-3 py-2\">Panel 1</Tab.Panel>\n                <Tab.Panel className=\"px-3 py-2\">Panel 2</Tab.Panel>\n                <Tab.Panel className=\"px-3 py-2\">Panel 3</Tab.Panel>\n              </Tab.Panels>\n            </Tab.Group>\n          </div>\n        </Dialog.Panel>\n      </Dialog>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/combobox-countries.tsx",
    "content": "import { Combobox } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\n\nimport { Button } from '../../components/button'\nimport { countries as allCountries } from '../../data'\nimport { classNames } from '../../utils/class-names'\n\nfunction useDebounce<T>(value: T, delay: number) {\n  let [debouncedValue, setDebouncedValue] = useState(value)\n  useEffect(() => {\n    let timer = setTimeout(() => setDebouncedValue(value), delay)\n    return () => clearTimeout(timer)\n  }, [value, delay])\n  return debouncedValue\n}\nexport default function Home() {\n  let [query, setQuery] = useState('')\n  let [activeCountry, setActiveCountry] = useState(allCountries[2])\n\n  // Mimic delayed response from an API\n  let actualQuery = useDebounce(query, 0 /* Change to higher value like 100 for testing purposes */)\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActiveCountry(allCountries[Math.floor(Math.random() * allCountries.length)])\n  }, [])\n\n  let countries =\n    actualQuery === ''\n      ? allCountries\n      : allCountries.filter((person) => person.toLowerCase().includes(actualQuery.toLowerCase()))\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"py-8 font-mono text-xs\">Selected country: {activeCountry}</div>\n        <div className=\"space-y-1\">\n          <Combobox\n            value={activeCountry}\n            onChange={(value) => {\n              setActiveCountry(value)\n              setQuery('')\n            }}\n            as=\"div\"\n          >\n            <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Country\n            </Combobox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n                <Combobox.Input\n                  onChange={(e) => setQuery(e.target.value)}\n                  className=\"outline-hidden border-none px-3 py-1\"\n                />\n                <Combobox.Button as={Button}>\n                  <span className=\"pointer-events-none flex items-center px-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </Combobox.Button>\n              </span>\n\n              <Combobox.Options\n                transition\n                anchor=\"bottom start\"\n                className=\"focus:outline-hidden data-closed:opacity-0 w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5\"\n              >\n                {countries.map((country) => (\n                  <Combobox.Option\n                    key={country}\n                    value={country}\n                    className={({ active }) => {\n                      return classNames(\n                        'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                        active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                      )\n                    }}\n                  >\n                    {({ active, selected }) => (\n                      <>\n                        <span\n                          className={classNames(\n                            'block truncate',\n                            selected ? 'font-semibold' : 'font-normal'\n                          )}\n                        >\n                          {country}\n                        </span>\n                        {selected && (\n                          <span\n                            className={classNames(\n                              'absolute inset-y-0 right-0 flex items-center pr-4',\n                              active ? 'text-white' : 'text-indigo-600'\n                            )}\n                          >\n                            <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                              <path\n                                fillRule=\"evenodd\"\n                                d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                clipRule=\"evenodd\"\n                              />\n                            </svg>\n                          </span>\n                        )}\n                      </>\n                    )}\n                  </Combobox.Option>\n                ))}\n              </Combobox.Options>\n            </div>\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/combobox-open-on-focus.tsx",
    "content": "// @ts-nocheck\nimport { Combobox } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nlet everybody = [\n  'Wade Cooper',\n  'Arlene Mccoy',\n  'Devon Webb',\n  'Tom Cook',\n  'Tanya Fox',\n  'Hellen Schmidt',\n  'Caroline Schultz',\n  'Mason Heaney',\n  'Claudie Smitham',\n  'Emil Schaefer',\n]\n\nfunction useDebounce<T>(value: T, delay: number) {\n  let [debouncedValue, setDebouncedValue] = useState(value)\n  useEffect(() => {\n    let timer = setTimeout(() => setDebouncedValue(value), delay)\n    return () => clearTimeout(timer)\n  }, [value, delay])\n  return debouncedValue\n}\nexport default function Home() {\n  let [query, setQuery] = useState('')\n  let [activePerson, setActivePerson] = useState(everybody[2])\n\n  // Mimic delayed response from an API\n  let actualQuery = useDebounce(query, 0 /* Change to higher value like 100 for testing purposes */)\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActivePerson(everybody[Math.floor(Math.random() * everybody.length)])\n  }, [])\n\n  let people =\n    actualQuery === ''\n      ? everybody\n      : everybody.filter((person) => person.toLowerCase().includes(actualQuery.toLowerCase()))\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"py-8 font-mono text-xs\">Selected person: {activePerson}</div>\n        <div className=\"space-y-1\">\n          <Combobox\n            value={activePerson}\n            onChange={(value) => {\n              setActivePerson(value)\n              setQuery('')\n            }}\n            immediate\n            as=\"div\"\n          >\n            <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </Combobox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n                <Combobox.Input\n                  onChange={(e) => setQuery(e.target.value)}\n                  className=\"outline-hidden border-none px-3 py-1\"\n                />\n                <Combobox.Button as={Button}>\n                  <span className=\"pointer-events-none flex items-center px-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </Combobox.Button>\n              </span>\n\n              <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <Combobox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                  {people.map((name) => (\n                    <Combobox.Option\n                      key={name}\n                      value={name}\n                      className={({ active }) => {\n                        return classNames(\n                          'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                          active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                        )\n                      }}\n                    >\n                      {({ active, selected }) => (\n                        <>\n                          <span\n                            className={classNames(\n                              'block truncate',\n                              selected ? 'font-semibold' : 'font-normal'\n                            )}\n                          >\n                            {name}\n                          </span>\n                          {selected && (\n                            <span\n                              className={classNames(\n                                'absolute inset-y-0 right-0 flex items-center pr-4',\n                                active ? 'text-white' : 'text-indigo-600'\n                              )}\n                            >\n                              <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                <path\n                                  fillRule=\"evenodd\"\n                                  d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                  clipRule=\"evenodd\"\n                                />\n                              </svg>\n                            </span>\n                          )}\n                        </>\n                      )}\n                    </Combobox.Option>\n                  ))}\n                </Combobox.Options>\n              </div>\n            </div>\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/combobox-virtual-with-empty-states.tsx",
    "content": "// @ts-nocheck\nimport { Combobox } from '@headlessui/react'\nimport { useRef, useState } from 'react'\nimport { classNames } from '../../utils/class-names'\n\nimport { Button } from '../../components/button'\n\ntype Option = {\n  name: string\n  disabled: boolean\n  empty?: boolean\n}\n\nexport default function Home() {\n  let [list, setList] = useState<Option[]>(() => [\n    { name: 'Alice', disabled: false },\n    { name: 'Bob', disabled: false },\n    { name: 'Charlie', disabled: false },\n    { name: 'David', disabled: false },\n    { name: 'Eve', disabled: false },\n    { name: 'Fred', disabled: false },\n    { name: 'George', disabled: false },\n    { name: 'Helen', disabled: false },\n    { name: 'Iris', disabled: false },\n    { name: 'John', disabled: false },\n    { name: 'Kate', disabled: false },\n    { name: 'Linda', disabled: false },\n    { name: 'Michael', disabled: false },\n    { name: 'Nancy', disabled: false },\n    { name: 'Oscar', disabled: true },\n    { name: 'Peter', disabled: false },\n    { name: 'Quentin', disabled: false },\n    { name: 'Robert', disabled: false },\n    { name: 'Sarah', disabled: false },\n    { name: 'Thomas', disabled: false },\n    { name: 'Ursula', disabled: false },\n    { name: 'Victor', disabled: false },\n    { name: 'Wendy', disabled: false },\n    { name: 'Xavier', disabled: false },\n    { name: 'Yvonne', disabled: false },\n    { name: 'Zachary', disabled: false },\n  ])\n\n  let emptyOption = useRef({ name: 'No results', disabled: true, empty: true })\n\n  let [query, setQuery] = useState('')\n  let [selectedPerson, setSelectedPerson] = useState<Option | null>(list[0])\n  let optionsRef = useRef<HTMLUListElement | null>(null)\n\n  let filtered =\n    query === ''\n      ? list\n      : list.filter((item) => item.name.toLowerCase().includes(query.toLowerCase()))\n\n  return (\n    <div className=\"mx-auto max-w-fit\">\n      <div className=\"py-8 font-mono text-xs\">Selected person: {selectedPerson?.name ?? 'N/A'}</div>\n      <Combobox\n        virtual={{\n          options: filtered.length > 0 ? filtered : [emptyOption.current],\n          disabled: (option) => option.disabled || option.empty,\n        }}\n        value={selectedPerson}\n        nullable\n        onChange={(value) => {\n          setSelectedPerson(value)\n          setQuery('')\n        }}\n        as=\"div\"\n        // Don't do this lol — it's not supported\n        // It's just so we can tab to the \"Add\" button for the demo\n        // The combobox doesn't actually support this behavior\n        onKeyDownCapture={(event: KeyboardEvent) => {\n          let addButton = document.querySelector('#add_person') as HTMLElement | null\n          if (event.key === 'Tab' && addButton && filtered.length === 0) {\n            event.preventDefault()\n            setTimeout(() => addButton.focus(), 0)\n          }\n        }}\n      >\n        <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n          Person\n        </Combobox.Label>\n\n        <div className=\"relative\">\n          <span className=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n            <Combobox.Input\n              onChange={(e) => setQuery(e.target.value)}\n              displayValue={(option: Option | null) => option?.name ?? ''}\n              className=\"outline-hidden border-none px-3 py-1\"\n            />\n            <Combobox.Button as={Button}>\n              <span className=\"pointer-events-none flex items-center px-2\">\n                <svg\n                  className=\"h-5 w-5 text-gray-400\"\n                  viewBox=\"0 0 20 20\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                >\n                  <path\n                    d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                    strokeWidth=\"1.5\"\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                  />\n                </svg>\n              </span>\n            </Combobox.Button>\n          </span>\n\n          <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n            <Combobox.Options\n              // This is a hack to make keep the options list around when it's empty\n              // It comes with some caveats:\n              // like the option callback being called with a null option (which is probably a bug)\n              static={filtered.length === 0}\n              ref={optionsRef}\n              className={classNames(\n                'shadow-2xs focus:outline-hidden max-h-60 rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5',\n                filtered.length === 0 ? 'overflow-hidden' : 'overflow-auto'\n              )}\n            >\n              {\n                // @ts-expect-error TODO: Properly handle this\n                ({ option }: { option: Option }) => {\n                  if (!option || option.empty) {\n                    return (\n                      <Combobox.Option\n                        // TODO: `disabled` being required is a bug\n                        disabled\n                        // Note: Do NOT use `null` for the `value`\n                        value={option ?? emptyOption.current}\n                        className=\"focus:outline-hidden relative w-full cursor-default select-none px-3 py-2 text-center\"\n                      >\n                        <div className=\"relative grid h-full grid-cols-1 grid-rows-1\">\n                          <div className=\"absolute inset-0\">\n                            <svg\n                              fill=\"none\"\n                              viewBox=\"0 0 24 24\"\n                              strokeWidth={0.5}\n                              stroke=\"currentColor\"\n                              className=\"-translate-y-1/4 text-gray-500/5\"\n                            >\n                              <path\n                                strokeLinecap=\"round\"\n                                strokeLinejoin=\"round\"\n                                d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z\"\n                              />\n                            </svg>\n                          </div>\n                          <div className=\"z-20 col-span-full col-start-1 row-span-full row-start-1 flex flex-col items-center justify-center p-8\">\n                            <h3 className=\"mx-2 mb-4 text-xl font-semibold text-gray-400\">\n                              No people found\n                            </h3>\n                            <button\n                              id=\"add_person\"\n                              type=\"button\"\n                              className=\"focus:outline-hidden rounded-sm bg-blue-500 px-4 py-2 font-semibold text-white hover:bg-blue-600 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n                              onClick={() => {\n                                let person = { name: query, disabled: false }\n                                setList((list) => [...list, person])\n                                setSelectedPerson(person)\n                              }}\n                            >\n                              Add \"{query}\"\n                            </button>\n                          </div>\n                        </div>\n                      </Combobox.Option>\n                    )\n                  }\n\n                  return (\n                    <Combobox.Option\n                      // TODO: `disabled` being required is a bug\n                      disabled={option.disabled}\n                      value={option}\n                      className={({ active }) => {\n                        return classNames(\n                          'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                          active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                        )\n                      }}\n                    >\n                      <span className=\"block truncate\">{option.name}</span>\n                    </Combobox.Option>\n                  )\n                }\n              }\n            </Combobox.Options>\n          </div>\n        </div>\n      </Combobox>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/combobox-virtualized.tsx",
    "content": "// @ts-nocheck\nimport { Combobox } from '@headlessui/react'\nimport { useMemo, useState } from 'react'\n\nimport { Button } from '../../components/button'\nimport { timezones as _allTimezones } from '../../data'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let [count, setCount] = useState(1_000)\n\n  let list = useMemo(() => {\n    console.time('Generating list')\n    let result = []\n\n    while (result.length < count) {\n      let batch = Math.floor(result.length / _allTimezones.length) + 1\n      result.push(`${_allTimezones[result.length % _allTimezones.length]} #${batch}`)\n    }\n    console.timeEnd('Generating list')\n\n    return result\n  }, [count])\n\n  return (\n    <div className=\"flex flex-col p-12\">\n      <label className=\"mx-auto flex w-24 items-center gap-2\">\n        <span>Items:</span>\n        <select\n          defaultValue={count}\n          className=\"mx-auto\"\n          onChange={(e) => {\n            setCount(Number(e.target.value))\n          }}\n        >\n          <option value={100}>100</option>\n          <option value={1_000}>1000</option>\n          <option value={10_000}>10k</option>\n          <option value={100_000}>100k</option>\n        </select>\n      </label>\n\n      <div className=\"flex\">\n        <Example data={list} virtual={true} initial=\"Europe/Brussels #1\" />\n        <Example data={list} virtual={false} initial=\"Europe/Brussels #1\" />\n      </div>\n    </div>\n  )\n}\n\nlet nf = new Intl.NumberFormat('en-US')\nfunction Example({ virtual = true, data, initial }: { virtual?: boolean; data; initial: string }) {\n  let [query, setQuery] = useState('')\n  let [activeTimezone, setActiveTimezone] = useState(initial)\n\n  let timezones =\n    query === ''\n      ? data\n      : data.filter((timezone) => timezone.toLowerCase().includes(query.toLowerCase()))\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"py-8 font-mono text-xs\">Selected timezone: {activeTimezone}</div>\n        <div className=\"space-y-1\">\n          <Combobox\n            virtual={virtual ? { options: timezones } : undefined}\n            value={activeTimezone}\n            onChange={(value) => {\n              setActiveTimezone(value)\n              setQuery('')\n            }}\n            as=\"div\"\n          >\n            <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Timezone{' '}\n              {virtual\n                ? `(virtual — ${nf.format(timezones.length)} items)`\n                : `(${nf.format(timezones.length)} items)`}\n            </Combobox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n                <Combobox.Input\n                  onChange={(e) => setQuery(e.target.value)}\n                  className=\"outline-hidden border-none px-3 py-1\"\n                />\n                <Combobox.Button as={Button}>\n                  <span className=\"pointer-events-none flex items-center px-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </Combobox.Button>\n              </span>\n\n              {virtual ? (\n                <Combobox.Options\n                  transition\n                  anchor=\"bottom start\"\n                  className=\"focus:outline-hidden data-closed:opacity-0 w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5\"\n                >\n                  {({ option }) => {\n                    return (\n                      <Combobox.Option\n                        value={option}\n                        className={({ active }) => {\n                          return classNames(\n                            'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                            active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                          )\n                        }}\n                      >\n                        {({ active, selected }) => (\n                          <>\n                            <span\n                              className={classNames(\n                                'block truncate',\n                                selected ? 'font-semibold' : 'font-normal'\n                              )}\n                            >\n                              {option as any}\n                            </span>\n                            {selected && (\n                              <span\n                                className={classNames(\n                                  'absolute inset-y-0 right-0 flex items-center pr-4',\n                                  active ? 'text-white' : 'text-indigo-600'\n                                )}\n                              >\n                                <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                  <path\n                                    fillRule=\"evenodd\"\n                                    d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                    clipRule=\"evenodd\"\n                                  />\n                                </svg>\n                              </span>\n                            )}\n                          </>\n                        )}\n                      </Combobox.Option>\n                    )\n                  }}\n                </Combobox.Options>\n              ) : (\n                <Combobox.Options\n                  transition\n                  anchor=\"bottom start\"\n                  className=\"focus:outline-hidden data-closed:opacity-0 w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5\"\n                >\n                  {timezones.map((timezone, idx) => {\n                    return (\n                      <Combobox.Option\n                        key={timezone}\n                        order={virtual ? idx : undefined}\n                        value={timezone}\n                        className={({ active }) => {\n                          return classNames(\n                            'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                            active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                          )\n                        }}\n                      >\n                        {({ active, selected }) => (\n                          <>\n                            <span\n                              className={classNames(\n                                'block truncate',\n                                selected ? 'font-semibold' : 'font-normal'\n                              )}\n                            >\n                              {timezone}\n                            </span>\n                            {selected && (\n                              <span\n                                className={classNames(\n                                  'absolute inset-y-0 right-0 flex items-center pr-4',\n                                  active ? 'text-white' : 'text-indigo-600'\n                                )}\n                              >\n                                <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                  <path\n                                    fillRule=\"evenodd\"\n                                    d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                    clipRule=\"evenodd\"\n                                  />\n                                </svg>\n                              </span>\n                            )}\n                          </>\n                        )}\n                      </Combobox.Option>\n                    )\n                  })}\n                </Combobox.Options>\n              )}\n            </div>\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/combobox-with-pure-tailwind.tsx",
    "content": "import { Combobox } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nlet everybody = [\n  'Wade Cooper',\n  'Arlene Mccoy',\n  'Devon Webb',\n  'Tom Cook',\n  'Tanya Fox',\n  'Hellen Schmidt',\n  'Caroline Schultz',\n  'Mason Heaney',\n  'Claudie Smitham',\n  'Emil Schaefer',\n]\n\nfunction useDebounce<T>(value: T, delay: number) {\n  let [debouncedValue, setDebouncedValue] = useState(value)\n  useEffect(() => {\n    let timer = setTimeout(() => setDebouncedValue(value), delay)\n    return () => clearTimeout(timer)\n  }, [value, delay])\n  return debouncedValue\n}\nexport default function Home() {\n  let [query, setQuery] = useState('')\n  let [activePerson, setActivePerson] = useState(everybody[2])\n\n  // Mimic delayed response from an API\n  let actualQuery = useDebounce(query, 0 /* Change to higher value like 100 for testing purposes */)\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActivePerson(everybody[Math.floor(Math.random() * everybody.length)])\n  }, [])\n\n  let people =\n    actualQuery === ''\n      ? everybody\n      : everybody.filter((person) => person.toLowerCase().includes(actualQuery.toLowerCase()))\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"py-8 font-mono text-xs\">Selected person: {activePerson}</div>\n        <div className=\"space-y-1\">\n          <Combobox\n            value={activePerson}\n            onChange={(value) => {\n              setActivePerson(value)\n              setQuery('')\n            }}\n            as=\"div\"\n          >\n            <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </Combobox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n                <Combobox.Input\n                  onChange={(e) => setQuery(e.target.value)}\n                  className=\"outline-hidden border-none px-3 py-1\"\n                />\n                <Combobox.Button as={Button}>\n                  <span className=\"pointer-events-none flex items-center px-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </Combobox.Button>\n              </span>\n\n              <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <Combobox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                  {people.map((name) => (\n                    <Combobox.Option\n                      key={name}\n                      value={name}\n                      className={({ active }) => {\n                        return classNames(\n                          'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                          active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                        )\n                      }}\n                    >\n                      {({ active, selected }) => (\n                        <>\n                          <span\n                            className={classNames(\n                              'block truncate',\n                              selected ? 'font-semibold' : 'font-normal'\n                            )}\n                          >\n                            {name}\n                          </span>\n                          {selected && (\n                            <span\n                              className={classNames(\n                                'absolute inset-y-0 right-0 flex items-center pr-4',\n                                active ? 'text-white' : 'text-indigo-600'\n                              )}\n                            >\n                              <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                <path\n                                  fillRule=\"evenodd\"\n                                  d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                  clipRule=\"evenodd\"\n                                />\n                              </svg>\n                            </span>\n                          )}\n                        </>\n                      )}\n                    </Combobox.Option>\n                  ))}\n                </Combobox.Options>\n              </div>\n            </div>\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/command-palette-with-groups.tsx",
    "content": "import { Combobox } from '@headlessui/react'\nimport { Fragment, useEffect, useState } from 'react'\n\nimport { classNames } from '../../utils/class-names'\n\nlet everybody = [\n  { id: 1, img: 'https://github.com/adamwathan.png', name: 'Adam Wathan' },\n  { id: 2, img: 'https://github.com/sschoger.png', name: 'Steve Schoger' },\n  { id: 3, img: 'https://github.com/bradlc.png', name: 'Brad Cornes' },\n  { id: 4, img: 'https://github.com/simonswiss.png', name: 'Simon Vrachliotis' },\n  { id: 5, img: 'https://github.com/robinmalfait.png', name: 'Robin Malfait' },\n  {\n    id: 6,\n    img: 'https://pbs.twimg.com/profile_images/1478879681491394569/eV2PyCnm_400x400.jpg',\n    name: 'James McDonald',\n  },\n  { id: 7, img: 'https://github.com/reinink.png', name: 'Jonathan Reinink' },\n  { id: 8, img: 'https://github.com/thecrypticace.png', name: 'Jordan Pittman' },\n]\n\nexport default function Home() {\n  let [query, setQuery] = useState('')\n  let [activePerson, setActivePerson] = useState(everybody[2])\n\n  function setPerson(person) {\n    setActivePerson(person)\n    setQuery(person.name ?? '')\n  }\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setPerson(everybody[Math.floor(Math.random() * everybody.length)])\n  }, [])\n\n  let people =\n    query === ''\n      ? everybody\n      : everybody.filter((person) => person.name.toLowerCase().includes(query.toLowerCase()))\n\n  let groups = people.reduce((groups, person) => {\n    let lastNameLetter = person.name.split(' ')[1][0]\n\n    groups.set(lastNameLetter, [...(groups.get(lastNameLetter) || []), person])\n\n    return groups\n  }, new Map())\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-lg\">\n        <div className=\"space-y-1\">\n          <Combobox\n            as=\"div\"\n            value={activePerson}\n            onChange={(person) => setPerson(person)}\n            className=\"shadow-xs w-full overflow-hidden rounded-sm border border-black/5 bg-white bg-clip-padding\"\n          >\n            {({ activeOption }) => {\n              return (\n                <div className=\"flex w-full flex-col\">\n                  <Combobox.Input\n                    onChange={(e) => setQuery(e.target.value)}\n                    className=\"outline-hidden w-full rounded-none border-none bg-none px-3 py-1\"\n                    placeholder=\"Search users…\"\n                    displayValue={(item: typeof activeOption) => item?.name}\n                  />\n                  <div className=\"flex\">\n                    <Combobox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 flex-1 overflow-auto text-base leading-6 sm:text-sm sm:leading-5\">\n                      {Array.from(groups.entries())\n                        .sort(([letterA], [letterZ]) => letterA.localeCompare(letterZ))\n                        .map(([letter, people]) => (\n                          <Fragment key={letter}>\n                            <div className=\"bg-gray-100 px-4 py-2\">{letter}</div>\n                            {people.map((person) => (\n                              <Combobox.Option\n                                key={person.id}\n                                value={person}\n                                className={({ active }) => {\n                                  return classNames(\n                                    'focus:outline-hidden relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9',\n                                    active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                                  )\n                                }}\n                              >\n                                {({ active, selected }) => (\n                                  <>\n                                    <img\n                                      src={person.img}\n                                      className=\"h-6 w-6 overflow-hidden rounded-full\"\n                                    />\n                                    <span\n                                      className={classNames(\n                                        'block truncate',\n                                        selected ? 'font-semibold' : 'font-normal'\n                                      )}\n                                    >\n                                      {person.name}\n                                    </span>\n                                    {active && (\n                                      <span\n                                        className={classNames(\n                                          'absolute inset-y-0 right-0 flex items-center pr-4',\n                                          active ? 'text-white' : 'text-indigo-600'\n                                        )}\n                                      >\n                                        <svg className=\"h-5 w-5\" viewBox=\"0 0 25 24\" fill=\"none\">\n                                          <path\n                                            d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                                            stroke=\"currentColor\"\n                                            strokeWidth=\"1.5\"\n                                            strokeLinecap=\"round\"\n                                            strokeLinejoin=\"round\"\n                                          />\n                                        </svg>\n                                      </span>\n                                    )}\n                                  </>\n                                )}\n                              </Combobox.Option>\n                            ))}\n                          </Fragment>\n                        ))}\n                    </Combobox.Options>\n\n                    {people.length === 0 ? (\n                      <div className=\"w-full py-4 text-center\">No person selected</div>\n                    ) : activeOption === null ? null : (\n                      <div className=\"border-l\">\n                        <div className=\"flex flex-col\">\n                          <div className=\"p-8 text-center\">\n                            <img\n                              src={activeOption.img}\n                              className=\"mb-4 inline-block h-16 w-16 overflow-hidden rounded-full\"\n                            />\n                            <div className=\"font-bold text-gray-900\">{activeOption.name}</div>\n                            <div className=\"text-gray-700\">Obviously cool person</div>\n                          </div>\n                        </div>\n                      </div>\n                    )}\n                  </div>\n                </div>\n              )\n            }}\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/command-palette.tsx",
    "content": "import { Combobox } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\n\nimport { classNames } from '../../utils/class-names'\n\nlet everybody = [\n  { id: 1, img: 'https://github.com/adamwathan.png', name: 'Adam Wathan' },\n  { id: 2, img: 'https://github.com/sschoger.png', name: 'Steve Schoger' },\n  { id: 3, img: 'https://github.com/bradlc.png', name: 'Brad Cornes' },\n  { id: 4, img: 'https://github.com/simonswiss.png', name: 'Simon Vrachliotis' },\n  { id: 5, img: 'https://github.com/robinmalfait.png', name: 'Robin Malfait' },\n  {\n    id: 6,\n    img: 'https://pbs.twimg.com/profile_images/1478879681491394569/eV2PyCnm_400x400.jpg',\n    name: 'James McDonald',\n  },\n  { id: 7, img: 'https://github.com/reinink.png', name: 'Jonathan Reinink' },\n  { id: 8, img: 'https://github.com/thecrypticace.png', name: 'Jordan Pittman' },\n]\n\nexport default function Home() {\n  let [query, setQuery] = useState('')\n  let [activePerson, setActivePerson] = useState(everybody[2])\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActivePerson(everybody[Math.floor(Math.random() * everybody.length)])\n  }, [])\n\n  let people =\n    query === ''\n      ? everybody\n      : everybody.filter((person) => person.name.toLowerCase().includes(query.toLowerCase()))\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-lg\">\n        <div className=\"space-y-1\">\n          <Combobox\n            as=\"div\"\n            value={activePerson}\n            onChange={(person) => setActivePerson(person)}\n            className=\"shadow-xs w-full overflow-hidden rounded-sm border border-black/5 bg-white bg-clip-padding\"\n          >\n            {({ activeOption, open }) => {\n              return (\n                <div className=\"flex w-full flex-col\">\n                  <Combobox.Input\n                    onChange={(e) => setQuery(e.target.value)}\n                    className=\"outline-hidden w-full rounded-none border-none px-3 py-1\"\n                    placeholder=\"Search users…\"\n                    displayValue={(item: typeof activePerson) => item?.name}\n                  />\n                  <div\n                    className={classNames(\n                      'flex border-t',\n                      activePerson && !open ? 'border-transparent' : 'border-gray-200'\n                    )}\n                  >\n                    <Combobox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 flex-1 overflow-auto py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                      {people.map((person) => (\n                        <Combobox.Option\n                          key={person.id}\n                          value={person}\n                          className={({ active }) => {\n                            return classNames(\n                              'focus:outline-hidden relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9',\n                              active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                            )\n                          }}\n                        >\n                          {({ active, selected }) => (\n                            <>\n                              <img\n                                src={person.img}\n                                className=\"h-6 w-6 overflow-hidden rounded-full\"\n                              />\n                              <span\n                                className={classNames(\n                                  'block truncate',\n                                  selected ? 'font-semibold' : 'font-normal'\n                                )}\n                              >\n                                {person.name}\n                              </span>\n                              {active && (\n                                <span\n                                  className={classNames(\n                                    'absolute inset-y-0 right-0 flex items-center pr-4',\n                                    active ? 'text-white' : 'text-indigo-600'\n                                  )}\n                                >\n                                  <svg className=\"h-5 w-5\" viewBox=\"0 0 25 24\" fill=\"none\">\n                                    <path\n                                      d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                                      stroke=\"currentColor\"\n                                      strokeWidth=\"1.5\"\n                                      strokeLinecap=\"round\"\n                                      strokeLinejoin=\"round\"\n                                    />\n                                  </svg>\n                                </span>\n                              )}\n                            </>\n                          )}\n                        </Combobox.Option>\n                      ))}\n                    </Combobox.Options>\n\n                    {people.length === 0 ? (\n                      <div className=\"w-full py-4 text-center\">No person selected</div>\n                    ) : activeOption === null ? null : (\n                      <div className=\"border-l\">\n                        <div className=\"flex flex-col\">\n                          <div className=\"p-8 text-center\">\n                            <img\n                              src={activeOption.img}\n                              className=\"mb-4 inline-block h-16 w-16 overflow-hidden rounded-full\"\n                            />\n                            <div className=\"font-bold text-gray-900\">{activeOption.name}</div>\n                            <div className=\"text-gray-700\">Obviously cool person</div>\n                          </div>\n                        </div>\n                      </div>\n                    )}\n                  </div>\n                </div>\n              )\n            }}\n          </Combobox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/combobox/multi-select.tsx",
    "content": "import { Combobox } from '@headlessui/react'\nimport { useState } from 'react'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet people = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n      <MultiPeopleList />\n    </div>\n  )\n}\n\nfunction MultiPeopleList() {\n  let [query, setQuery] = useState('')\n  let [activePersons, setActivePersons] = useState([people[2], people[3]])\n\n  return (\n    <div className=\"w-full max-w-4xl\">\n      <div className=\"space-y-1\">\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            console.log([...new FormData(e.currentTarget).entries()])\n          }}\n        >\n          <Combobox\n            value={activePersons}\n            onChange={(people) => setActivePersons(people)}\n            name=\"people\"\n            multiple\n          >\n            <Combobox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </Combobox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs inline-block w-full rounded-md\">\n                <div className=\"focus-within:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus-within:border-blue-700 focus-within:ring-1 focus-within:ring-blue-700 sm:text-sm sm:leading-5\">\n                  <span className=\"block flex flex-wrap gap-2\">\n                    {activePersons.map((person) => (\n                      <span\n                        key={person.id}\n                        className=\"flex items-center gap-1 rounded-sm bg-blue-50 px-2 py-0.5\"\n                      >\n                        <span>{person.name}</span>\n                        <svg\n                          className=\"h-4 w-4 cursor-pointer\"\n                          fill=\"none\"\n                          stroke=\"currentColor\"\n                          viewBox=\"0 0 24 24\"\n                          xmlns=\"http://www.w3.org/2000/svg\"\n                          onClick={(e) => {\n                            e.stopPropagation()\n                            e.preventDefault()\n                            setActivePersons((existing) => existing.filter((p) => p !== person))\n                          }}\n                        >\n                          <path\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                            strokeWidth=\"2\"\n                            d=\"M6 18L18 6M6 6l12 12\"\n                          />\n                        </svg>\n                      </span>\n                    ))}\n                    <Combobox.Input\n                      onChange={(event) => setQuery(event.target.value)}\n                      onFocus={() => query != '' && setQuery('')}\n                      className=\"border-none p-0 focus:ring-0\"\n                      placeholder=\"Search...\"\n                    />\n                  </span>\n                  <Combobox.Button className=\"absolute inset-y-0 right-0 flex items-center pr-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </Combobox.Button>\n                </div>\n              </span>\n\n              <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <Combobox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                  {people\n                    .filter((person) => person.name.toLowerCase().includes(query.toLowerCase()))\n                    .map((person) => (\n                      <Combobox.Option\n                        key={person.id}\n                        value={person}\n                        className={({ active }) => {\n                          return classNames(\n                            'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                            active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                          )\n                        }}\n                      >\n                        {({ active, selected }) => (\n                          <>\n                            <span\n                              className={classNames(\n                                'block truncate',\n                                selected ? 'font-semibold' : 'font-normal'\n                              )}\n                            >\n                              {person.name}\n                            </span>\n                            {selected && (\n                              <span\n                                className={classNames(\n                                  'absolute inset-y-0 right-0 flex items-center pr-4',\n                                  active ? 'text-white' : 'text-indigo-600'\n                                )}\n                              >\n                                <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                  <path\n                                    fillRule=\"evenodd\"\n                                    d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                    clipRule=\"evenodd\"\n                                  />\n                                </svg>\n                              </span>\n                            )}\n                          </>\n                        )}\n                      </Combobox.Option>\n                    ))}\n                </Combobox.Options>\n              </div>\n            </div>\n          </Combobox>\n          <button className=\"shadow-xs focus:outline-hidden mt-2 inline-flex items-center rounded-sm border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2\">\n            Submit\n          </button>\n        </form>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/dialog-built-in-transition.tsx",
    "content": "import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'\nimport { useState } from 'react'\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(false)\n  let [transitionBackdrop, setTransitionBackdrop] = useState(true)\n  let [transitionPanel, setTransitionPanel] = useState(true)\n\n  return (\n    <>\n      <div className=\"flex gap-4 p-12\">\n        <Button onClick={() => setIsOpen((v) => !v)}>Toggle!</Button>\n        <Button onClick={() => setTransitionBackdrop((v) => !v)}>\n          <span>Toggle transition backdrop</span>\n          <span\n            className={classNames(\n              'ml-2 inline-flex size-4 rounded-md',\n              transitionBackdrop ? 'bg-green-500' : 'bg-red-500'\n            )}\n          ></span>\n        </Button>\n        <Button onClick={() => setTransitionPanel((v) => !v)}>\n          <span>Toggle transition panel</span>\n          <span\n            className={classNames(\n              'ml-2 inline-flex size-4 rounded-md',\n              transitionPanel ? 'bg-green-500' : 'bg-red-500'\n            )}\n          ></span>\n        </Button>\n      </div>\n\n      <Dialog\n        open={isOpen}\n        onClose={() => setIsOpen(false)}\n        className=\"data-closed:opacity-0 relative z-50 duration-500\"\n      >\n        <DialogBackdrop\n          transition={transitionBackdrop}\n          className=\"data-closed:opacity-0 fixed inset-0 bg-black/30 duration-500 ease-out\"\n        />\n        <div className=\"fixed inset-0 flex w-screen items-center justify-center p-4\">\n          <DialogPanel\n            transition={transitionPanel}\n            className=\"data-closed:scale-95 data-closed:opacity-0 w-full max-w-lg space-y-4 bg-white p-12 duration-500 ease-out\"\n          >\n            <h1 className=\"text-2xl font-bold\">Dialog</h1>\n            <p>\n              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar, nunc nec\n              vehicula fermentum, nunc sapien tristique ipsum, nec facilisis dolor sapien non dui.\n              Nullam vel sapien ultrices, lacinia felis sit amet, fermentum odio. Nullam vel sapien\n              ultrices, lacinia felis sit amet, fermentum odio.\n            </p>\n            <Button onClick={() => setIsOpen(false)}>Close</Button>\n          </DialogPanel>\n        </div>\n      </Dialog>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/dialog-focus-issue.tsx",
    "content": "import { Dialog } from '@headlessui/react'\nimport { useState } from 'react'\nimport { Button } from '../../components/button'\n\nfunction Modal(props) {\n  return (\n    <Dialog className=\"relative z-50\" {...props}>\n      <div className=\"fixed inset-0 bg-green-500 bg-opacity-90 backdrop-blur-sm backdrop-filter\" />\n      <div className=\"fixed inset-0 overflow-y-auto\">\n        <div className=\"flex min-h-full items-center justify-center p-4\">\n          <Dialog.Panel className=\"relative m-5 flex w-full max-w-3xl gap-4 rounded-lg bg-white p-10 shadow-sm\">\n            <Button>One</Button>\n            <Button>Two</Button>\n          </Dialog.Panel>\n        </div>\n      </div>\n    </Dialog>\n  )\n}\n\nexport default function DialogFocusIssue() {\n  let [isOpen, setIsOpen] = useState(false)\n\n  return (\n    <div className=\"p-10\">\n      <h1 className=\"py-2 text-3xl font-semibold\">Headless UI Focus Jump</h1>\n      <Button onClick={() => setIsOpen(true)}>Open Dialog</Button>\n      <div className=\"bg-white p-20\"></div>\n      <div className=\"bg-gray-100 p-20\"></div>\n      <div className=\"bg-gray-200 p-20\"></div>\n      <div className=\"bg-gray-300 p-20\"></div>\n      <div className=\"bg-gray-400 p-20\"></div>\n      <div className=\"bg-gray-500 p-20\"></div>\n      <div className=\"bg-gray-600 p-20\"></div>\n      <div className=\"bg-gray-700 p-20\"></div>\n      <div className=\"bg-gray-800 p-20\"></div>\n      <div className=\"bg-gray-900 p-20\"></div>\n      <div className=\"bg-black p-20\"></div>\n      <Modal open={isOpen} onClose={() => setIsOpen(false)} />\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/dialog-scroll-issue.tsx",
    "content": "import { Dialog, Transition } from '@headlessui/react'\nimport { useState } from 'react'\n\nfunction MyDialog({ open, close }) {\n  return (\n    <>\n      <Transition show={open}>\n        <Dialog onClose={close} className=\"relative z-50\">\n          <Transition.Child\n            enter=\"transition duration-500 ease-out\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-100\"\n            leave=\"transition duration-500 ease-out\"\n            leaveFrom=\"opacity-100\"\n            leaveTo=\"opacity-0\"\n          >\n            <div className=\"fixed bottom-0 left-0 top-0 flex items-center justify-center bg-red-500 p-4\">\n              <Dialog.Panel className=\"mx-auto w-48 rounded-sm bg-white p-4\">\n                <p className=\"my-2\">Gray area should be scrollable</p>\n\n                <p className=\"h-32 overflow-y-scroll border bg-gray-100\">\n                  Are you sure you want to deactivate your account? All of your data will be\n                  permanently removed. This action cannot be undone.\n                </p>\n\n                <p>Colored area on the right should not be scrollable</p>\n\n                <a\n                  href=\"#foo\"\n                  onClick={() => {\n                    setTimeout(() => {\n                      close()\n                    }, 2000)\n                  }}\n                >\n                  Click me to close dialog and scroll to Foo\n                </a>\n              </Dialog.Panel>\n            </div>\n          </Transition.Child>\n        </Dialog>\n      </Transition>\n    </>\n  )\n}\n\nexport default function App() {\n  let [isOpen, setIsOpen] = useState(false)\n\n  return (\n    <div>\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <MyDialog open={isOpen} close={() => setIsOpen(false)} />\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n      <a id=\"foo\" className=\"block w-full bg-pink-500 p-12\">\n        Hello from Foo!\n      </a>\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n      <button onClick={() => setIsOpen((v) => !v)}>Toggle dialog</button>\n      <div className=\"h-[50vh]\" />\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/dialog-with-shadow-children.tsx",
    "content": "import { Dialog } from '@headlessui/react'\nimport { useEffect, useRef, useState } from 'react'\nimport { Button } from '../../components/button'\n\nif (typeof document !== 'undefined') {\n  class MyCustomElement extends HTMLElement {\n    shadow: ShadowRoot\n\n    constructor() {\n      super()\n      this.shadow = this.attachShadow({ mode: 'closed' })\n    }\n\n    connectedCallback() {\n      let button = document.createElement('button')\n      button.textContent = 'Inside shadow-sm root (closed)'\n      this.shadow.appendChild(button)\n    }\n  }\n\n  customElements.define('my-custom-element', MyCustomElement)\n}\n\nfunction ShadowChildren({ id }: { id: string }) {\n  let container = useRef<HTMLDivElement | null>(null)\n\n  useEffect(() => {\n    if (!container.current || container.current.shadowRoot) {\n      return\n    }\n\n    let shadowRoot = container.current.attachShadow({ mode: 'open' })\n    let button = document.createElement('button')\n    button.id = id\n    button.style.display = 'block'\n    button.textContent = 'Inside shadow-sm root (open)'\n\n    let mce = document.createElement('my-custom-element')\n\n    shadowRoot.appendChild(button)\n    shadowRoot.appendChild(mce)\n  }, [])\n\n  return <div ref={container}></div>\n}\n\nexport default function App() {\n  const [open, setOpen] = useState(false)\n\n  return (\n    <div>\n      <Button onClick={() => setOpen(true)}>open</Button>\n      <Dialog open={open} onClose={() => setOpen(false)}>\n        <div className=\"fixed inset-0 z-50 bg-gray-900/75 backdrop-blur-lg\">\n          <div>\n            <button\n              className=\"m-4 rounded-sm border-0 bg-gray-500 px-3 py-1 font-medium text-white hover:bg-gray-600\"\n              id=\"btn_outside_light\"\n            >\n              Outside shadow root\n            </button>\n            <ShadowChildren id=\"btn_outside_shadow\" />\n          </div>\n        </div>\n        <Dialog.Panel className=\"fixed left-16 top-16 z-50 h-64 w-64 rounded-lg border border-black/10 bg-white bg-clip-padding p-12 shadow-lg\">\n          <div>\n            <button\n              className=\"m-4 rounded-sm border-0 bg-gray-500 px-3 py-1 font-medium text-white hover:bg-gray-600\"\n              id=\"btn_inside_light\"\n            >\n              Outside shadow root\n            </button>\n            <ShadowChildren id=\"btn_inside_shadow\" />\n          </div>\n        </Dialog.Panel>\n      </Dialog>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/dialog.tsx",
    "content": "import { Dialog, Menu, Portal, Transition } from '@headlessui/react'\nimport { useState } from 'react'\nimport Flatpickr from 'react-flatpickr'\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\nimport { usePopper } from '../../utils/hooks/use-popper'\n\nimport 'flatpickr/dist/themes/light.css'\n\nfunction resolveClass({ active, disabled }) {\n  return classNames(\n    'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n    active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n    disabled && 'cursor-not-allowed opacity-50'\n  )\n}\n\nfunction Nested({ onClose, level = 0 }) {\n  let [showChild, setShowChild] = useState(false)\n\n  return (\n    <>\n      <Dialog open={true} onClose={onClose} className=\"fixed inset-0 z-10\">\n        <div className=\"fixed inset-0 bg-gray-500 opacity-25\" />\n        <Dialog.Panel\n          className=\"fixed left-12 top-24 z-10 w-96 bg-white p-4\"\n          style={{\n            transform: `translate(calc(50px * ${level}), calc(50px * ${level}))`,\n          }}\n        >\n          <p>Level: {level}</p>\n          <div className=\"flex gap-4\">\n            <Button onClick={() => setShowChild(true)}>Open (1)</Button>\n            <Button onClick={() => setShowChild(true)}>Open (2)</Button>\n            <Button onClick={() => setShowChild(true)}>Open (3)</Button>\n          </div>\n        </Dialog.Panel>\n        {showChild && <Nested onClose={() => setShowChild(false)} level={level + 1} />}\n      </Dialog>\n    </>\n  )\n}\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(false)\n  let [nested, setNested] = useState(false)\n\n  let [trigger, container] = usePopper({\n    placement: 'bottom-end',\n    strategy: 'fixed',\n    modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n  })\n\n  let [date, setDate] = useState(new Date())\n\n  return (\n    <>\n      <div className=\"flex gap-4 p-12\">\n        <Button onClick={() => setIsOpen((v) => !v)}>Toggle!</Button>\n        <Button onClick={() => setNested(true)}>Show nested</Button>\n      </div>\n      {nested && <Nested onClose={() => setNested(false)} />}\n\n      <Transition\n        data-debug=\"Dialog\"\n        show={isOpen}\n        beforeEnter={() => console.log('[Transition] Before enter')}\n        afterEnter={() => console.log('[Transition] After enter')}\n        beforeLeave={() => console.log('[Transition] Before leave')}\n        afterLeave={() => console.log('[Transition] After leave')}\n      >\n        <Dialog\n          onClose={() => {\n            console.log('close')\n            setIsOpen(false)\n          }}\n        >\n          <div className=\"fixed inset-0 z-10 overflow-y-auto\">\n            <div className=\"flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0\">\n              <Transition.Child\n                enter=\"ease-out duration-300\"\n                enterFrom=\"opacity-0\"\n                enterTo=\"opacity-100\"\n                leave=\"ease-in duration-200\"\n                leaveFrom=\"opacity-100\"\n                leaveTo=\"opacity-0\"\n                entered=\"opacity-100\"\n                beforeEnter={() => console.log('[Transition.Child] [Overlay] Before enter')}\n                afterEnter={() => console.log('[Transition.Child] [Overlay] After enter')}\n                beforeLeave={() => console.log('[Transition.Child] [Overlay] Before leave')}\n                afterLeave={() => console.log('[Transition.Child] [Overlay] After leave')}\n              >\n                <div className=\"fixed inset-0 bg-gray-500/75 transition-opacity\" />\n              </Transition.Child>\n\n              <Transition.Child\n                as=\"div\"\n                className=\"relative\"\n                enter=\"ease-out transform duration-300\"\n                enterFrom=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n                enterTo=\"opacity-100 translate-y-0 sm:scale-100\"\n                leave=\"ease-in transform duration-200\"\n                leaveFrom=\"opacity-100 translate-y-0 sm:scale-100\"\n                leaveTo=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n                beforeEnter={() => console.log('[Transition.Child] [Panel] Before enter')}\n                afterEnter={() => console.log('[Transition.Child] [Panel] After enter')}\n                beforeLeave={() => console.log('[Transition.Child] [Panel] Before leave')}\n                afterLeave={() => console.log('[Transition.Child] [Panel] After leave')}\n              >\n                {/* This element is to trick the browser into centering the modal contents. */}\n                <span\n                  className=\"hidden sm:inline-block sm:h-screen sm:align-middle\"\n                  aria-hidden=\"true\"\n                >\n                  &#8203;\n                </span>\n                <Dialog.Panel className=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\">\n                  <div className=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n                    <div className=\"sm:flex sm:items-start\">\n                      <div className=\"mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10\">\n                        {/* Heroicon name: exclamation */}\n                        <svg\n                          className=\"h-6 w-6 text-red-600\"\n                          xmlns=\"http://www.w3.org/2000/svg\"\n                          fill=\"none\"\n                          viewBox=\"0 0 24 24\"\n                          stroke=\"currentColor\"\n                          aria-hidden=\"true\"\n                        >\n                          <path\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                            strokeWidth={2}\n                            d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n                          />\n                        </svg>\n                      </div>\n                      <div className=\"mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left\">\n                        <Dialog.Title\n                          as=\"h3\"\n                          className=\"text-lg font-medium leading-6 text-gray-900\"\n                        >\n                          Deactivate account\n                        </Dialog.Title>\n                        <div className=\"mt-2\">\n                          <p className=\"text-sm text-gray-500\">\n                            Are you sure you want to deactivate your account? All of your data will\n                            be permanently removed. This action cannot be undone.\n                          </p>\n                          <div className=\"relative mt-10 inline-flex gap-4 text-left\">\n                            <Menu>\n                              <Menu.Button as={Button} ref={trigger}>\n                                <span>Choose a reason</span>\n                                <svg\n                                  className=\"-mr-1 ml-2 h-5 w-5\"\n                                  viewBox=\"0 0 20 20\"\n                                  fill=\"currentColor\"\n                                >\n                                  <path\n                                    fillRule=\"evenodd\"\n                                    d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                                    clipRule=\"evenodd\"\n                                  />\n                                </svg>\n                              </Menu.Button>\n\n                              <Transition\n                                enter=\"transition duration-300 ease-out\"\n                                enterFrom=\"transform scale-95 opacity-0\"\n                                enterTo=\"transform scale-100 opacity-100\"\n                                leave=\"transition duration-75 ease-out\"\n                                leaveFrom=\"transform scale-100 opacity-100\"\n                                leaveTo=\"transform scale-95 opacity-0\"\n                              >\n                                <Portal>\n                                  <Menu.Items\n                                    ref={container}\n                                    className=\"outline-hidden z-20 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n                                  >\n                                    <div className=\"px-4 py-3\">\n                                      <p className=\"text-sm leading-5\">Signed in as</p>\n                                      <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                                        tom@example.com\n                                      </p>\n                                    </div>\n\n                                    <div className=\"py-1\">\n                                      <Menu.Item\n                                        as=\"a\"\n                                        href=\"#account-settings\"\n                                        className={resolveClass}\n                                      >\n                                        Account settings\n                                      </Menu.Item>\n                                      <Menu.Item as=\"a\" href=\"#support\" className={resolveClass}>\n                                        Support\n                                      </Menu.Item>\n                                      <Menu.Item\n                                        as=\"a\"\n                                        disabled\n                                        href=\"#new-feature\"\n                                        className={resolveClass}\n                                      >\n                                        New feature (soon)\n                                      </Menu.Item>\n                                      <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n                                        License\n                                      </Menu.Item>\n                                    </div>\n\n                                    <div className=\"py-1\">\n                                      <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n                                        Sign out\n                                      </Menu.Item>\n                                    </div>\n                                  </Menu.Items>\n                                </Portal>\n                              </Transition>\n                            </Menu>\n                          </div>\n                          <Flatpickr value={date} onChange={([date]) => setDate(date)} />\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n                  <div className=\"flex bg-gray-50 px-4 py-3 sm:flex-row-reverse sm:gap-2\">\n                    <Button onClick={() => setIsOpen(false)}>Deactivate</Button>\n                    <Button onClick={() => setIsOpen(false)}>Cancel</Button>\n                  </div>\n                </Dialog.Panel>\n              </Transition.Child>\n            </div>\n          </div>\n        </Dialog>\n      </Transition>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/scrollable-dialog.tsx",
    "content": "import { Dialog, Transition } from '@headlessui/react'\nimport { ExclamationIcon } from '@heroicons/react/outline'\nimport { useRef, useState } from 'react'\n\nexport default function Example() {\n  const [open, setOpen] = useState(false)\n\n  const cancelButtonRef = useRef(null)\n\n  return (\n    <>\n      <div className=\"p-12\">\n        <button\n          type=\"button\"\n          className=\"shadow-xs focus:outline-hidden inline-flex items-center rounded-md border border-gray-300 bg-white px-6 py-3 text-base font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2\"\n          onClick={() => setOpen(true)}\n        >\n          Open Dialog\n        </button>\n      </div>\n      <Transition.Root show={open}>\n        <Dialog as=\"div\" className=\"relative z-10\" initialFocus={cancelButtonRef} onClose={setOpen}>\n          <Transition.Child\n            enter=\"ease-out duration-300\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-100\"\n            leave=\"ease-in duration-200\"\n            leaveFrom=\"opacity-100\"\n            leaveTo=\"opacity-0\"\n          >\n            <div className=\"fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity\" />\n          </Transition.Child>\n\n          <div className=\"fixed inset-0 z-10 overflow-y-auto\">\n            <div className=\"flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0\">\n              <Transition.Child\n                enter=\"ease-out duration-300\"\n                enterFrom=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n                enterTo=\"opacity-100 translate-y-0 sm:scale-100\"\n                leave=\"ease-in duration-200\"\n                leaveFrom=\"opacity-100 translate-y-0 sm:scale-100\"\n                leaveTo=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n              >\n                <Dialog.Panel className=\"relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6\">\n                  <div className=\"sm:flex sm:items-start\">\n                    <div className=\"mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10\">\n                      <ExclamationIcon className=\"h-6 w-6 text-red-600\" aria-hidden=\"true\" />\n                    </div>\n                    <div className=\"mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left\">\n                      <Dialog.Title as=\"h3\" className=\"text-lg font-medium leading-6 text-gray-900\">\n                        Deactivate account\n                      </Dialog.Title>\n                      <div className=\"mt-2\">\n                        <p className=\"text-sm text-gray-500\">\n                          Are you sure you want to deactivate your account? All of your data will be\n                          permanently removed from our servers forever. This action cannot be\n                          undone.\n                        </p>\n                        {Array(20)\n                          .fill(null)\n                          .map((_, i) => (\n                            <p key={i} className=\"text-sm text-gray-500\">\n                              Are you sure you want to deactivate your account? All of your data\n                              will be permanently removed from our servers forever. This action\n                              cannot be undone.\n                            </p>\n                          ))}\n                      </div>\n                    </div>\n                  </div>\n                  <div className=\"mt-5 sm:ml-10 sm:mt-4 sm:flex sm:pl-4\">\n                    <button\n                      type=\"button\"\n                      className=\"shadow-xs focus:outline-hidden inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white hover:bg-red-700 focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:w-auto sm:text-sm\"\n                      onClick={() => setOpen(false)}\n                    >\n                      Deactivate\n                    </button>\n                    <button\n                      type=\"button\"\n                      className=\"shadow-xs focus:outline-hidden mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:ml-3 sm:mt-0 sm:w-auto sm:text-sm\"\n                      onClick={() => setOpen(false)}\n                      ref={cancelButtonRef}\n                    >\n                      Cancel\n                    </button>\n                  </div>\n                </Dialog.Panel>\n              </Transition.Child>\n            </div>\n          </div>\n        </Dialog>\n      </Transition.Root>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/scrollable-page-with-dialog.tsx",
    "content": "import { Dialog, Transition } from '@headlessui/react'\nimport { useState } from 'react'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(false)\n\n  return (\n    <>\n      {Array(5)\n        .fill(null)\n        .map((_, i) => (\n          <p key={i} className=\"m-4\">\n            Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam numquam beatae,\n            maiores sint est perferendis molestiae deleniti dolorem, illum vel, quam atque facilis!\n            Necessitatibus nostrum recusandae nemo corrupti, odio eius?\n          </p>\n        ))}\n\n      <button\n        type=\"button\"\n        onClick={() => setIsOpen((v) => !v)}\n        className=\"focus:shadow-outline-blue shadow-xs focus:outline-hidden m-12 rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 sm:text-sm sm:leading-5\"\n      >\n        Toggle!\n      </button>\n\n      {Array(20)\n        .fill(null)\n        .map((_, i) => (\n          <p key={i} className=\"m-4\">\n            Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam numquam beatae,\n            maiores sint est perferendis molestiae deleniti dolorem, illum vel, quam atque facilis!\n            Necessitatibus nostrum recusandae nemo corrupti, odio eius?\n          </p>\n        ))}\n\n      <Transition\n        data-debug=\"Dialog\"\n        show={isOpen}\n        beforeEnter={() => console.log('[Transition] Before enter')}\n        afterEnter={() => console.log('[Transition] After enter')}\n        beforeLeave={() => console.log('[Transition] Before leave')}\n        afterLeave={() => console.log('[Transition] After leave')}\n      >\n        <Dialog\n          onClose={() => {\n            console.log('close')\n            setIsOpen(false)\n          }}\n        >\n          <div className=\"fixed inset-0 z-10 overflow-y-auto\">\n            <div className=\"flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0\">\n              <Transition.Child\n                enter=\"ease-out duration-300\"\n                enterFrom=\"opacity-0\"\n                enterTo=\"opacity-75\"\n                leave=\"ease-in duration-200\"\n                leaveFrom=\"opacity-75\"\n                leaveTo=\"opacity-0\"\n                entered=\"opacity-75\"\n                beforeEnter={() => console.log('[Transition.Child] [Overlay] Before enter')}\n                afterEnter={() => console.log('[Transition.Child] [Overlay] After enter')}\n                beforeLeave={() => console.log('[Transition.Child] [Overlay] Before leave')}\n                afterLeave={() => console.log('[Transition.Child] [Overlay] After leave')}\n              >\n                <div className=\"fixed inset-0 bg-gray-500 transition-opacity\" />\n              </Transition.Child>\n\n              <Transition.Child\n                as=\"div\"\n                enter=\"ease-out transform duration-300\"\n                enterFrom=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n                enterTo=\"opacity-100 translate-y-0 sm:scale-100\"\n                leave=\"ease-in transform duration-200\"\n                leaveFrom=\"opacity-100 translate-y-0 sm:scale-100\"\n                leaveTo=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n                beforeEnter={() => console.log('[Transition.Child] [Panel] Before enter')}\n                afterEnter={() => console.log('[Transition.Child] [Panel] After enter')}\n                beforeLeave={() => console.log('[Transition.Child] [Panel] Before leave')}\n                afterLeave={() => console.log('[Transition.Child] [Panel] After leave')}\n              >\n                {/* This element is to trick the browser into centering the modal contents. */}\n                <span\n                  className=\"hidden sm:inline-block sm:h-screen sm:align-middle\"\n                  aria-hidden=\"true\"\n                >\n                  &#8203;\n                </span>\n                <Dialog.Panel className=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\">\n                  <div className=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n                    <div className=\"sm:flex sm:items-start\">\n                      <div className=\"mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10\">\n                        {/* Heroicon name: exclamation */}\n                        <svg\n                          className=\"h-6 w-6 text-red-600\"\n                          xmlns=\"http://www.w3.org/2000/svg\"\n                          fill=\"none\"\n                          viewBox=\"0 0 24 24\"\n                          stroke=\"currentColor\"\n                          aria-hidden=\"true\"\n                        >\n                          <path\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                            strokeWidth={2}\n                            d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n                          />\n                        </svg>\n                      </div>\n                      <div className=\"mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left\">\n                        <Dialog.Title\n                          as=\"h3\"\n                          className=\"text-lg font-medium leading-6 text-gray-900\"\n                        >\n                          Deactivate account\n                        </Dialog.Title>\n                        <div className=\"mt-2\">\n                          <p className=\"text-sm text-gray-500\">\n                            Are you sure you want to deactivate your account? All of your data will\n                            be permanently removed. This action cannot be undone.\n                          </p>\n                        </div>\n                        <input type=\"text\" />\n                      </div>\n                    </div>\n                  </div>\n                  <div className=\"bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6\">\n                    <button\n                      type=\"button\"\n                      onClick={() => setIsOpen(false)}\n                      className=\"focus:shadow-outline-red shadow-xs focus:outline-hidden inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white hover:bg-red-700 sm:ml-3 sm:w-auto sm:text-sm\"\n                    >\n                      Deactivate\n                    </button>\n                    <button\n                      type=\"button\"\n                      onClick={() => setIsOpen(false)}\n                      className=\"focus:shadow-outline-indigo shadow-xs focus:outline-hidden mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 hover:text-gray-500 sm:mt-0 sm:w-auto sm:text-sm\"\n                    >\n                      Cancel\n                    </button>\n                  </div>\n                </Dialog.Panel>\n              </Transition.Child>\n            </div>\n          </div>\n        </Dialog>\n      </Transition>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/dialog/sibling-dialogs.tsx",
    "content": "import {\n  Dialog,\n  DialogPanel,\n  Menu,\n  MenuButton,\n  MenuItem,\n  MenuItems,\n  Transition,\n  TransitionChild,\n} from '@headlessui/react'\nimport { useState } from 'react'\nimport { Button } from '../../components/button'\n\nexport default function App() {\n  let [openEdit, setOpenEdit] = useState(false)\n  let [openDelete, setOpenDelete] = useState(false)\n  let [openConfirm, setOpenConfirm] = useState(false)\n  let [openDeleted, setOpenDeleted] = useState(false)\n\n  return (\n    <div className=\"p-12\">\n      <Button onClick={() => setOpenEdit((v) => !v)}>Toggle Dialog</Button>\n\n      <MyDialog level={0} open={openEdit} onClose={setOpenEdit}>\n        <div>My Edit dialog</div>\n        <div className=\"flex-1 py-8\">\n          <Menu>\n            <MenuButton data-autofocus as={Button}>\n              Menu button\n            </MenuButton>\n            <MenuItems\n              anchor=\"bottom start\"\n              className=\"outline-hidden z-50 flex w-[calc(var(--button-width)*2)] flex-col rounded-sm bg-white p-1 shadow-sm\"\n            >\n              <MenuItem\n                as=\"button\"\n                className=\"data-active:bg-gray-100 rounded-sm px-2 py-1 text-left\"\n              >\n                Item A\n              </MenuItem>\n              <MenuItem\n                as=\"button\"\n                className=\"data-active:bg-gray-100 rounded-sm px-2 py-1 text-left\"\n              >\n                Item B\n              </MenuItem>\n              <MenuItem\n                as=\"button\"\n                className=\"data-active:bg-gray-100 rounded-sm px-2 py-1 text-left\"\n                onClick={() => setOpenDelete(true)}\n              >\n                Delete\n              </MenuItem>\n            </MenuItems>\n          </Menu>\n        </div>\n        <div className=\"flex gap-2\">\n          <Button onClick={() => setOpenDelete(true)}>Delete</Button>\n          <Button onClick={() => setOpenEdit(false)}>Close</Button>\n        </div>\n      </MyDialog>\n\n      <MyDialog level={1} open={openDelete} onClose={setOpenDelete}>\n        <div>My Delete Dialog</div>\n        <div className=\"flex-1 py-8\">...</div>\n        <div className=\"flex gap-2\">\n          <Button data-autofocus onClick={() => setOpenConfirm(true)}>\n            Confirm\n          </Button>\n          <Button onClick={() => setOpenDelete(false)}>Close</Button>\n        </div>\n      </MyDialog>\n\n      <MyDialog level={2} open={openConfirm} onClose={setOpenConfirm}>\n        <div>Are you sure??</div>\n        <div className=\"flex-1 py-8\">If confirmed, this dialog will close.</div>\n        <div className=\"flex gap-2\">\n          <Button\n            data-autofocus\n            onClick={() => {\n              setOpenDeleted(true)\n              setOpenConfirm(false)\n            }}\n          >\n            CONFIRM\n          </Button>\n          <Button onClick={() => setOpenConfirm(false)}>Close</Button>\n        </div>\n      </MyDialog>\n\n      <MyDialog level={3} open={openDeleted} onClose={setOpenDeleted}>\n        <div>SUCCESSFULLY DELETED</div>\n        <div className=\"flex gap-2\">\n          <Button data-autofocus onClick={() => setOpenDeleted(false)}>\n            Close\n          </Button>\n        </div>\n      </MyDialog>\n    </div>\n  )\n}\n\nfunction MyDialog({ level = 0, open, onClose, children }: any) {\n  return (\n    <Transition\n      show={open}\n      enter=\"transition duration-300\"\n      enterFrom=\"opacity-0\"\n      enterTo=\"opacity-100\"\n      leave=\"transition duration-100\"\n      leaveFrom=\"opacity-100\"\n      leaveTo=\"opacity-0\"\n    >\n      <Dialog onClose={onClose} className=\"fixed inset-0 z-50 flex items-center justify-center\">\n        <div className=\"fixed inset-0 bg-gray-500/25\" />\n        <TransitionChild\n          enter=\"transition duration-300\"\n          enterFrom=\"scale-95\"\n          enterTo=\"scale-100\"\n          leave=\"transition duration-100\"\n          leaveFrom=\"scale-100\"\n          leaveTo=\"scale-95\"\n        >\n          <DialogPanel\n            style={{\n              transform: `translate(calc(50px * ${level}), calc(50px * ${level}))`,\n            }}\n            className=\"relative z-10 flex w-96 -translate-y-24 flex-col rounded-sm bg-white p-4 shadow-xl\"\n          >\n            {children}\n          </DialogPanel>\n        </TransitionChild>\n      </Dialog>\n    </Transition>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/disclosure/disclosure.tsx",
    "content": "import { Disclosure, Transition } from '@headlessui/react'\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <Disclosure>\n          <Disclosure.Button>Trigger</Disclosure.Button>\n\n          <Transition\n            enter=\"transition duration-1000 ease-out\"\n            enterFrom=\"transform scale-95 opacity-0\"\n            enterTo=\"transform scale-100 opacity-100\"\n            leave=\"transition duration-1000 ease-out\"\n            leaveFrom=\"transform scale-100 opacity-100\"\n            leaveTo=\"transform scale-95 opacity-0\"\n          >\n            <Disclosure.Panel className=\"mt-4 bg-white p-4\">Content</Disclosure.Panel>\n          </Transition>\n        </Disclosure>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/listbox/listbox-overlaps.tsx",
    "content": "import { Label, Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react'\nimport { useState } from 'react'\n\nlet people = [\n  'Wade Cooper',\n  'Arlene Mccoy',\n  'Devon Webb',\n  'Tom Cook',\n  'Tanya Fox',\n  'Hellen Schmidt',\n  'Caroline Schultz',\n  'Mason Heaney',\n  'Claudie Smitham',\n  'Emil Schaefer',\n]\n\nexport default function Home() {\n  let [active, setActivePerson] = useState(people[0])\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"space-y-1\">\n          <Listbox value={active} onChange={setActivePerson}>\n            <Label className=\"block text-sm font-medium leading-5 text-gray-700\">Assigned to</Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs inline-block w-full rounded-md\">\n                <ListboxButton className=\"relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out sm:text-sm sm:leading-5\">\n                  <span className=\"block truncate\">{active}</span>\n                  <span className=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </ListboxButton>\n              </span>\n\n              <ListboxOptions\n                anchor=\"selection\"\n                transition\n                className=\"focus:outline-hidden data-closed:scale-95 data-closed:opacity-0 w-(--button-width) overflow-auto rounded-md border border-gray-300 bg-white py-1 text-base leading-6 shadow-lg transition duration-200 ease-out [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5\"\n              >\n                {people.map((name) => (\n                  <ListboxOption\n                    key={name}\n                    value={name}\n                    className=\"focus:outline-hidden data-active:bg-indigo-600 data-active:text-white group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900\"\n                  >\n                    <span className=\"group-data-selected:font-semibold block truncate font-normal\">\n                      {name}\n                    </span>\n                    <span className=\"group-data-active:text-white group-data-selected:flex absolute inset-y-0 right-0 hidden items-center pr-4 text-indigo-600\">\n                      <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </ListboxOption>\n                ))}\n              </ListboxOptions>\n            </div>\n          </Listbox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/listbox/listbox-with-pure-tailwind.tsx",
    "content": "import { Label, Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\n\nlet people = [\n  'Wade Cooper',\n  'Arlene Mccoy',\n  'Devon Webb',\n  'Tom Cook',\n  'Tanya Fox',\n  'Hellen Schmidt',\n  'Caroline Schultz',\n  'Mason Heaney',\n  'Claudie Smitham',\n  'Emil Schaefer',\n]\n\nexport default function Home() {\n  let [active, setActivePerson] = useState(people[2])\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActivePerson(people[Math.floor(Math.random() * people.length)])\n  }, [])\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mx-auto w-full max-w-xs\">\n        <div className=\"space-y-1\">\n          <Listbox value={active} onChange={setActivePerson}>\n            <Label className=\"block text-sm font-medium leading-5 text-gray-700\">Assigned to</Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs inline-block w-full rounded-md\">\n                <ListboxButton className=\"relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out sm:text-sm sm:leading-5\">\n                  <span className=\"block truncate\">{active}</span>\n                  <span className=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </ListboxButton>\n              </span>\n\n              <ListboxOptions\n                anchor=\"bottom\"\n                transition\n                className=\"focus:outline-hidden data-closed:scale-95 data-closed:opacity-0 w-(--button-width) overflow-auto rounded-md border border-gray-300 bg-white py-1 text-base leading-6 shadow-lg transition duration-200 ease-out [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5\"\n              >\n                {people.map((name) => (\n                  <ListboxOption\n                    key={name}\n                    value={name}\n                    className=\"focus:outline-hidden data-active:bg-indigo-600 data-active:text-white group relative cursor-default select-none py-2 pl-3 pr-9 text-gray-900\"\n                  >\n                    <span className=\"group-data-selected:font-semibold block truncate font-normal\">\n                      {name}\n                    </span>\n                    <span className=\"group-data-active:text-white group-data-selected:flex absolute inset-y-0 right-0 hidden items-center pr-4 text-indigo-600\">\n                      <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </ListboxOption>\n                ))}\n              </ListboxOptions>\n            </div>\n          </Listbox>\n        </div>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/listbox/multi-select.tsx",
    "content": "import { Listbox } from '@headlessui/react'\nimport { useState } from 'react'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet people = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n      <MultiPeopleList />\n    </div>\n  )\n}\n\nfunction MultiPeopleList() {\n  let [activePersons, setActivePersons] = useState([people[2], people[3]])\n\n  return (\n    <div className=\"w-full max-w-4xl\">\n      <div className=\"space-y-1\">\n        <form\n          onSubmit={(e) => {\n            e.preventDefault()\n            console.log([...new FormData(e.currentTarget).entries()])\n          }}\n        >\n          <Listbox value={activePersons} onChange={setActivePersons} name=\"people\" multiple>\n            <Listbox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </Listbox.Label>\n\n            <div className=\"relative\">\n              <span className=\"shadow-xs inline-block w-full rounded-md\">\n                <Listbox.Button className=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\">\n                  <span className=\"block flex flex-wrap gap-2\">\n                    {activePersons.length === 0 ? (\n                      <span className=\"p-0.5\">Empty</span>\n                    ) : (\n                      activePersons.map((person) => (\n                        <span\n                          key={person.id}\n                          className=\"flex items-center gap-1 rounded-sm bg-blue-50 px-2 py-0.5\"\n                        >\n                          <span>{person.name}</span>\n                          <svg\n                            className=\"h-4 w-4 cursor-pointer\"\n                            fill=\"none\"\n                            stroke=\"currentColor\"\n                            viewBox=\"0 0 24 24\"\n                            xmlns=\"http://www.w3.org/2000/svg\"\n                            onClick={(e) => {\n                              e.stopPropagation()\n                              e.preventDefault()\n                              setActivePersons((existing) => existing.filter((p) => p !== person))\n                            }}\n                          >\n                            <path\n                              strokeLinecap=\"round\"\n                              strokeLinejoin=\"round\"\n                              strokeWidth=\"2\"\n                              d=\"M6 18L18 6M6 6l12 12\"\n                            />\n                          </svg>\n                        </span>\n                      ))\n                    )}\n                  </span>\n                  <span className=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                    <svg\n                      className=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        strokeWidth=\"1.5\"\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </Listbox.Button>\n              </span>\n\n              <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <Listbox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                  {people.map((person) => (\n                    <Listbox.Option\n                      key={person.id}\n                      value={person}\n                      className={({ active }) => {\n                        return classNames(\n                          'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                          active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                        )\n                      }}\n                    >\n                      {({ active, selected }) => (\n                        <>\n                          <span\n                            className={classNames(\n                              'block truncate',\n                              selected ? 'font-semibold' : 'font-normal'\n                            )}\n                          >\n                            {person.name}\n                          </span>\n                          {selected && (\n                            <span\n                              className={classNames(\n                                'absolute inset-y-0 right-0 flex items-center pr-4',\n                                active ? 'text-white' : 'text-indigo-600'\n                              )}\n                            >\n                              <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                                <path\n                                  fillRule=\"evenodd\"\n                                  d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                  clipRule=\"evenodd\"\n                                />\n                              </svg>\n                            </span>\n                          )}\n                        </>\n                      )}\n                    </Listbox.Option>\n                  ))}\n                </Listbox.Options>\n              </div>\n            </div>\n          </Listbox>\n          <button className=\"shadow-xs focus:outline-hidden mt-2 inline-flex items-center rounded-sm border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2\">\n            Submit\n          </button>\n        </form>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/listbox/multiple-elements.tsx",
    "content": "import { Listbox } from '@headlessui/react'\nimport { useEffect, useState } from 'react'\nimport { classNames } from '../../utils/class-names'\n\nlet people = [\n  'Wade Cooper',\n  'Arlene Mccoy',\n  'Devon Webb',\n  'Tom Cook',\n  'Tanya Fox',\n  'Hellen Schmidt',\n  'Caroline Schultz',\n  'Mason Heaney',\n  'Claudie Smitham',\n  'Emil Schaefer',\n]\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n      <PeopleList />\n\n      <div>\n        <label htmlFor=\"email\" className=\"block text-sm font-medium leading-5 text-gray-700\">\n          Email\n        </label>\n        <div className=\"shadow-xs relative mt-1 rounded-md\">\n          <input\n            className=\"form-input block w-full sm:text-sm sm:leading-5\"\n            placeholder=\"you@example.com\"\n          />\n        </div>\n      </div>\n\n      <PeopleList />\n    </div>\n  )\n}\n\nfunction PeopleList() {\n  let [active, setActivePerson] = useState(people[2])\n\n  // Choose a random person on mount\n  useEffect(() => {\n    setActivePerson(people[Math.floor(Math.random() * people.length)])\n  }, [])\n\n  return (\n    <div className=\"w-64\">\n      <div className=\"space-y-1\">\n        <Listbox\n          value={active}\n          onChange={(value) => {\n            console.log('value:', value)\n            setActivePerson(value)\n          }}\n        >\n          <Listbox.Label className=\"block text-sm font-medium leading-5 text-gray-700\">\n            Assigned to\n          </Listbox.Label>\n\n          <div className=\"relative\">\n            <span className=\"shadow-xs inline-block w-full rounded-md\">\n              <Listbox.Button className=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\">\n                <span className=\"block truncate\">{active}</span>\n                <span className=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                  <svg\n                    className=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </Listbox.Button>\n            </span>\n\n            <div className=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <Listbox.Options className=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\">\n                {people.map((name) => (\n                  <Listbox.Option\n                    key={name}\n                    value={name}\n                    className={({ active }) => {\n                      return classNames(\n                        'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                        active ? 'bg-indigo-600 text-white' : 'text-gray-900'\n                      )\n                    }}\n                  >\n                    {({ active, selected }) => (\n                      <>\n                        <span\n                          className={classNames(\n                            'block truncate',\n                            selected ? 'font-semibold' : 'font-normal'\n                          )}\n                        >\n                          {name}\n                        </span>\n                        {selected && (\n                          <span\n                            className={classNames(\n                              'absolute inset-y-0 right-0 flex items-center pr-4',\n                              active ? 'text-white' : 'text-indigo-600'\n                            )}\n                          >\n                            <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                              <path\n                                fillRule=\"evenodd\"\n                                d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                                clipRule=\"evenodd\"\n                              />\n                            </svg>\n                          </span>\n                        )}\n                      </>\n                    )}\n                  </Listbox.Option>\n                ))}\n              </Listbox.Options>\n            </div>\n          </div>\n        </Listbox>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu-with-floating-ui.tsx",
    "content": "import { offset, useFloating } from '@floating-ui/react'\nimport { Menu } from '@headlessui/react'\nimport { ReactNode, useEffect, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let { refs, floatingStyles } = useFloating({\n    placement: 'bottom-end',\n    strategy: 'fixed',\n    middleware: [offset(10)],\n  })\n\n  function resolveClass({ active, disabled }) {\n    return classNames(\n      'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',\n      active && 'bg-gray-100 text-gray-900',\n      disabled && 'cursor-not-allowed opacity-50'\n    )\n  }\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mt-64 inline-block text-left\">\n        <Menu>\n          <span className=\"shadow-xs inline-flex rounded-md\">\n            <Menu.Button ref={refs.setReference} as={Button}>\n              <span>Options</span>\n              <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                <path\n                  fillRule=\"evenodd\"\n                  d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                  clipRule=\"evenodd\"\n                />\n              </svg>\n            </Menu.Button>\n          </span>\n\n          <Portal>\n            <Menu.Items\n              className=\"outline-hidden w-56 divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n              ref={refs.setFloating}\n              style={floatingStyles}\n            >\n              <div className=\"px-4 py-3\">\n                <p className=\"text-sm leading-5\">Signed in as</p>\n                <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                  tom@example.com\n                </p>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#account-settings\" className={resolveClass}>\n                  Account settings\n                </Menu.Item>\n                <Menu.Item>\n                  {(data) => (\n                    <a href=\"#support\" className={resolveClass(data)}>\n                      Support\n                    </a>\n                  )}\n                </Menu.Item>\n                <Menu.Item as=\"a\" disabled href=\"#new-feature\" className={resolveClass}>\n                  New feature (soon)\n                </Menu.Item>\n                <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n                  License\n                </Menu.Item>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n                  Sign out\n                </Menu.Item>\n              </div>\n            </Menu.Items>\n          </Portal>\n        </Menu>\n      </div>\n    </div>\n  )\n}\n\nfunction Portal(props: { children: ReactNode }) {\n  let { children } = props\n  let [mounted, setMounted] = useState(false)\n\n  useEffect(() => setMounted(true), [])\n\n  if (!mounted) return null\n  return createPortal(children, document.body)\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu-with-framer-motion.tsx",
    "content": "import { Menu, MenuItemProps } from '@headlessui/react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport Link from 'next/link'\nimport { forwardRef } from 'react'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"relative inline-block text-left\">\n        <Menu>\n          {({ open }) => (\n            <>\n              <span className=\"shadow-xs rounded-md\">\n                <Menu.Button as={Button}>\n                  <span>Options</span>\n                  <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                    <path\n                      fillRule=\"evenodd\"\n                      d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                      clipRule=\"evenodd\"\n                    />\n                  </svg>\n                </Menu.Button>\n              </span>\n\n              <AnimatePresence>\n                {open && (\n                  <Menu.Items\n                    static\n                    as={motion.div}\n                    initial={{ opacity: 0, y: 0 }}\n                    animate={{ opacity: 1, y: '0.5rem' }}\n                    exit={{ opacity: 0, y: 0 }}\n                    className=\"outline-hidden absolute right-0 w-56 divide-y divide-gray-100 rounded-md border border-gray-200 bg-white opacity-0 shadow-lg\"\n                  >\n                    <div className=\"px-4 py-3\">\n                      <p className=\"text-sm leading-5\">Signed in as</p>\n                      <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                        tom@example.com\n                      </p>\n                    </div>\n\n                    <div className=\"py-1\">\n                      <Item href=\"#account-settings\">Account settings</Item>\n                      <Item as={Link} href=\"#support\">\n                        Support\n                      </Item>\n                      <Item href=\"#new-feature\" disabled>\n                        New feature (soon)\n                      </Item>\n                      <Item href=\"#license\">License</Item>\n                    </div>\n\n                    <div className=\"py-1\">\n                      <Item as={SignOutButton} />\n                    </div>\n                  </Menu.Items>\n                )}\n              </AnimatePresence>\n            </>\n          )}\n        </Menu>\n      </div>\n    </div>\n  )\n}\n\nlet SignOutButton = forwardRef<HTMLButtonElement>((props, ref) => {\n  return (\n    <form\n      method=\"POST\"\n      action=\"#\"\n      onSubmit={(e) => {\n        e.preventDefault()\n        alert('SIGNED OUT')\n      }}\n      className=\"w-full\"\n    >\n      <button ref={ref} type=\"submit\" {...props}>\n        Sign out\n      </button>\n    </form>\n  )\n})\n\nlet Item = forwardRef<HTMLAnchorElement, MenuItemProps<any>>((props, ref) => {\n  return (\n    <Menu.Item\n      ref={ref}\n      as=\"a\"\n      className={({ active, disabled }) =>\n        classNames(\n          'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700',\n          active && 'bg-gray-100 text-gray-900',\n          disabled && 'cursor-not-allowed opacity-50'\n        )\n      }\n      {...props}\n    />\n  )\n})\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu-with-popper.tsx",
    "content": "import { Menu } from '@headlessui/react'\nimport { ReactNode, useEffect, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\nimport { usePopper } from '../../utils/hooks/use-popper'\n\nexport default function Home() {\n  let [trigger, container] = usePopper({\n    placement: 'bottom-end',\n    strategy: 'fixed',\n    modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n  })\n\n  function resolveClass({ active, disabled }) {\n    return classNames(\n      'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',\n      active && 'bg-gray-100 text-gray-900',\n      disabled && 'cursor-not-allowed opacity-50'\n    )\n  }\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mt-64 inline-block text-left\">\n        <Menu>\n          <span className=\"shadow-xs inline-flex rounded-md\">\n            <Menu.Button ref={trigger} as={Button}>\n              <span>Options</span>\n              <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                <path\n                  fillRule=\"evenodd\"\n                  d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                  clipRule=\"evenodd\"\n                />\n              </svg>\n            </Menu.Button>\n          </span>\n\n          <Portal>\n            <Menu.Items\n              className=\"outline-hidden w-56 divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n              ref={container}\n            >\n              <div className=\"px-4 py-3\">\n                <p className=\"text-sm leading-5\">Signed in as</p>\n                <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                  tom@example.com\n                </p>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#account-settings\" className={resolveClass}>\n                  Account settings\n                </Menu.Item>\n                <Menu.Item>\n                  {(data) => (\n                    <a href=\"#support\" className={resolveClass(data)}>\n                      Support\n                    </a>\n                  )}\n                </Menu.Item>\n                <Menu.Item as=\"a\" disabled href=\"#new-feature\" className={resolveClass}>\n                  New feature (soon)\n                </Menu.Item>\n                <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n                  License\n                </Menu.Item>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n                  Sign out\n                </Menu.Item>\n              </div>\n            </Menu.Items>\n          </Portal>\n        </Menu>\n      </div>\n    </div>\n  )\n}\n\nfunction Portal(props: { children: ReactNode }) {\n  let { children } = props\n  let [mounted, setMounted] = useState(false)\n\n  useEffect(() => setMounted(true), [])\n\n  if (!mounted) return null\n  return createPortal(children, document.body)\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu-with-transition-and-popper.tsx",
    "content": "import { Menu, Transition } from '@headlessui/react'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\nimport { usePopper } from '../../utils/hooks/use-popper'\n\nexport default function Home() {\n  let [trigger, container] = usePopper({\n    placement: 'bottom-end',\n    strategy: 'fixed',\n    modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n  })\n\n  function resolveClass({ active, disabled }) {\n    return classNames(\n      'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n      active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n      disabled && 'cursor-not-allowed opacity-50'\n    )\n  }\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"mt-64 inline-block text-left\">\n        <Menu>\n          <span className=\"shadow-xs rounded-md\">\n            <Menu.Button ref={trigger} as={Button}>\n              <span>Options</span>\n              <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                <path\n                  fillRule=\"evenodd\"\n                  d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                  clipRule=\"evenodd\"\n                />\n              </svg>\n            </Menu.Button>\n          </span>\n\n          <div ref={container} className=\"w-56\">\n            <Transition\n              enter=\"transition duration-100 ease-out\"\n              enterFrom=\"transform scale-95 opacity-0\"\n              enterTo=\"transform scale-100 opacity-100\"\n              leave=\"transition duration-75 ease-out\"\n              leaveFrom=\"transform scale-100 opacity-100\"\n              leaveTo=\"transform scale-95 opacity-0\"\n            >\n              <Menu.Items className=\"outline-hidden divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\">\n                <div className=\"px-4 py-3\">\n                  <p className=\"text-sm leading-5\">Signed in as</p>\n                  <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                    tom@example.com\n                  </p>\n                </div>\n\n                <div className=\"py-1\">\n                  <Menu.Item as=\"a\" href=\"#account-settings\" className={resolveClass}>\n                    Account settings\n                  </Menu.Item>\n                  <Menu.Item>\n                    {(data) => (\n                      <a href=\"#support\" className={resolveClass(data)}>\n                        Support\n                      </a>\n                    )}\n                  </Menu.Item>\n                  <Menu.Item as=\"a\" disabled href=\"#new-feature\" className={resolveClass}>\n                    New feature (soon)\n                  </Menu.Item>\n                  <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n                    License\n                  </Menu.Item>\n                </div>\n                <div className=\"py-1\">\n                  <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n                    Sign out\n                  </Menu.Item>\n                </div>\n              </Menu.Items>\n            </Transition>\n          </div>\n        </Menu>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu-with-transition.tsx",
    "content": "import { Menu, Transition } from '@headlessui/react'\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  function resolveClass({ active, disabled }) {\n    return classNames(\n      'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n      active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n      disabled && 'cursor-not-allowed opacity-50'\n    )\n  }\n\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <div className=\"relative inline-block text-left\">\n        <Menu>\n          <span className=\"shadow-xs rounded-md\">\n            <Menu.Button as={Button}>\n              <span>Options</span>\n              <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                <path\n                  fillRule=\"evenodd\"\n                  d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                  clipRule=\"evenodd\"\n                />\n              </svg>\n            </Menu.Button>\n          </span>\n\n          <Transition\n            enter=\"transition duration-500 ease-out\"\n            enterFrom=\"transform scale-95 opacity-0\"\n            enterTo=\"transform scale-100 opacity-100\"\n            leave=\"transition duration-500 ease-out\"\n            leaveFrom=\"transform scale-100 opacity-100\"\n            leaveTo=\"transform scale-95 opacity-0\"\n            beforeEnter={() => console.log('Before enter')}\n            afterEnter={() => console.log('After enter')}\n            beforeLeave={() => console.log('Before leave')}\n            afterLeave={() => console.log('After leave')}\n          >\n            <Menu.Items\n              anchor=\"bottom start\"\n              className=\"outline-hidden w-[calc(var(--button-width)*2)] divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg [--anchor-gap:--spacing(2)]\"\n            >\n              <div className=\"px-4 py-3\">\n                <p className=\"text-sm leading-5\">Signed in as</p>\n                <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                  tom@example.com\n                </p>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#account-settings\" className={resolveClass}>\n                  Account settings\n                </Menu.Item>\n                <Menu.Item as=\"a\" href=\"#support\" className={resolveClass}>\n                  Support\n                </Menu.Item>\n                <Menu.Item as=\"a\" disabled href=\"#new-feature\" className={resolveClass}>\n                  New feature (soon)\n                </Menu.Item>\n                <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n                  License\n                </Menu.Item>\n              </div>\n\n              <div className=\"py-1\">\n                <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n                  Sign out\n                </Menu.Item>\n              </div>\n            </Menu.Items>\n          </Transition>\n        </Menu>\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/menu.tsx",
    "content": "import { Menu } from '@headlessui/react'\n\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n      <ExampleMenu />\n    </div>\n  )\n}\n\nexport function ExampleMenu() {\n  return (\n    <div className=\"relative inline-block text-left\">\n      <Menu>\n        <span className=\"shadow-xs rounded-md\">\n          <Menu.Button as={Button}>\n            <span>Options</span>\n            <svg\n              className=\"-mr-1 ml-2 h-5 w-5 transition-transform duration-150\"\n              viewBox=\"0 0 20 20\"\n              fill=\"currentColor\"\n            >\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </Menu.Button>\n        </span>\n\n        <Menu.Items\n          anchor=\"bottom start\"\n          className=\"outline-hidden z-50 w-56 divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg [--anchor-gap:--spacing(1)]\"\n        >\n          <div className=\"px-4 py-3\">\n            <p className=\"text-sm leading-5\">Signed in as</p>\n            <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n          </div>\n\n          <div className=\"py-1\">\n            <CustomMenuItem href=\"#account-settings\">Account settings</CustomMenuItem>\n            <CustomMenuItem href=\"#support\">Support</CustomMenuItem>\n            <CustomMenuItem disabled href=\"#new-feature\">\n              New feature (soon)\n            </CustomMenuItem>\n            <CustomMenuItem href=\"#license\">License</CustomMenuItem>\n          </div>\n          <div className=\"py-1\">\n            <CustomMenuItem href=\"#sign-out\">Sign out</CustomMenuItem>\n          </div>\n        </Menu.Items>\n      </Menu>\n    </div>\n  )\n}\n\nfunction CustomMenuItem(props) {\n  return (\n    <Menu.Item {...props}>\n      {({ active, disabled }) => (\n        <button\n          onClick={() => {\n            alert(`You clicked on ${props.href}`)\n          }}\n          className={classNames(\n            'flex w-full justify-between px-4 py-2 text-left text-sm leading-5',\n            active ? 'bg-indigo-500 text-white' : 'text-gray-700',\n            disabled && 'cursor-not-allowed opacity-50'\n          )}\n        >\n          <span className={classNames(active && 'font-bold')}>{props.children}</span>\n          <kbd className={classNames('font-sans', active && 'text-indigo-50')}>⌘K</kbd>\n        </button>\n      )}\n    </Menu.Item>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/menu/multiple-elements.tsx",
    "content": "import { Menu } from '@headlessui/react'\nimport { Button } from '../../components/button'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  return (\n    <div className=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n      <Dropdown />\n\n      <div>\n        <div className=\"shadow-xs relative rounded-md\">\n          <input\n            className=\"form-input block w-full sm:text-sm sm:leading-5\"\n            placeholder=\"you@example.com\"\n          />\n        </div>\n      </div>\n\n      <Dropdown />\n    </div>\n  )\n}\n\nfunction Dropdown() {\n  function resolveClass({ active, disabled }) {\n    return classNames(\n      'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',\n      active && 'bg-gray-100 text-gray-900',\n      disabled && 'cursor-not-allowed opacity-50'\n    )\n  }\n\n  return (\n    <div className=\"relative inline-block text-left\">\n      <Menu>\n        <span className=\"shadow-xs inline-flex rounded-md\">\n          <Menu.Button as={Button}>\n            <span>Options</span>\n            <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </Menu.Button>\n        </span>\n\n        <Menu.Items\n          modal={false}\n          className=\"outline-hidden 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\"\n        >\n          <div className=\"px-4 py-3\">\n            <p className=\"text-sm leading-5\">Signed in as</p>\n            <p className=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n          </div>\n\n          <div className=\"py-1\">\n            <Menu.Item as=\"a\" href=\"#account-settings\" className={resolveClass}>\n              Account settings\n            </Menu.Item>\n            <Menu.Item>\n              {(data) => (\n                <a href=\"#support\" className={resolveClass(data)}>\n                  Support\n                </a>\n              )}\n            </Menu.Item>\n            <Menu.Item as=\"a\" disabled href=\"#new-feature\" className={resolveClass}>\n              New feature (soon)\n            </Menu.Item>\n            <Menu.Item as=\"a\" href=\"#license\" className={resolveClass}>\n              License\n            </Menu.Item>\n          </div>\n\n          <div className=\"py-1\">\n            <Menu.Item as=\"a\" href=\"#sign-out\" className={resolveClass}>\n              Sign out\n            </Menu.Item>\n          </div>\n        </Menu.Items>\n      </Menu>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/popover/popover.tsx",
    "content": "import { Popover, Transition } from '@headlessui/react'\nimport React, { forwardRef } from 'react'\nimport { ExampleMenu } from '../menu/menu'\n\nlet Button = forwardRef(\n  (props: React.ComponentProps<'button'>, ref: React.MutableRefObject<HTMLButtonElement>) => {\n    return (\n      <Popover.Button\n        className=\"focus:outline-hidden border-2 border-transparent bg-gray-300 px-3 py-2 text-left focus:border-blue-900\"\n        {...props}\n        ref={ref}\n      />\n    )\n  }\n)\n\nexport default function Home() {\n  let items = ['First', 'Second', 'Third', 'Fourth']\n\n  return (\n    <div className=\"flex items-center justify-center space-x-12 p-12\">\n      <button>Previous</button>\n\n      <Popover.Group as=\"nav\" aria-label=\"Mythical University\" className=\"flex space-x-3\">\n        <Popover as=\"div\" className=\"relative\">\n          <Transition\n            enter=\"transition ease-out duration-300 transform\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-100\"\n            leave=\"transition ease-in duration-300 transform\"\n            leaveFrom=\"opacity-100\"\n            leaveTo=\"opacity-0\"\n          >\n            <Popover.Overlay className=\"fixed inset-0 z-20 bg-gray-500/75\"></Popover.Overlay>\n          </Transition>\n\n          <Popover.Button className=\"focus:outline-hidden relative z-30 border-2 border-transparent bg-gray-300 px-3 py-2 focus:border-blue-900\">\n            Normal\n          </Popover.Button>\n          <Popover.Panel className=\"absolute z-30 flex w-64 flex-col border-2 border-blue-900 bg-gray-100\">\n            {items.map((item, i) => (\n              <Button key={item} hidden={i === 2}>\n                Normal - {item}\n              </Button>\n            ))}\n            <div className=\"p-2\">\n              <ExampleMenu />\n            </div>\n          </Popover.Panel>\n        </Popover>\n\n        <Popover as=\"div\" className=\"relative\">\n          <Button>Focus</Button>\n          <Popover.Panel\n            focus\n            className=\"absolute flex w-64 flex-col border-2 border-blue-900 bg-gray-100\"\n          >\n            {items.map((item) => (\n              <Button key={item}>Focus - {item}</Button>\n            ))}\n          </Popover.Panel>\n        </Popover>\n\n        <Popover as=\"div\" className=\"relative\">\n          <Button>Portal</Button>\n          <Popover.Panel\n            anchor=\"bottom start\"\n            className=\"flex w-64 flex-col border-2 border-blue-900 bg-gray-100 [--anchor-gap:--spacing(1)]\"\n          >\n            {items.map((item) => (\n              <Button key={item}>Portal - {item}</Button>\n            ))}\n            <div className=\"p-2\">\n              <ExampleMenu />\n            </div>\n          </Popover.Panel>\n        </Popover>\n\n        <Popover as=\"div\" className=\"relative\">\n          <Button>Focus in Portal</Button>\n          <Popover.Panel\n            focus\n            anchor=\"bottom start\"\n            className=\"flex w-64 flex-col border-2 border-blue-900 bg-gray-100 [--anchor-gap:--spacing(1)]\"\n          >\n            {items.map((item) => (\n              <Button key={item}>Focus in Portal - {item}</Button>\n            ))}\n          </Popover.Panel>\n        </Popover>\n      </Popover.Group>\n\n      <button>Next</button>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/radio-group/radio-group.tsx",
    "content": "import { RadioGroup } from '@headlessui/react'\nimport { useState } from 'react'\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let access = [\n    {\n      id: 'access-1',\n      name: 'Public access',\n      description: 'This project would be available to anyone who has the link',\n    },\n    {\n      id: 'access-2',\n      name: 'Private to Project Members',\n      description: 'Only members of this project would be able to access',\n    },\n    {\n      id: 'access-3',\n      name: 'Private to you',\n      description: 'You are the only one able to access this project',\n    },\n  ]\n  let [active, setActive] = useState()\n\n  return (\n    <div className=\"max-w-xl p-12\">\n      <a href=\"/\">Link before</a>\n      <RadioGroup value={active} onChange={setActive}>\n        <fieldset className=\"space-y-4\">\n          <legend>\n            <h2 className=\"text-xl\">Privacy setting</h2>\n          </legend>\n\n          <div className=\"-space-y-px rounded-md bg-white\">\n            {access.map(({ id, name, description }, i) => {\n              return (\n                <RadioGroup.Option\n                  key={id}\n                  value={id}\n                  className={({ active }) =>\n                    classNames(\n                      // Rounded corners\n                      i === 0 && 'rounded-tl-md rounded-tr-md',\n                      access.length - 1 === i && 'rounded-bl-md rounded-br-md',\n\n                      // Shared\n                      'focus:outline-hidden relative flex border p-4',\n                      active ? 'z-10 border-indigo-200 bg-indigo-50' : 'border-gray-200'\n                    )\n                  }\n                >\n                  {({ active, checked }) => (\n                    <div className=\"flex w-full items-center justify-between\">\n                      <div className=\"ml-3 flex cursor-pointer flex-col\">\n                        <span\n                          className={classNames(\n                            'block text-sm font-medium leading-5',\n                            active ? 'text-indigo-900' : 'text-gray-900'\n                          )}\n                        >\n                          {name}\n                        </span>\n                        <span\n                          className={classNames(\n                            'block text-sm leading-5',\n                            active ? 'text-indigo-700' : 'text-gray-500'\n                          )}\n                        >\n                          {description}\n                        </span>\n                      </div>\n                      <div>\n                        {checked && (\n                          <svg\n                            xmlns=\"http://www.w3.org/2000/svg\"\n                            fill=\"none\"\n                            viewBox=\"0 0 24 24\"\n                            stroke=\"currentColor\"\n                            className=\"h-5 w-5 text-indigo-500\"\n                          >\n                            <path\n                              strokeLinecap=\"round\"\n                              strokeLinejoin=\"round\"\n                              strokeWidth={2}\n                              d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n                            />\n                          </svg>\n                        )}\n                      </div>\n                    </div>\n                  )}\n                </RadioGroup.Option>\n              )\n            })}\n          </div>\n        </fieldset>\n      </RadioGroup>\n      <a href=\"/\">Link after</a>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/styles.css",
    "content": "@import 'tailwindcss';\n@plugin '@tailwindcss/forms';\n@plugin '@tailwindcss/typography';\n@plugin '@headlessui/tailwindcss';\n\n/*\n  The default border color has changed to `currentcolor` in Tailwind CSS v4,\n  so we've added these compatibility styles to make sure everything still\n  looks the same as it did with Tailwind CSS v3.\n\n  If we ever want to remove these styles, we need to add an explicit border\n  color utility to any element that depends on these defaults.\n*/\n@layer base {\n  *,\n  ::after,\n  ::before,\n  ::backdrop,\n  ::file-selector-button {\n    border-color: var(--color-gray-200, currentcolor);\n  }\n}\n"
  },
  {
    "path": "playgrounds/react/pages/suspense/portal.tsx",
    "content": "'use client'\n\nimport { Portal } from '@headlessui/react'\nimport { lazy, Suspense } from 'react'\n\nfunction MyComponent({ children }: { children(message: string): React.JSX.Element }) {\n  return <>{children('test')}</>\n}\n\nlet MyComponentLazy = lazy(async () => {\n  await new Promise((resolve) => setTimeout(resolve, 4000))\n\n  return { default: MyComponent }\n})\n\nexport default function Index() {\n  return (\n    <div>\n      <h1 className=\"p-8 text-3xl font-bold\">Suspense + Portals</h1>\n\n      <Portal>\n        <div className=\"absolute right-48 top-24 z-10 flex h-32 w-32 flex-col items-center justify-center rounded-sm border border-black/5 bg-white bg-clip-padding p-px shadow-sm\">\n          <div className=\"w-full rounded-t-sm bg-gray-100 p-1 text-center text-gray-700\">\n            Instant\n          </div>\n          <div className=\"flex w-full flex-1 items-center justify-center text-3xl font-bold text-gray-400\">\n            1\n          </div>\n        </div>\n      </Portal>\n      <Portal>\n        <div className=\"absolute right-8 top-24 z-10 flex h-32 w-32 flex-col items-center justify-center rounded-sm border border-black/5 bg-white bg-clip-padding p-px shadow-sm\">\n          <div className=\"w-full rounded-t-sm bg-gray-100 p-1 text-center text-gray-700\">\n            Instant\n          </div>\n          <div className=\"flex w-full flex-1 items-center justify-center text-3xl font-bold text-gray-400\">\n            2\n          </div>\n        </div>\n      </Portal>\n\n      <Suspense fallback={<span>Loading ...</span>}>\n        <MyComponentLazy>\n          {(env) => (\n            <div>\n              <Portal>\n                <div className=\"absolute right-48 top-64 z-10 flex h-32 w-32 flex-col items-center justify-center rounded-sm border border-black/5 bg-white bg-clip-padding p-px shadow-sm\">\n                  <div className=\"w-full rounded-t-sm bg-gray-100 p-1 text-center text-gray-700\">\n                    Suspense\n                  </div>\n                  <div className=\"flex w-full flex-1 items-center justify-center text-3xl font-bold text-gray-400\">\n                    {env} 1\n                  </div>\n                </div>\n              </Portal>\n              <Portal>\n                <div className=\"absolute right-8 top-64 z-10 flex h-32 w-32 flex-col items-center justify-center rounded-sm border border-black/5 bg-white bg-clip-padding p-px shadow-sm\">\n                  <div className=\"w-full rounded-t-sm bg-gray-100 p-1 text-center text-gray-700\">\n                    Suspense\n                  </div>\n                  <div className=\"flex w-full flex-1 items-center justify-center text-3xl font-bold text-gray-400\">\n                    {env} 2\n                  </div>\n                </div>\n              </Portal>\n            </div>\n          )}\n        </MyComponentLazy>\n      </Suspense>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/switch/switch-with-pure-tailwind.tsx",
    "content": "import { Switch } from '@headlessui/react'\nimport { useState } from 'react'\n\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let [state, setState] = useState(false)\n\n  return (\n    <div className=\"flex h-full w-screen items-start justify-center bg-gray-50 p-12\">\n      <Switch.Group as=\"div\" className=\"flex items-center space-x-4\">\n        <Switch.Label>Enable notifications</Switch.Label>\n\n        <Switch\n          as=\"button\"\n          checked={state}\n          onChange={setState}\n          className={({ checked }) =>\n            classNames(\n              'focus:shadow-outline focus:outline-hidden relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out',\n              checked ? 'bg-indigo-600 hover:bg-indigo-800' : 'bg-gray-200 hover:bg-gray-400'\n            )\n          }\n        >\n          {({ checked }) => (\n            <>\n              <span\n                className={classNames(\n                  'inline-block h-5 w-5 transform rounded-full bg-white transition duration-200 ease-in-out',\n                  checked ? 'translate-x-5' : 'translate-x-0'\n                )}\n              />\n            </>\n          )}\n        </Switch>\n      </Switch.Group>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/tabs/tabs-with-pure-tailwind.tsx",
    "content": "import { Switch, Tab } from '@headlessui/react'\nimport { useState } from 'react'\n\nimport { classNames } from '../../utils/class-names'\n\nexport default function Home() {\n  let tabs = [\n    { name: 'My Account', content: 'Tab content for my account' },\n    { name: 'Company', content: 'Tab content for company', disabled: true },\n    { name: 'Team Members', content: 'Tab content for team members' },\n    { name: 'Billing', content: 'Tab content for billing' },\n  ]\n\n  let [manual, setManual] = useState(false)\n\n  return (\n    <div className=\"flex h-full w-screen flex-col items-start space-y-12 bg-gray-50 p-12\">\n      <Switch.Group as=\"div\" className=\"flex items-center space-x-4\">\n        <Switch.Label>Manual keyboard activation</Switch.Label>\n\n        <Switch\n          as=\"button\"\n          checked={manual}\n          onChange={setManual}\n          className={({ checked }) =>\n            classNames(\n              'focus:shadow-outline focus:outline-hidden relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out',\n              checked ? 'bg-indigo-600' : 'bg-gray-200'\n            )\n          }\n        >\n          {({ checked }) => (\n            <span\n              className={classNames(\n                'inline-block h-5 w-5 transform rounded-full bg-white transition duration-200 ease-in-out',\n                checked ? 'translate-x-5' : 'translate-x-0'\n              )}\n            />\n          )}\n        </Switch>\n      </Switch.Group>\n\n      <Tab.Group\n        className=\"flex w-full max-w-3xl flex-col\"\n        as=\"div\"\n        manual={manual}\n        defaultIndex={2}\n      >\n        <Tab.List className=\"relative z-0 flex divide-x divide-gray-200 rounded-lg shadow-sm\">\n          {tabs.map((tab, tabIdx) => (\n            <Tab\n              key={tab.name}\n              disabled={tab.disabled}\n              className={({ selected }) =>\n                classNames(\n                  selected ? 'text-gray-900' : 'text-gray-500 hover:text-gray-700',\n                  tabIdx === 0 ? 'rounded-l-lg' : '',\n                  tabIdx === tabs.length - 1 ? 'rounded-r-lg' : '',\n                  tab.disabled && 'opacity-50',\n                  'group relative min-w-0 flex-1 overflow-hidden bg-white px-4 py-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10'\n                )\n              }\n            >\n              {({ selected }) => (\n                <>\n                  <span>{tab.name}</span>\n                  {tab.disabled && <small className=\"inline-block px-4 text-xs\">(disabled)</small>}\n                  <span\n                    aria-hidden=\"true\"\n                    className={classNames(\n                      selected ? 'bg-indigo-500' : 'bg-transparent',\n                      'absolute inset-x-0 bottom-0 h-0.5'\n                    )}\n                  />\n                </>\n              )}\n            </Tab>\n          ))}\n        </Tab.List>\n\n        <Tab.Panels className=\"mt-4\">\n          {tabs.map((tab) => (\n            <Tab.Panel className=\"rounded-lg bg-white p-4 shadow-sm\" key={tab.name}>\n              {tab.content}\n            </Tab.Panel>\n          ))}\n        </Tab.Panels>\n      </Tab.Group>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/appear.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { Fragment, useState } from 'react'\nimport { Button } from '../../components/button'\n\nexport default function AppearExample() {\n  let [show, setShow] = useState(true)\n  let [lazy, setLazy] = useState(false)\n\n  return (\n    <div className=\"space-y-4 p-8\">\n      <div className=\"flex items-center gap-3\">\n        <Button onClick={() => setShow((v) => !v)}>Toggle show</Button>\n        <Button onClick={() => setLazy((v) => !v)}>Toggle lazy</Button>\n      </div>\n\n      <div className=\"flex items-center gap-4\">\n        <div className=\"rounded-md bg-white p-4 shadow-sm ring-1 ring-black/5\">\n          <span className=\"mb-2\">Initial render</span>\n          <div className=\"grid max-w-6xl grid-cols-4 gap-4\">\n            <Transition\n              as=\"div\"\n              show={show}\n              appear={true}\n              unmount={true}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n              className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n            >\n              Appear: true, unmount: true\n            </Transition>\n\n            <Transition\n              as={Fragment}\n              show={show}\n              appear={true}\n              unmount={true}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n            >\n              <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                Appear: true, as={`Fragment`}, unmount: true\n              </div>\n            </Transition>\n\n            <Transition\n              as=\"div\"\n              show={show}\n              appear={false}\n              unmount={true}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n              className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n            >\n              Appear: false, unmount: true\n            </Transition>\n\n            <Transition\n              as={Fragment}\n              show={show}\n              appear={false}\n              unmount={true}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n            >\n              <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                Appear: false, as={`Fragment`}, unmount: true\n              </div>\n            </Transition>\n\n            <Transition\n              as=\"div\"\n              show={show}\n              appear={true}\n              unmount={false}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n              className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n            >\n              Appear: true, unmount: false\n            </Transition>\n\n            <Transition\n              as={Fragment}\n              show={show}\n              appear={true}\n              unmount={false}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n            >\n              <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                Appear: true, as={`Fragment`}, unmount: false\n              </div>\n            </Transition>\n\n            <Transition\n              as=\"div\"\n              show={show}\n              appear={false}\n              unmount={false}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n              className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n            >\n              Appear: false, unmount: false\n            </Transition>\n\n            <Transition\n              as={Fragment}\n              show={show}\n              appear={false}\n              unmount={false}\n              enter=\"duration-1000 transition\"\n              enterFrom=\"opacity-0 scale-95\"\n              enterTo=\"opacity-100 scale-100\"\n              leave=\"duration-1000 transition\"\n              leaveFrom=\"opacity-100 scale-100\"\n              leaveTo=\"opacity-0 scale-95\"\n            >\n              <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                Appear: false, as={`Fragment`}, unmount: false\n              </div>\n            </Transition>\n          </div>\n        </div>\n\n        {lazy && (\n          <div className=\"rounded-md bg-white p-4 shadow-sm ring-1 ring-black/5\">\n            <span className=\"mb-2\">Not on the initial render</span>\n            <div className=\"grid max-w-6xl grid-cols-4 gap-4\">\n              <Transition\n                as=\"div\"\n                show={show}\n                appear={true}\n                unmount={true}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n                className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n              >\n                Appear: true, unmount: true\n              </Transition>\n\n              <Transition\n                as={Fragment}\n                show={show}\n                appear={true}\n                unmount={true}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n              >\n                <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                  Appear: true, as={`Fragment`}, unmount: true\n                </div>\n              </Transition>\n\n              <Transition\n                as=\"div\"\n                show={show}\n                appear={false}\n                unmount={true}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n                className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n              >\n                Appear: false, unmount: true\n              </Transition>\n\n              <Transition\n                as={Fragment}\n                show={show}\n                appear={false}\n                unmount={true}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n              >\n                <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                  Appear: false, as={`Fragment`}, unmount: true\n                </div>\n              </Transition>\n\n              <Transition\n                as=\"div\"\n                show={show}\n                appear={true}\n                unmount={false}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n                className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n              >\n                Appear: true, unmount: false\n              </Transition>\n\n              <Transition\n                as={Fragment}\n                show={show}\n                appear={true}\n                unmount={false}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n              >\n                <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                  Appear: true, as={`Fragment`}, unmount: false\n                </div>\n              </Transition>\n\n              <Transition\n                as=\"div\"\n                show={show}\n                appear={false}\n                unmount={false}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n                className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\"\n              >\n                Appear: false, unmount: false\n              </Transition>\n\n              <Transition\n                as={Fragment}\n                show={show}\n                appear={false}\n                unmount={false}\n                enter=\"duration-1000 transition\"\n                enterFrom=\"opacity-0 scale-95\"\n                enterTo=\"opacity-100 scale-100\"\n                leave=\"duration-1000 transition\"\n                leaveFrom=\"opacity-100 scale-100\"\n                leaveTo=\"opacity-0 scale-95\"\n              >\n                <div className=\"aspect-square flex-1 rounded-md bg-blue-200 p-4\">\n                  Appear: false, as={`Fragment`}, unmount: false\n                </div>\n              </Transition>\n            </div>\n          </div>\n        )}\n      </div>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/both-apis.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport clsx from 'clsx'\nimport { useState } from 'react'\n\nexport default function Example() {\n  const [open, setOpen] = useState(false)\n  return (\n    <div className=\"grid min-h-full place-content-center\">\n      <div className=\"flex flex-col\">\n        <button onClick={() => setOpen((open) => !open)}>Toggle transition</button>\n        <div className=\"flex h-20 w-80\">\n          <Before open={open} />\n          <After open={open} />\n        </div>\n      </div>\n    </div>\n  )\n}\n\nfunction Before({ open }: { open: boolean }) {\n  return (\n    <Transition\n      show={open}\n      enter=\"transition ease-in-out duration-300\"\n      enterFrom=\"opacity-0 -translate-x-full\"\n      enterTo=\"opacity-100 translate-x-0\"\n      leave=\"transition ease-in-out duration-300\"\n      leaveFrom=\"opacity-100 translate-x-0\"\n      leaveTo=\"opacity-0 translate-x-full\"\n    >\n      <div className=\"h-20 w-48 border bg-blue-500 p-2 text-white\">Using specific props</div>\n    </Transition>\n  )\n}\n\nfunction After({ open }: { open: boolean }) {\n  return (\n    <Transition show={open}>\n      <div\n        className={clsx([\n          // Defaults\n          'h-20 w-48 border bg-blue-500 p-2 text-white transition ease-in-out',\n          // Closed\n          'data-closed:opacity-0',\n          // Entering\n          'data-enter:duration-300 data-enter:data-closed:-translate-x-full',\n          // Leaving\n          'data-leave:duration-300 data-leave:data-closed:translate-x-full',\n        ])}\n      >\n        Using data attributes\n      </div>\n    </Transition>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/component-examples/dropdown.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport Head from 'next/head'\nimport { useState } from 'react'\n\nexport default function Home() {\n  return (\n    <>\n      <Head>\n        <title>Transition Component - Playground</title>\n      </Head>\n\n      <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n        <Dropdown />\n      </div>\n    </>\n  )\n}\n\nfunction Dropdown() {\n  let [isOpen, setIsOpen] = useState(false)\n\n  return (\n    <div className=\"relative inline-block text-left\">\n      <div>\n        <span className=\"shadow-xs rounded-md\">\n          <button\n            type=\"button\"\n            className=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n            id=\"options-menu\"\n            aria-haspopup=\"true\"\n            aria-expanded={isOpen}\n            onClick={() => setIsOpen((v) => !v)}\n          >\n            Options\n            <svg className=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </button>\n        </span>\n      </div>\n\n      <Transition\n        as=\"div\"\n        show={isOpen}\n        enter=\"transition ease-out duration-75\"\n        enterFrom=\"transform opacity-0 scale-95\"\n        enterTo=\"transform opacity-100 scale-100\"\n        leave=\"transition ease-in duration-150\"\n        leaveFrom=\"transform opacity-100 scale-100\"\n        leaveTo=\"transform opacity-0 scale-95\"\n        className=\"absolute right-0 mt-2 w-56 origin-top-right rounded-md shadow-lg\"\n      >\n        <div className=\"shadow-2xs rounded-md bg-white\">\n          <div\n            className=\"py-1\"\n            role=\"menu\"\n            aria-orientation=\"vertical\"\n            aria-labelledby=\"options-menu\"\n          >\n            <a\n              href=\"/\"\n              className=\"focus:outline-hidden block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900\"\n              role=\"menuitem\"\n            >\n              Account settings\n            </a>\n            <a\n              href=\"/\"\n              className=\"focus:outline-hidden block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900\"\n              role=\"menuitem\"\n            >\n              Support\n            </a>\n            <a\n              href=\"/\"\n              className=\"focus:outline-hidden block px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900\"\n              role=\"menuitem\"\n            >\n              License\n            </a>\n            <form method=\"POST\" action=\"#\">\n              <button\n                type=\"submit\"\n                className=\"focus:outline-hidden block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900\"\n                role=\"menuitem\"\n              >\n                Sign out\n              </button>\n            </form>\n          </div>\n        </div>\n      </Transition>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/component-examples/modal.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { useRef, useState } from 'react'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(false)\n  function toggle() {\n    setIsOpen((v) => !v)\n  }\n\n  let [email, setEmail] = useState('')\n  let [events, setEvents] = useState([])\n  let inputRef = useRef(null)\n\n  function addEvent(name) {\n    setEvents((existing) => [...existing, `${new Date().toJSON()} - ${name}`])\n  }\n\n  return (\n    <div>\n      <div className=\"flex space-x-4 p-12\">\n        <div className=\"inline-block p-12\">\n          <span className=\"shadow-xs mt-3 flex w-full rounded-md sm:mt-0 sm:w-auto\">\n            <button\n              onClick={toggle}\n              type=\"button\"\n              className=\"focus:shadow-outline-blue shadow-xs focus:outline-hidden inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 sm:text-sm sm:leading-5\"\n            >\n              Show modal\n            </button>\n          </span>\n        </div>\n\n        <ul className=\"bg-gray-200 p-4 text-gray-900\">\n          <h3 className=\"font-bold\">Events:</h3>\n          {events.map((event, i) => (\n            <li key={i} className=\"font-mono text-sm\">\n              {event}\n            </li>\n          ))}\n        </ul>\n      </div>\n\n      <Transition\n        as=\"div\"\n        show={isOpen}\n        className=\"fixed inset-0 z-10 overflow-y-auto\"\n        beforeEnter={() => {\n          addEvent('[Root] Before enter')\n        }}\n        afterEnter={() => {\n          inputRef.current?.focus()\n          addEvent('[Root] After enter')\n        }}\n        beforeLeave={() => {\n          addEvent('[Root] Before leave')\n        }}\n        afterLeave={() => {\n          addEvent('[Root] After leave')\n          setEmail('')\n        }}\n      >\n        <div className=\"flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0\">\n          <Transition.Child\n            enter=\"ease-out duration-300\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-100\"\n            leave=\"ease-in duration-200\"\n            leaveFrom=\"opacity-100\"\n            leaveTo=\"opacity-0\"\n            beforeEnter={() => addEvent('[Overlay] Before enter')}\n            afterEnter={() => addEvent('[Overlay] After enter')}\n            beforeLeave={() => addEvent('[Overlay] Before leave')}\n            afterLeave={() => addEvent('[Overlay] After leave')}\n          >\n            <div className=\"fixed inset-0 transition-opacity\">\n              <div className=\"absolute inset-0 bg-gray-500 opacity-75\"></div>\n            </div>\n          </Transition.Child>\n          {/* This element is to trick the browser into centering the modal contents. */}\n          <span className=\"hidden sm:inline-block sm:h-screen sm:align-middle\"></span>&#8203;\n          <Transition.Child\n            as=\"div\"\n            className=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\"\n            role=\"dialog\"\n            aria-modal=\"true\"\n            aria-labelledby=\"modal-headline\"\n            enter=\"ease-out duration-300\"\n            enterFrom=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n            enterTo=\"opacity-100 translate-y-0 sm:scale-100\"\n            leave=\"ease-in duration-200\"\n            leaveFrom=\"opacity-100 translate-y-0 sm:scale-100\"\n            leaveTo=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n            beforeEnter={() => addEvent('[Panel] Before enter')}\n            afterEnter={() => addEvent('[Panel] After enter')}\n            beforeLeave={() => addEvent('[Panel] Before leave')}\n            afterLeave={() => addEvent('[Panel] After leave')}\n          >\n            <div className=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n              <div className=\"sm:flex sm:items-start\">\n                <div className=\"mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10\">\n                  {/* Heroicon name: exclamation */}\n                  <svg\n                    className=\"h-6 w-6 text-red-600\"\n                    xmlns=\"http://www.w3.org/2000/svg\"\n                    fill=\"none\"\n                    viewBox=\"0 0 24 24\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                      strokeWidth=\"2\"\n                      d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n                    />\n                  </svg>\n                </div>\n                <div className=\"mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left\">\n                  <h3 className=\"text-lg font-medium leading-6 text-gray-900\" id=\"modal-headline\">\n                    Deactivate account\n                  </h3>\n                  <div className=\"mt-2\">\n                    <p className=\"text-sm leading-5 text-gray-500\">\n                      Are you sure you want to deactivate your account? All of your data will be\n                      permanently removed. This action cannot be undone.\n                    </p>\n                  </div>\n                  <div className=\"mt-2\">\n                    <div>\n                      <label\n                        htmlFor=\"email\"\n                        className=\"block text-sm font-medium leading-5 text-gray-700\"\n                      >\n                        Email address\n                      </label>\n                      <div className=\"shadow-xs relative mt-1 rounded-md\">\n                        <input\n                          ref={inputRef}\n                          value={email}\n                          onChange={(event) => setEmail(event.target.value)}\n                          type=\"email\"\n                          id=\"email\"\n                          className=\"form-input block w-full px-3 sm:text-sm sm:leading-5\"\n                          placeholder=\"name@example.com\"\n                        />\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n            <div className=\"bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6\">\n              <span className=\"shadow-xs flex w-full rounded-md sm:ml-3 sm:w-auto\">\n                <button\n                  type=\"button\"\n                  className=\"focus:shadow-outline-red shadow-xs focus:outline-hidden inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium leading-6 text-white transition duration-150 ease-in-out hover:bg-red-500 focus:border-red-700 sm:text-sm sm:leading-5\"\n                >\n                  Deactivate\n                </button>\n              </span>\n              <span className=\"shadow-xs mt-3 flex w-full rounded-md sm:mt-0 sm:w-auto\">\n                <button\n                  onClick={toggle}\n                  type=\"button\"\n                  className=\"focus:shadow-outline-blue shadow-xs focus:outline-hidden inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 sm:text-sm sm:leading-5\"\n                >\n                  Cancel\n                </button>\n              </span>\n            </div>\n          </Transition.Child>\n        </div>\n      </Transition>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/component-examples/nested/hidden.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { ReactNode, useState } from 'react'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(true)\n\n  return (\n    <>\n      <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n        <div className=\"w-96 space-y-2\">\n          <span className=\"shadow-xs inline-flex rounded-md\">\n            <button\n              type=\"button\"\n              onClick={() => setIsOpen((v) => !v)}\n              className=\"duration-150-out focus:shadow-outline-blue focus:outline-hidden inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition hover:text-gray-500 focus:border-blue-300 active:bg-gray-50 active:text-gray-800\"\n            >\n              {isOpen ? 'Hide' : 'Show'}\n            </button>\n          </span>\n\n          <Transition as=\"div\" show={isOpen} unmount={false}>\n            <Box>\n              <Box>\n                <Box>\n                  <Box />\n                </Box>\n                <Box>\n                  <Box>\n                    <Box>\n                      <Box />\n                    </Box>\n                  </Box>\n                </Box>\n              </Box>\n            </Box>\n          </Transition>\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction Box({ children }: { children?: ReactNode }) {\n  return (\n    <Transition.Child\n      unmount={false}\n      enter=\"transition translate duration-300\"\n      enterFrom=\"transform -translate-x-full\"\n      enterTo=\"transform translate-x-0\"\n      leave=\"transition translate duration-300\"\n      leaveFrom=\"transform translate-x-0\"\n      leaveTo=\"transform translate-x-full\"\n    >\n      <div className=\"space-y-2 rounded-md bg-white p-4 text-sm font-semibold uppercase tracking-wide text-gray-700 shadow-sm\">\n        <span>This is a box</span>\n        {children}\n      </div>\n    </Transition.Child>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/component-examples/nested/unmount.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { ReactNode, useState } from 'react'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(true)\n\n  return (\n    <>\n      <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n        <div className=\"w-96 space-y-2\">\n          <span className=\"shadow-xs inline-flex rounded-md\">\n            <button\n              type=\"button\"\n              onClick={() => setIsOpen((v) => !v)}\n              className=\"duration-150-out focus:shadow-outline-blue focus:outline-hidden inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition hover:text-gray-500 focus:border-blue-300 active:bg-gray-50 active:text-gray-800\"\n            >\n              {isOpen ? 'Hide' : 'Show'}\n            </button>\n          </span>\n\n          <Transition as=\"div\" show={isOpen} unmount={true}>\n            <Box>\n              <Box>\n                <Box>\n                  <Box />\n                </Box>\n                <Box>\n                  <Box>\n                    <Box>\n                      <Box />\n                    </Box>\n                  </Box>\n                </Box>\n              </Box>\n            </Box>\n          </Transition>\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction Box({ children }: { children?: ReactNode }) {\n  return (\n    <Transition.Child\n      as=\"div\"\n      unmount={true}\n      enter=\"transition translate duration-300\"\n      enterFrom=\"transform -translate-x-full\"\n      enterTo=\"transform translate-x-0\"\n      leave=\"transition translate duration-300\"\n      leaveFrom=\"transform translate-x-0\"\n      leaveTo=\"transform translate-x-full\"\n    >\n      <div className=\"space-y-2 rounded-md bg-white p-4 text-sm font-semibold uppercase tracking-wide text-gray-700 shadow-sm\">\n        <span>This is a box</span>\n        {children}\n      </div>\n    </Transition.Child>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/component-examples/peek-a-boo.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { useState } from 'react'\n\nexport default function Home() {\n  let [isOpen, setIsOpen] = useState(true)\n\n  return (\n    <>\n      <div className=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n        <div className=\"w-96 space-y-2\">\n          <span className=\"shadow-xs inline-flex rounded-md\">\n            <button\n              type=\"button\"\n              onClick={() => setIsOpen((v) => !v)}\n              className=\"focus:shadow-outline-blue focus:outline-hidden inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium leading-4 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 active:bg-gray-50 active:text-gray-800\"\n            >\n              {isOpen ? 'Hide' : 'Show'}\n            </button>\n          </span>\n\n          <Transition\n            as=\"div\"\n            show={isOpen}\n            appear={false}\n            beforeEnter={() => console.log('beforeEnter')}\n            afterEnter={() => console.log('afterEnter')}\n            beforeLeave={() => console.log('beforeLeave')}\n            afterLeave={() => console.log('afterLeave')}\n            enter=\"transition-colors ease-out duration-[5s]\"\n            enterFrom=\"transform bg-red-500\"\n            enterTo=\"transform bg-blue-500\"\n            leave=\"transition-colors ease-in duration-[5s]\"\n            leaveFrom=\"transform bg-blue-500\"\n            leaveTo=\"transform bg-red-500\"\n            entered=\"bg-blue-500\"\n            className=\"h-64 rounded-md p-4 shadow-sm\"\n          >\n            Contents to show and hide\n          </Transition>\n        </div>\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/full-page-examples/full-page-transition.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport Head from 'next/head'\nimport { useEffect, useRef, useState } from 'react'\n\nimport { classNames } from '../../../utils/class-names'\nimport { match } from '../../../utils/match'\n\nexport default function Shell() {\n  return (\n    <>\n      <Head>\n        <title>Transition Component - Full Page Transition</title>\n      </Head>\n      <div className=\"h-full bg-gray-50 p-12\">\n        <div className=\"flex h-full flex-1 flex-col overflow-hidden rounded-lg shadow-lg\">\n          <FullPageTransition />\n        </div>\n      </div>\n    </>\n  )\n}\n\nfunction usePrevious<T>(value: T) {\n  let ref = useRef(value)\n  useEffect(() => {\n    ref.current = value\n  }, [value])\n  return ref.current\n}\n\nenum Direction {\n  Forwards = ' -> ',\n  Backwards = ' <- ',\n}\n\nlet pages = ['Dashboard', 'Team', 'Projects', 'Calendar', 'Reports']\nlet colors = [\n  'bg-linear-to-r from-teal-400 to-blue-400',\n  'bg-linear-to-r from-blue-400 to-orange-400',\n  'bg-linear-to-r from-orange-400 to-purple-400',\n  'bg-linear-to-r from-purple-400 to-green-400',\n  'bg-linear-to-r from-green-400 to-teal-400',\n]\n\nfunction FullPageTransition() {\n  let [activePage, setActivePage] = useState(0)\n  let previousPage = usePrevious(activePage)\n\n  let direction = activePage > previousPage ? Direction.Forwards : Direction.Backwards\n\n  let transitions = match(direction, {\n    [Direction.Forwards]: {\n      enter: 'transition transform ease-in-out duration-500',\n      enterFrom: 'translate-x-full',\n      enterTo: 'translate-x-0',\n      leave: 'transition transform ease-in-out duration-500',\n      leaveFrom: 'translate-x-0',\n      leaveTo: '-translate-x-full',\n    },\n    [Direction.Backwards]: {\n      enter: 'transition transform ease-in-out duration-500',\n      enterFrom: '-translate-x-full',\n      enterTo: 'translate-x-0',\n      leave: 'transition transform ease-in-out duration-500',\n      leaveFrom: 'translate-x-0',\n      leaveTo: 'translate-x-full',\n    },\n  })\n\n  return (\n    <div>\n      <div className=\"bg-gray-800 pb-32\">\n        <nav className=\"bg-gray-800\">\n          <div className=\"mx-auto max-w-7xl sm:px-6 lg:px-8\">\n            <div className=\"border-b border-gray-700\">\n              <div className=\"flex h-16 items-center justify-between px-4 sm:px-0\">\n                <div className=\"flex items-center\">\n                  <div className=\"shrink-0\">\n                    <img\n                      className=\"h-8 w-8\"\n                      src=\"https://tailwindui.com/img/logos/workflow-mark-on-dark.svg\"\n                      alt=\"Workflow logo\"\n                    />\n                  </div>\n                  <div className=\"hidden md:block\">\n                    <div className=\"ml-10 flex items-baseline space-x-4\">\n                      {pages.map((page, i) => (\n                        <button\n                          key={page}\n                          onClick={() => setActivePage(i)}\n                          className={classNames(\n                            'focus:outline-hidden rounded-md px-3 py-2 text-sm font-medium focus:bg-gray-700 focus:text-white',\n                            i === activePage\n                              ? 'bg-gray-900 text-white'\n                              : 'text-gray-300 hover:bg-gray-700 hover:text-white'\n                          )}\n                        >\n                          {page}\n                        </button>\n                      ))}\n                    </div>\n                  </div>\n                </div>\n                <div className=\"hidden md:block\">\n                  <div className=\"ml-4 flex items-center md:ml-6\">\n                    <button\n                      className=\"focus:outline-hidden rounded-full border-2 border-transparent p-1 text-gray-400 hover:text-white focus:bg-gray-700 focus:text-white\"\n                      aria-label=\"Notifications\"\n                    >\n                      <svg\n                        className=\"h-6 w-6\"\n                        stroke=\"currentColor\"\n                        fill=\"none\"\n                        viewBox=\"0 0 24 24\"\n                      >\n                        <path\n                          strokeLinecap=\"round\"\n                          strokeLinejoin=\"round\"\n                          strokeWidth=\"2\"\n                          d=\"M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9\"\n                        />\n                      </svg>\n                    </button>\n\n                    {/* Profile dropdown */}\n                    <div className=\"relative ml-3\">\n                      <div>\n                        <button\n                          className=\"focus:shadow-solid focus:outline-hidden flex max-w-xs items-center rounded-full text-sm text-white\"\n                          id=\"user-menu\"\n                          aria-label=\"User menu\"\n                          aria-haspopup=\"true\"\n                        >\n                          <img\n                            className=\"h-8 w-8 rounded-full\"\n                            src=\"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80\"\n                            alt=\"\"\n                          />\n                        </button>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </nav>\n        <header className=\"py-10\">\n          <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n            <h1 className=\"relative inline-block text-3xl font-bold leading-9 text-white\">\n              {pages[activePage]}\n            </h1>\n          </div>\n        </header>\n      </div>\n\n      <main className=\"-mt-32\">\n        <div className=\"mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8\">\n          <div className=\"rounded-lg bg-white px-5 py-6 shadow-sm sm:px-6\">\n            <div className=\"relative h-96 overflow-hidden rounded-lg\">\n              {pages.map((page, i) => (\n                <Transition\n                  as=\"div\"\n                  appear={false}\n                  key={page}\n                  show={activePage === i}\n                  className={classNames(\n                    'absolute inset-0 rounded-lg p-8 text-3xl font-bold text-white',\n                    colors[i]\n                  )}\n                  {...transitions}\n                >\n                  {page} page content\n                </Transition>\n              ))}\n            </div>\n          </div>\n        </div>\n      </main>\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/full-page-examples/layout-with-sidebar.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport Head from 'next/head'\nimport { useEffect, useState } from 'react'\n\nexport default function App() {\n  let [mobileOpen, setMobileOpen] = useState(false)\n\n  useEffect(() => {\n    function handleEscape(event) {\n      if (!mobileOpen) return\n\n      if (event.key === 'Escape') {\n        setMobileOpen(false)\n      }\n    }\n\n    document.addEventListener('keyup', handleEscape)\n    return () => document.removeEventListener('keyup', handleEscape)\n  }, [mobileOpen])\n\n  return (\n    <>\n      <Head>\n        <title>Transition Component - Layout with sidebar</title>\n      </Head>\n\n      <div className=\"bg-cool-gray-100 flex h-screen overflow-hidden\">\n        {/* Off-canvas menu for mobile */}\n        <Transition as=\"div\" show={mobileOpen} unmount={false} className=\"fixed inset-0 z-40 flex\">\n          {/* Off-canvas menu overlay, show/hide based on off-canvas menu state. */}\n          <Transition.Child\n            as=\"div\"\n            unmount={false}\n            enter=\"transition-opacity ease-linear duration-300\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-100\"\n            leave=\"transition-opacity ease-linear duration-300\"\n            leaveFrom=\"opacity-100\"\n            leaveTo=\"opacity-0\"\n          >\n            {() => (\n              <div className=\"fixed inset-0\">\n                <div\n                  onClick={() => setMobileOpen(false)}\n                  className=\"bg-cool-gray-600 absolute inset-0 opacity-75\"\n                />\n              </div>\n            )}\n          </Transition.Child>\n\n          {/* Off-canvas menu, show/hide based on off-canvas menu state. */}\n          <Transition.Child\n            as=\"div\"\n            unmount={false}\n            enter=\"transition ease-in-out duration-300 transform\"\n            enterFrom=\"-translate-x-full\"\n            enterTo=\"translate-x-0\"\n            leave=\"transition ease-in-out duration-300 transform\"\n            leaveFrom=\"translate-x-0\"\n            leaveTo=\"-translate-x-full\"\n            className=\"relative flex w-full max-w-xs flex-1 flex-col bg-teal-600 pb-4 pt-5\"\n          >\n            <div className=\"absolute right-0 top-0 -mr-14 p-1\">\n              <Transition.Child\n                unmount={false}\n                className=\"focus:bg-cool-gray-600 focus:outline-hidden flex h-12 w-12 items-center justify-center rounded-full\"\n                aria-label=\"Close sidebar\"\n                as=\"button\"\n                onClick={() => setMobileOpen(false)}\n              >\n                <svg\n                  className=\"h-6 w-6 text-white\"\n                  stroke=\"currentColor\"\n                  fill=\"none\"\n                  viewBox=\"0 0 24 24\"\n                >\n                  <path\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    strokeWidth=\"2\"\n                    d=\"M6 18L18 6M6 6l12 12\"\n                  />\n                </svg>\n              </Transition.Child>\n            </div>\n            <div className=\"flex shrink-0 items-center px-4\">\n              <img\n                className=\"h-8 w-auto\"\n                src=\"https://tailwindui.com/img/logos/easywire-logo-on-brand.svg\"\n                alt=\"Easywire logo\"\n              />\n            </div>\n          </Transition.Child>\n          <div className=\"w-14 shrink-0\">\n            {/* Dummy element to force sidebar to shrink to fit close icon */}\n          </div>\n        </Transition>\n\n        {/* Static sidebar for desktop */}\n        <div className=\"hidden lg:flex lg:shrink-0\">\n          <div className=\"flex w-64 flex-col\">\n            {/* Sidebar component, swap this element with another sidebar if you like */}\n            <div className=\"flex grow flex-col overflow-y-auto bg-teal-600 pb-4 pt-5\">\n              <div className=\"flex shrink-0 items-center px-4\">\n                <img\n                  className=\"h-8 w-auto\"\n                  src=\"https://tailwindui.com/img/logos/easywire-logo-on-brand.svg\"\n                  alt=\"Easywire logo\"\n                />\n              </div>\n            </div>\n          </div>\n        </div>\n        <div className=\"focus:outline-hidden flex-1 overflow-auto\" tabIndex={0}>\n          <div className=\"relative z-10 flex h-16 shrink-0 border-b border-gray-200 bg-white lg:border-none\">\n            <button\n              className=\"border-cool-gray-200 text-cool-gray-400 focus:bg-cool-gray-100 focus:text-cool-gray-600 focus:outline-hidden border-r px-4 lg:hidden\"\n              aria-label=\"Open sidebar\"\n              onClick={() => setMobileOpen(true)}\n            >\n              <svg\n                className=\"h-6 w-6 transition duration-150 ease-in-out\"\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"currentColor\"\n              >\n                <path\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"2\"\n                  d=\"M4 6h16M4 12h8m-8 6h16\"\n                />\n              </svg>\n            </button>\n            {/* Search bar */}\n            <div className=\"flex flex-1 justify-between px-4 sm:px-6 lg:mx-auto lg:max-w-6xl lg:px-8\">\n              <div className=\"flex flex-1\">\n                <form className=\"flex w-full md:ml-0\" action=\"#\" method=\"GET\">\n                  <label htmlFor=\"search_field\" className=\"sr-only\">\n                    Search\n                  </label>\n                  <div className=\"text-cool-gray-400 focus-within:text-cool-gray-600 relative w-full\">\n                    <div className=\"pointer-events-none absolute inset-y-0 left-0 flex items-center\">\n                      <svg className=\"h-5 w-5\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n                        <path\n                          fillRule=\"evenodd\"\n                          clipRule=\"evenodd\"\n                          d=\"M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z\"\n                        />\n                      </svg>\n                    </div>\n                    <input\n                      id=\"search_field\"\n                      className=\"text-cool-gray-900 placeholder-cool-gray-500 focus:placeholder-cool-gray-400 focus:outline-hidden block h-full w-full rounded-md py-2 pl-8 pr-3 sm:text-sm\"\n                      placeholder=\"Search\"\n                      type=\"search\"\n                    />\n                  </div>\n                </form>\n              </div>\n            </div>\n          </div>\n          <main className=\"relative z-0 flex-1 overflow-y-auto p-8\">\n            {/* Replace with your content */}\n            <div className=\"h-96 rounded-lg border-4 border-dashed border-gray-200\"></div>\n            {/* /End replace */}\n          </main>\n        </div>\n      </div>\n    </>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/pages/transitions/react-hot-toast.tsx",
    "content": "import { Transition } from '@headlessui/react'\nimport { resolveValue, toast, Toaster, ToastIcon } from 'react-hot-toast'\n\nconst TailwindToaster = () => {\n  return (\n    <Toaster position=\"top-right\">\n      {(t) => (\n        <Transition\n          appear\n          as=\"div\"\n          show={t.visible}\n          className=\"flex transform rounded-sm bg-white p-4 shadow-lg\"\n          enter=\"transition-all duration-500\"\n          enterFrom=\"opacity-0 scale-50\"\n          enterTo=\"opacity-100 scale-100\"\n          leave=\"transition-all duration-150\"\n          leaveFrom=\"opacity-100 scale-100\"\n          leaveTo=\"opacity-0 scale-75\"\n        >\n          <ToastIcon toast={t} />\n          <p className=\"px-2\">{resolveValue(t.message, t)}</p>\n        </Transition>\n      )}\n    </Toaster>\n  )\n}\n\nexport default function App() {\n  return (\n    <div className=\"m-8\">\n      <button\n        className=\"rounded-sm bg-blue-500 p-4 text-white\"\n        onClick={() => toast.success('This is Tailwind CSS')}\n      >\n        Create TailwindCSS Toast\n      </button>\n      <TailwindToaster />\n    </div>\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    '@tailwindcss/postcss': {},\n  },\n}\n"
  },
  {
    "path": "playgrounds/react/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"incremental\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"downlevelIteration\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\"\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "playgrounds/react/utils/class-names.ts",
    "content": "export function classNames(...classes: (false | null | undefined | string)[]): string {\n  return classes.filter(Boolean).join(' ')\n}\n"
  },
  {
    "path": "playgrounds/react/utils/hooks/use-popper.ts",
    "content": "import { createPopper, Options } from '@popperjs/core'\nimport { RefCallback, useCallback, useMemo, useRef } from 'react'\n\n/**\n * Example implementation to use Popper: https://popper.js.org/\n */\nexport function usePopper(\n  options?: Partial<Options>\n): [RefCallback<Element | null>, RefCallback<HTMLElement | null>] {\n  let reference = useRef<Element>(null)\n  let popper = useRef<HTMLElement>(null)\n\n  let cleanupCallback = useRef(() => {})\n\n  let instantiatePopper = useCallback(() => {\n    if (!reference.current) return\n    if (!popper.current) return\n\n    if (cleanupCallback.current) cleanupCallback.current()\n\n    cleanupCallback.current = createPopper(reference.current, popper.current, options).destroy\n  }, [reference, popper, cleanupCallback, options])\n\n  return useMemo(\n    () => [\n      (referenceDomNode) => {\n        reference.current = referenceDomNode\n        instantiatePopper()\n      },\n      (popperDomNode) => {\n        popper.current = popperDomNode\n        instantiatePopper()\n      },\n    ],\n    [reference, popper, instantiatePopper]\n  )\n}\n"
  },
  {
    "path": "playgrounds/react/utils/match.ts",
    "content": "export function match<TValue extends string | number = string, TReturnValue = unknown>(\n  value: TValue,\n  lookup: Record<TValue, TReturnValue | ((...args: any[]) => TReturnValue)>,\n  ...args: any[]\n): TReturnValue {\n  if (value in lookup) {\n    let returnValue = lookup[value]\n    return typeof returnValue === 'function' ? returnValue(...args) : returnValue\n  }\n\n  let error = new Error(\n    `Tried to handle \"${value}\" but there is no handler defined. Only defined handlers are: ${Object.keys(\n      lookup\n    )\n      .map((key) => `\"${key}\"`)\n      .join(', ')}.`\n  )\n  if (Error.captureStackTrace) Error.captureStackTrace(error, match)\n  throw error\n}\n"
  },
  {
    "path": "playgrounds/react/utils/resolve-all-examples.ts",
    "content": "import fs from 'fs'\nimport path from 'path'\nexport type ExamplesType = {\n  name: string\n  path: string\n  children?: ExamplesType[]\n}\n\nexport async function resolveAllExamples(...paths: string[]) {\n  let base = path.resolve(process.cwd(), ...paths)\n\n  if (!fs.existsSync(base)) {\n    return false\n  }\n\n  let files = await fs.promises.readdir(base, { withFileTypes: true })\n  let items: ExamplesType[] = []\n\n  for (let file of files) {\n    if (file.name === '.DS_Store') {\n      continue\n    }\n\n    // Skip reserved filenames from Next. E.g.: _app.tsx, _error.tsx\n    if (file.name.startsWith('_')) {\n      continue\n    }\n\n    let bucket: ExamplesType = {\n      name: file.name.replace(/-/g, ' ').replace(/\\.tsx?/g, ''),\n      path: [...paths, file.name]\n        .join('/')\n        .replace(/^pages/, '')\n        .replace(/\\.tsx?/g, '')\n        .replace(/\\/+/g, '/'),\n    }\n\n    if (file.isDirectory()) {\n      let children = await resolveAllExamples(...paths, file.name)\n\n      if (children) {\n        bucket.children = children\n      }\n    }\n\n    items.push(bucket)\n  }\n\n  return items\n}\n"
  },
  {
    "path": "playgrounds/vue/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Headless UI - Playground</title>\n    <link rel=\"stylesheet\" href=\"https://rsms.me/inter/inter.css\" />\n  </head>\n  <body class=\"h-full w-full font-sans text-gray-900 antialiased\">\n    <div class=\"h-full w-full\" id=\"app\"></div>\n    <script type=\"module\" src=\"/src/main.ts\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "playgrounds/vue/package.json",
    "content": "{\n  \"name\": \"playground-vue\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"directories\": {\n    \"example\": \"examples\"\n  },\n  \"scripts\": {\n    \"prebuild\": \"npm run build --workspace=@headlessui/vue && npm run build --workspace=@headlessui/tailwindcss\",\n    \"predev\": \"npm run build --workspace=@headlessui/vue && npm run build --workspace=@headlessui/tailwindcss\",\n    \"dev:tailwindcss\": \"npm run watch --workspace=@headlessui/tailwindcss\",\n    \"dev:headlessui\": \"npm run watch --workspace=@headlessui/vue\",\n    \"dev:next\": \"vite serve\",\n    \"dev\": \"npm-run-all -p dev:*\",\n    \"build\": \"NODE_ENV=production vite build\",\n    \"lint-types\": \"echo\",\n    \"clean\": \"rimraf ./dist\"\n  },\n  \"dependencies\": {\n    \"@headlessui/vue\": \"*\",\n    \"@heroicons/vue\": \"^1.0.6\",\n    \"@tailwindcss/forms\": \"^0.5.2\",\n    \"@tailwindcss/postcss\": \"^4.1.3\",\n    \"@tailwindcss/typography\": \"^0.5.2\",\n    \"postcss\": \"^8.4.14\",\n    \"tailwindcss\": \"^4.1.3\",\n    \"vue\": \"^3.4.27\",\n    \"vue-flatpickr-component\": \"^9.0.5\",\n    \"vue-router\": \"^4.3.2\"\n  },\n  \"devDependencies\": {\n    \"@floating-ui/vue\": \"^1.0.2\",\n    \"@vitejs/plugin-vue\": \"^5.0.5\",\n    \"vite\": \"^5.2.12\"\n  }\n}\n"
  },
  {
    "path": "playgrounds/vue/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    '@tailwindcss/postcss': {},\n  },\n}\n"
  },
  {
    "path": "playgrounds/vue/src/.generated/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "playgrounds/vue/src/App.vue",
    "content": "<template>\n  <router-view v-if=\"layout === 'raw'\" />\n  <Layout v-else>\n    <router-view />\n    <KeyCaster />\n  </Layout>\n</template>\n\n<script>\nimport { computed } from 'vue'\nimport { useRoute } from 'vue-router'\nimport KeyCaster from './KeyCaster.vue'\nimport Layout from './Layout.vue'\n\nexport default {\n  name: 'App',\n  components: {\n    Layout,\n    KeyCaster,\n  },\n\n  setup() {\n    let route = useRoute()\n    let layout = computed(() => route.query['layout'] ?? 'full')\n\n    return {\n      layout,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/KeyCaster.vue",
    "content": "<template>\n  <div\n    class=\"pointer-events-none fixed bottom-4 right-4 z-50 cursor-default select-none overflow-hidden rounded-md bg-blue-800 px-4 py-2 text-2xl tracking-wide text-blue-100 shadow-sm\"\n    v-if=\"keys.length > 0\"\n  >\n    {{ keys.slice().reverse().join(' ') }}\n  </div>\n</template>\n\n<script>\nimport { defineComponent, ref } from 'vue'\n\nlet isMac = navigator.userAgent.indexOf('Mac OS X') !== -1\n\nlet KeyDisplay = isMac\n  ? {\n      ArrowUp: '↑',\n      ArrowDown: '↓',\n      ArrowLeft: '←',\n      ArrowRight: '→',\n      Home: '↖',\n      End: '↘',\n      Alt: '⌥',\n      CapsLock: '⇪',\n      Meta: '⌘',\n      Shift: '⇧',\n      Control: '⌃',\n      Backspace: '⌫',\n      Delete: '⌦',\n      Enter: '↵',\n      Escape: '⎋',\n      Tab: '⇥',\n      ShiftTab: '⇤',\n      PageUp: '⇞',\n      PageDown: '⇟',\n      ' ': '␣',\n    }\n  : {\n      ArrowUp: '↑',\n      ArrowDown: '↓',\n      ArrowLeft: '←',\n      ArrowRight: '→',\n      Meta: 'Win',\n      Control: 'Ctrl',\n      Backspace: '⌫',\n      Delete: 'Del',\n      Escape: 'Esc',\n      PageUp: 'PgUp',\n      PageDown: 'PgDn',\n      ' ': '␣',\n    }\n\nexport default defineComponent({\n  setup() {\n    let keys = ref([])\n\n    window.addEventListener('keydown', (event) => {\n      keys.value.unshift(\n        event.shiftKey && event.key !== 'Shift'\n          ? KeyDisplay[`Shift${event.key}`] ?? event.key\n          : KeyDisplay[event.key] ?? event.key\n      )\n      setTimeout(() => keys.value.pop(), 2000)\n    })\n\n    return { keys }\n  },\n})\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/Layout.vue",
    "content": "<template>\n  <div\n    class=\"flex h-screen flex-col overflow-hidden bg-gray-700 font-sans text-gray-900 antialiased\"\n  >\n    <header\n      class=\"relative z-10 flex shrink-0 items-center justify-between border-b border-gray-200 bg-gray-700 px-4 py-4 sm:px-6 lg:px-8\"\n    >\n      <router-link to=\"/\">\n        <svg class=\"h-6\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 243 42\">\n          <path\n            fill=\"#fff\"\n            d=\"M65.74 13.663c-2.62 0-4.702.958-5.974 2.95V6.499h-4.163V33.32h4.163V23.051c0-3.908 2.159-5.518 4.896-5.518 2.62 0 4.317 1.533 4.317 4.445V33.32h4.162V21.557c0-4.982-3.083-7.894-7.4-7.894zM79.936 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.674-10.116-6.052 0-10.176 4.407-10.176 10.078 0 5.748 4.124 10.078 10.484 10.078 3.778 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.925 1.341-2.66 2.376-4.972 2.376-3.084 0-5.512-1.533-6.168-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.506 0 4.934 1.418 5.512 4.713H79.898zM113.282 14.161v2.72c-1.465-1.992-3.739-3.218-6.746-3.218-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.162V14.16h-4.162zm-6.09 15.71c-3.469 0-6.091-2.567-6.091-6.13 0-3.564 2.622-6.131 6.091-6.131 3.469 0 6.09 2.567 6.09 6.13 0 3.564-2.621 6.132-6.09 6.132zM136.597 6.498v10.384c-1.465-1.993-3.739-3.219-6.746-3.219-5.242 0-9.597 4.368-9.597 10.078 0 5.67 4.355 10.078 9.597 10.078 3.007 0 5.281-1.227 6.746-3.258v2.76h4.163V6.497h-4.163zm-6.09 23.374c-3.469 0-6.09-2.568-6.09-6.131 0-3.564 2.621-6.131 6.09-6.131s6.09 2.567 6.09 6.13c0 3.564-2.621 6.132-6.09 6.132zM144.648 33.32h4.163V5.348h-4.163V33.32zM155.957 25.503h15.341c.077-.536.154-1.15.154-1.724 0-5.518-3.931-10.116-9.675-10.116-6.051 0-10.176 4.407-10.176 10.078 0 5.748 4.125 10.078 10.485 10.078 3.777 0 6.668-1.572 8.441-4.177l-3.43-1.993c-.926 1.341-2.66 2.376-4.973 2.376-3.083 0-5.512-1.533-6.167-4.521zm-.038-3.372c.578-2.873 2.698-4.713 5.82-4.713 2.505 0 4.934 1.418 5.512 4.713h-11.332zM177.137 19.45c0-1.38 1.311-2.032 2.814-2.032 1.581 0 2.93.69 3.623 2.184l3.508-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.279-2.759l-3.584 2.07c1.233 2.758 4.008 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.369-4.98-10.369-8.468zM192.774 19.45c0-1.38 1.31-2.032 2.813-2.032 1.581 0 2.93.69 3.624 2.184l3.507-1.954c-1.349-2.529-3.97-3.985-7.131-3.985-3.931 0-7.053 2.26-7.053 5.863 0 6.859 10.368 4.943 10.368 8.353 0 1.533-1.426 2.146-3.276 2.146-2.12 0-3.662-1.035-4.278-2.759l-3.585 2.07c1.233 2.758 4.009 4.483 7.863 4.483 4.163 0 7.516-2.07 7.516-5.902 0-7.088-10.368-4.98-10.368-8.468zM224.523 28.9c2.889 0 5.027-1.715 5.027-4.53v-8.782h-2.588v8.577c0 1.268-.676 2.219-2.439 2.219s-2.438-.951-2.438-2.22v-8.576h-2.569v8.782c0 2.815 2.138 4.53 5.007 4.53zM232.257 15.588V28.64h2.588V15.588h-2.588z\"\n          />\n          <path\n            fill=\"#fff\"\n            fill-rule=\"evenodd\"\n            d=\"M233.817 9.328H220.42c-2.96 0-5.359 2.385-5.359 5.327v13.318c0 2.942 2.399 5.327 5.359 5.327h13.397c2.959 0 5.358-2.385 5.358-5.327V14.655c0-2.942-2.399-5.327-5.358-5.327zM220.42 6.664c-4.439 0-8.038 3.578-8.038 7.99v13.319c0 4.413 3.599 7.99 8.038 7.99h13.397c4.439 0 8.038-3.577 8.038-7.99V14.655c0-4.413-3.599-7.99-8.038-7.99H220.42z\"\n            clip-rule=\"evenodd\"\n          />\n          <path\n            fill=\"#fff\"\n            fill-rule=\"evenodd\"\n            d=\"M220.42 9.328h13.397c2.959 0 5.358 2.385 5.358 5.327v13.318c0 2.942-2.399 5.327-5.358 5.327H220.42c-2.96 0-5.359-2.385-5.359-5.327V14.655c0-2.942 2.399-5.327 5.359-5.327zm-8.038 5.327c0-4.413 3.599-7.99 8.038-7.99h13.397c4.439 0 8.038 3.577 8.038 7.99v13.318c0 4.413-3.599 7.99-8.038 7.99H220.42c-4.439 0-8.038-3.577-8.038-7.99V14.655z\"\n            clip-rule=\"evenodd\"\n          />\n          <path\n            fill=\"url(#prefix__paint0_linear)\"\n            d=\"M8.577 26.097l25.779-8.556c-.514-3.201-.88-5.342-1.307-6.974-.457-1.756-.821-2.226-.965-2.39a5.026 5.026 0 00-1.81-1.306c-.2-.086-.762-.284-2.583-.175-1.924.116-4.453.507-8.455 1.137-4.003.63-6.529 1.035-8.395 1.516-1.766.456-2.239.817-2.403.96a4.999 4.999 0 00-1.315 1.8c-.085.198-.285.757-.175 2.568.116 1.913.51 4.426 1.143 8.405.178 1.114.337 2.113.486 3.015z\"\n          />\n          <path\n            fill=\"url(#prefix__paint1_linear)\"\n            fill-rule=\"evenodd\"\n            d=\"M1.47 24.124C.244 16.427-.37 12.58.96 9.49A11.665 11.665 0 014.027 5.29c2.545-2.21 6.416-2.82 14.16-4.039C25.93.031 29.8-.578 32.907.743a11.729 11.729 0 014.225 3.05c2.223 2.53 2.836 6.38 4.063 14.076 1.226 7.698 1.84 11.546.511 14.636a11.666 11.666 0 01-3.069 4.199c-2.545 2.21-6.416 2.82-14.159 4.039-7.743 1.219-11.614 1.828-14.722.508a11.728 11.728 0 01-4.224-3.05C3.31 35.67 2.697 31.82 1.47 24.123zm13.657 13.668c2.074-.125 4.743-.54 8.697-1.163 3.953-.622 6.62-1.047 8.632-1.566 1.949-.502 2.846-.992 3.426-1.496a7.5 7.5 0 001.973-2.7c.302-.703.494-1.703.372-3.7-.125-2.063-.543-4.716-1.17-8.646-.625-3.93-1.053-6.582-1.574-8.582-.506-1.937-.999-2.83-1.505-3.405a7.54 7.54 0 00-2.716-1.961c-.707-.301-1.713-.492-3.723-.371-2.074.125-4.743.54-8.697 1.163-3.953.622-6.62 1.047-8.632 1.565-1.949.503-2.846.993-3.426 1.497a7.5 7.5 0 00-1.972 2.699c-.303.704-.495 1.704-.373 3.701.125 2.062.543 4.716 1.17 8.646.625 3.93 1.053 6.582 1.574 8.581.506 1.938 1 2.83 1.505 3.406a7.54 7.54 0 002.716 1.961c.707.3 1.713.492 3.723.37z\"\n            clip-rule=\"evenodd\"\n          />\n          <defs>\n            <linearGradient\n              id=\"prefix__paint0_linear\"\n              x1=\"16.759\"\n              x2=\"23.386\"\n              y1=\"0\"\n              y2=\"41.662\"\n              gradientUnits=\"userSpaceOnUse\"\n            >\n              <stop stop-color=\"#66E3FF\" />\n              <stop offset=\"1\" stop-color=\"#7064F9\" />\n            </linearGradient>\n            <linearGradient\n              id=\"prefix__paint1_linear\"\n              x1=\"16.759\"\n              x2=\"23.386\"\n              y1=\"0\"\n              y2=\"41.662\"\n              gradientUnits=\"userSpaceOnUse\"\n            >\n              <stop stop-color=\"#66E3FF\" />\n              <stop offset=\"1\" stop-color=\"#7064F9\" />\n            </linearGradient>\n          </defs>\n        </svg>\n      </router-link>\n      <span class=\"font-bold text-white\">(Vue)</span>\n    </header>\n    <main class=\"flex-1 overflow-auto bg-gray-50\">\n      <slot></slot>\n      <KeyCaster />\n    </main>\n  </div>\n</template>\n\n<script>\nimport KeyCaster from './KeyCaster.vue'\n\nexport default {\n  name: 'Layout',\n  components: {\n    KeyCaster,\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/Button.vue",
    "content": "<template>\n  <button\n    type=\"button\"\n    class=\"focus:outline-hidden ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100\"\n    v-bind=\"$props\"\n  >\n    <slot></slot>\n  </button>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/Home.vue",
    "content": "<script setup>\nimport { defineComponent } from 'vue'\nimport { useRouter } from 'vue-router'\n\nlet Examples = defineComponent({\n  props: ['routes'],\n  setup:\n    (props, { slots }) =>\n    () =>\n      slots.default({ routes: props.routes, slots }),\n})\n\nlet router = useRouter()\nlet routes = router\n  .getRoutes()\n  .filter((example) => example.path !== '/')\n  .filter((route) => route.meta.isRoot)\n</script>\n\n<template>\n  <div class=\"container mx-auto my-24\">\n    <div class=\"prose\">\n      <h2>Examples</h2>\n      <Examples :routes=\"routes\" v-slot=\"{ routes, slots }\">\n        <ul>\n          <li v-for=\"{ children, meta, path } in routes\">\n            <template v-if=\"children.length > 0\">\n              <h3 class=\"text-xl\">{{ meta.name }}</h3>\n              <!-- This is a bit cursed but it works -->\n              <component v-for=\"vnode in slots.default({ routes: children, slots })\" :is=\"vnode\" />\n            </template>\n            <template v-else>\n              <router-link :key=\"path\" :to=\"path\">\n                {{ meta.name }}\n              </router-link>\n            </template>\n          </li>\n        </ul>\n      </Examples>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combinations/form.vue",
    "content": "<template>\n  <div class=\"py-8\">\n    <form\n      class=\"mx-auto flex h-full max-w-4xl flex-col items-start justify-center gap-8 rounded-lg border bg-white p-6\"\n      @submit.prevent=\"submitForm\"\n    >\n      <div class=\"grid w-full grid-cols-[repeat(auto-fill,minmax(350px,1fr))] items-start gap-3\">\n        <Section title=\"Switch\">\n          <Section title=\"Single value\">\n            <SwitchGroup as=\"div\" class=\"flex items-center justify-between space-x-4\">\n              <SwitchLabel>Enable notifications</SwitchLabel>\n\n              <Switch\n                :defaultChecked=\"true\"\n                name=\"notifications\"\n                class=\"focus:outline-hidden ui-checked:bg-blue-600 ui-not-checked:bg-gray-200 relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n              >\n                <span\n                  class=\"ui-checked:translate-x-5 ui-not-checked:translate-x-0 inline-block h-5 w-5 transform rounded-full bg-white\"\n                />\n              </Switch>\n            </SwitchGroup>\n          </Section>\n\n          <Section title=\"Multiple values\">\n            <SwitchGroup as=\"div\" class=\"flex items-center justify-between space-x-4\">\n              <SwitchLabel>Apple</SwitchLabel>\n\n              <Switch\n                name=\"fruit[]\"\n                value=\"apple\"\n                class=\"focus:outline-hidden ui-checked:bg-blue-600 ui-not-checked:bg-gray-200 relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n              >\n                <span\n                  class=\"ui-checked:translate-x-5 ui-not-checked:translate-x-0 inline-block h-5 w-5 transform rounded-full bg-white\"\n                />\n              </Switch>\n            </SwitchGroup>\n\n            <SwitchGroup as=\"div\" class=\"flex items-center justify-between space-x-4\">\n              <SwitchLabel>Banana</SwitchLabel>\n              <Switch\n                name=\"fruit[]\"\n                value=\"banana\"\n                class=\"focus:outline-hidden ui-checked:bg-blue-600 ui-not-checked:bg-gray-200 relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n              >\n                <span\n                  class=\"ui-checked:translate-x-5 ui-not-checked:translate-x-0 inline-block h-5 w-5 transform rounded-full bg-white\"\n                />\n              </Switch>\n            </SwitchGroup>\n          </Section>\n        </Section>\n        <Section title=\"Radio Group\">\n          <RadioGroup defaultValue=\"sm\" name=\"size\">\n            <div class=\"flex -space-x-px rounded-md bg-white\">\n              <RadioGroupOption\n                v-for=\"size in sizes\"\n                :key=\"size\"\n                :value=\"size\"\n                class=\"focus:outline-hidden ui-active:z-10 ui-active:border-blue-200 ui-active:bg-blue-50 ui-not-active:border-gray-200 relative flex w-20 border px-2 py-4 first:rounded-l-md last:rounded-r-md focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n              >\n                <div class=\"flex w-full items-center justify-between\">\n                  <div class=\"ml-3 flex cursor-pointer flex-col\">\n                    <span\n                      class=\"ui-active:text-blue-900 ui-not-active:text-gray-900 block text-sm font-medium leading-5\"\n                    >\n                      {{ size }}\n                    </span>\n                  </div>\n                  <div>\n                    <svg\n                      xmlns=\"http://www.w3.org/2000/svg\"\n                      fill=\"none\"\n                      viewBox=\"0 0 24 24\"\n                      stroke=\"currentColor\"\n                      class=\"ui-checked:block ui-not-checked:hidden h-5 w-5 text-blue-500\"\n                    >\n                      <path\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                        strokeWidth=\"2\"\n                        d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n                      />\n                    </svg>\n                  </div>\n                </div>\n              </RadioGroupOption>\n            </div>\n          </RadioGroup>\n        </Section>\n        <Section title=\"Listbox\">\n          <div class=\"w-full space-y-1\">\n            <Listbox name=\"person\" :defaultValue=\"people[1]\" v-slot=\"{ value }\">\n              <div class=\"relative\">\n                <span class=\"shadow-xs inline-block w-full rounded-md\">\n                  <ListboxButton\n                    class=\"focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5\"\n                  >\n                    <span class=\"block truncate\">{{ value?.name?.first }}</span>\n                    <span\n                      class=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\"\n                    >\n                      <svg\n                        class=\"h-5 w-5 text-gray-400\"\n                        viewBox=\"0 0 20 20\"\n                        fill=\"none\"\n                        stroke=\"currentColor\"\n                      >\n                        <path\n                          d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                          strokeWidth=\"1.5\"\n                          strokeLinecap=\"round\"\n                          strokeLinejoin=\"round\"\n                        />\n                      </svg>\n                    </span>\n                  </ListboxButton>\n                </span>\n\n                <div class=\"absolute z-10 mt-1 w-full rounded-md bg-white shadow-lg\">\n                  <ListboxOptions\n                    class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n                  >\n                    <ListboxOption\n                      v-for=\"person in people\"\n                      :key=\"person.id\"\n                      :value=\"person\"\n                      class=\"ui-active:bg-blue-600 ui-active:text-white ui-not-active:text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9\"\n                    >\n                      <span\n                        class=\"ui-selected:font-semibold ui-not-selected:font-normal block truncate\"\n                      >\n                        {{ person.name.first }}\n                      </span>\n                      <span\n                        class=\"ui-selected:block ui-not-selected:hidden ui-active:text-white ui-not-active:text-blue-600 absolute inset-y-0 right-0 flex items-center pr-4\"\n                      >\n                        <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                          <path\n                            fillRule=\"evenodd\"\n                            d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                            clipRule=\"evenodd\"\n                          />\n                        </svg>\n                      </span>\n                    </ListboxOption>\n                  </ListboxOptions>\n                </div>\n              </div>\n            </Listbox>\n          </div>\n        </Section>\n        <Section title=\"Combobox\">\n          <div class=\"w-full space-y-1\">\n            <Combobox\n              name=\"location\"\n              defaultValue=\"New York\"\n              @change=\"query = ''\"\n              v-slot=\"{ open, value }\"\n            >\n              <div class=\"relative\">\n                <div class=\"flex w-full flex-col\">\n                  <ComboboxInput\n                    @change=\"query = $event.target.value\"\n                    class=\"shadow-xs focus:outline-hidden w-full rounded-md border-gray-300 bg-clip-padding px-3 py-1 focus:border-gray-300 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n                    placeholder=\"Search users...\"\n                  />\n                  <div\n                    class=\"flex border-t\"\n                    :class=\"[value && !open ? 'border-transparent' : 'border-gray-200']\"\n                  >\n                    <div class=\"absolute z-10 mt-1 w-full rounded-md bg-white shadow-lg\">\n                      <ComboboxOptions\n                        class=\"shadow-2xs max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n                      >\n                        <ComboboxOption\n                          v-for=\"location in locations.filter((l) =>\n                            l.toLowerCase().includes(query.toLowerCase())\n                          )\"\n                          :key=\"location\"\n                          :value=\"location\"\n                          class=\"ui-active:bg-blue-600 ui-active:text-white ui-not-active:text-gray-900 relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9\"\n                        >\n                          <span\n                            class=\"ui-selected:font-semibold ui-not-selected:font-normal block truncate\"\n                          >\n                            {{ location }}\n                          </span>\n                          <span\n                            class=\"ui-active:block ui-active:text-white ui-not-active:hidden ui-not-active:text-blue-600 absolute inset-y-0 right-0 flex items-center pr-4\"\n                          >\n                            <svg class=\"h-5 w-5\" viewBox=\"0 0 25 24\" fill=\"none\">\n                              <path\n                                d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                                stroke=\"currentColor\"\n                                strokeWidth=\"1.5\"\n                                strokeLinecap=\"round\"\n                                strokeLinejoin=\"round\"\n                              />\n                            </svg>\n                          </span>\n                        </ComboboxOption>\n                      </ComboboxOptions>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </Combobox>\n          </div>\n        </Section>\n      </div>\n\n      <div class=\"space-x-4\">\n        <button\n          class=\"shadow-xs focus:outline-hidden rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 hover:text-gray-500 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5\"\n        >\n          Submit\n        </button>\n\n        <button\n          type=\"reset\"\n          class=\"shadow-xs focus:outline-hidden rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium leading-6 text-gray-700 hover:text-gray-500 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 sm:text-sm sm:leading-5\"\n        >\n          Reset\n        </button>\n      </div>\n\n      <div class=\"w-full border-t py-4\">\n        <span>Form data (entries):</span>\n        <pre class=\"text-sm\">{{ JSON.stringify([...result.entries()], null, 2) }}</pre>\n      </div>\n    </form>\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport {\n  Switch,\n  SwitchLabel,\n  SwitchGroup,\n  RadioGroup,\n  RadioGroupOption,\n  Listbox,\n  ListboxButton,\n  ListboxOptions,\n  ListboxOption,\n  ListboxLabel,\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxOptions,\n  ComboboxOption,\n  ComboboxLabel,\n} from '@headlessui/vue'\nlet html = String.raw\n\nlet Section = {\n  props: {\n    title: { type: String, default: '' },\n  },\n  template: html`\n    <fieldset class=\"rounded-lg border bg-gray-200/20 p-3\">\n      <legend class=\"rounded-md border bg-gray-100 px-2 text-sm uppercase\">{{ title }}</legend>\n      <div class=\"flex flex-col gap-3\">\n        <slot />\n      </div>\n    </fieldset>\n  `,\n}\n\nfunction submitForm(event) {\n  result.value = new FormData(event.currentTarget)\n}\n\nlet sizes = ref(['xs', 'sm', 'md', 'lg', 'xl'])\nlet people = ref([\n  { id: 1, name: { first: 'Alice' } },\n  { id: 2, name: { first: 'Bob' } },\n  { id: 3, name: { first: 'Charlie' } },\n])\nlet locations = ref(['New York', 'London', 'Paris', 'Berlin'])\n\nlet result = ref(\n  typeof window === 'undefined' || typeof document === 'undefined' ? [] : new FormData()\n)\n\nlet query = ref('')\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combinations/tabs-in-dialog.vue",
    "content": "<template>\n  <button @click=\"open = true\">Open dialog</button>\n  <Dialog :open=\"open\" @close=\"open = false\" class=\"fixed inset-0 grid place-content-center\">\n    <DialogOverlay class=\"fixed inset-0 bg-gray-500/70\" />\n    <div\n      class=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\"\n    >\n      <div class=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n        <TabGroup>\n          <TabList>\n            <Tab class=\"px-3 py-2\">Tab 1</Tab>\n            <Tab class=\"px-3 py-2\">Tab 2</Tab>\n            <Tab class=\"px-3 py-2\">Tab 3</Tab>\n          </TabList>\n          <TabPanels>\n            <TabPanel class=\"px-3 py-2\">Panel 1</TabPanel>\n            <TabPanel class=\"px-3 py-2\">Panel 2</TabPanel>\n            <TabPanel class=\"px-3 py-2\">Panel 3</TabPanel>\n          </TabPanels>\n        </TabGroup>\n      </div>\n    </div>\n  </Dialog>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Dialog, DialogOverlay, TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'\n\nlet open = ref(false)\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/_virtual-example.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <div class=\"py-8 font-mono text-xs\">Selected timezone: {{ activeTimezone }}</div>\n      <div class=\"space-y-1\">\n        <Combobox nullable v-model=\"activeTimezone\" as=\"div\" :virtual=\"virtual\">\n          <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n            Timezone\n            {{\n              virtual\n                ? `(virtual — ${nf.format(timezones.length)} items)`\n                : `(${nf.format(timezones.length)} items)`\n            }}\n          </ComboboxLabel>\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n              <ComboboxInput\n                @change=\"query = $event.target.value\"\n                class=\"outline-hidden border-none px-3 py-1\"\n              />\n              <ComboboxButton\n                class=\"focus:outline-hidden cursor-default border-l bg-gray-100 px-1 text-indigo-600\"\n              >\n                <span class=\"pointer-events-none flex items-center px-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ComboboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ComboboxOptions\n                v-if=\"!virtual\"\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ComboboxOption\n                  v-for=\"(timezone, idx) in timezones\"\n                  :key=\"timezone\"\n                  :value=\"timezone\"\n                  :order=\"virtual ? idx : undefined\"\n                  v-slot=\"{ active, selected }\"\n                  as=\"template\"\n                >\n                  <li\n                    :class=\"[\n                      'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ timezone }}\n                    </span>\n                    <span\n                      v-if=\"selected\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </li>\n                </ComboboxOption>\n              </ComboboxOptions>\n              <ComboboxOptions\n                v-if=\"virtual\"\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n                v-slot=\"{ option: timezone }\"\n              >\n                <ComboboxOption :value=\"timezone\" v-slot=\"{ active, selected }\" as=\"template\">\n                  <li\n                    :class=\"[\n                      'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ timezone }}\n                    </span>\n                    <span\n                      v-if=\"selected\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </li>\n                </ComboboxOption>\n              </ComboboxOptions>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nlet nf = new Intl.NumberFormat('en-US')\nimport { ref, computed } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nlet props = defineProps(['data', 'initial', 'virtual'])\n\nlet query = ref('')\nlet activeTimezone = ref(props.initial)\nlet timezones = computed(() => {\n  return query.value === ''\n    ? props.data\n    : props.data.filter((timezone) => timezone.toLowerCase().includes(query.value.toLowerCase()))\n})\n\nlet virtual = computed(() => {\n  return props.virtual ? { options: timezones.value } : null\n})\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/combobox-countries.vue",
    "content": "<script>\nimport { countries as allCountries } from '../../data'\nimport { ref, defineComponent, computed, onMounted, watch } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nexport default defineComponent({\n  components: {\n    Combobox,\n    ComboboxButton,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxOption,\n    ComboboxOptions,\n  },\n  setup() {\n    let query = ref('')\n    let activeCountry = ref(allCountries[2]) // allCountries[Math.floor(Math.random() * allCountries.length)]\n    let filteredCountries = computed(() => {\n      return query.value === ''\n        ? allCountries\n        : allCountries.filter((country) => {\n            return country.toLowerCase().includes(query.value.toLowerCase())\n          })\n    })\n\n    // Choose a random country on mount\n    onMounted(() => {\n      activeCountry.value = allCountries[Math.floor(Math.random() * allCountries.length)]\n    })\n\n    watch(activeCountry, () => {\n      query.value = ''\n    })\n\n    return {\n      query,\n      activeCountry,\n      filteredCountries,\n    }\n  },\n})\n</script>\n\n<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <div class=\"py-8 font-mono text-xs\">\n        Selected country: {{ activeCountry?.name ?? 'Nothing yet' }}\n      </div>\n      <div class=\"space-y-1\">\n        <Combobox v-model=\"activeCountry\" as=\"div\">\n          <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n            Assigned to\n          </ComboboxLabel>\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n              <ComboboxInput\n                @change=\"query = $event.target.value\"\n                class=\"outline-hidden border-none px-3 py-1\"\n              />\n              <ComboboxButton\n                class=\"focus:outline-hidden cursor-default border-l bg-gray-100 px-1 text-indigo-600\"\n              >\n                <span class=\"pointer-events-none flex items-center px-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ComboboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ComboboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ComboboxOption\n                  v-for=\"country in filteredCountries\"\n                  :key=\"country\"\n                  :value=\"country\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <div\n                    :class=\"[\n                      'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ country }}\n                    </span>\n                    <span\n                      v-if=\"selected\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </div>\n                </ComboboxOption>\n              </ComboboxOptions>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/combobox-open-on-focus.vue",
    "content": "<script>\nimport { ref, defineComponent, computed, onMounted, watch } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nlet everybody = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nexport default defineComponent({\n  components: {\n    Combobox,\n    ComboboxButton,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxOption,\n    ComboboxOptions,\n  },\n  setup() {\n    let query = ref('')\n    let activePerson = ref(everybody[2]) // everybody[Math.floor(Math.random() * everybody.length)]\n    let filteredPeople = computed(() => {\n      return query.value === ''\n        ? everybody\n        : everybody.filter((person) => {\n            return person.name.toLowerCase().includes(query.value.toLowerCase())\n          })\n    })\n\n    // Choose a random person on mount\n    onMounted(() => {\n      activePerson.value = everybody[Math.floor(Math.random() * everybody.length)]\n    })\n\n    watch(activePerson, () => {\n      query.value = ''\n    })\n\n    return {\n      query,\n      activePerson,\n      filteredPeople,\n    }\n  },\n})\n</script>\n\n<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <div class=\"py-8 font-mono text-xs\">\n        Selected person: {{ activePerson?.name ?? 'Nobody yet' }}\n      </div>\n      <div class=\"space-y-1\">\n        <Combobox v-model=\"activePerson\" as=\"div\" immediate>\n          <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n            Assigned to\n          </ComboboxLabel>\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n              <ComboboxInput\n                @change=\"query = $event.target.value\"\n                :displayValue=\"(person) => person?.name ?? ''\"\n                class=\"outline-hidden border-none px-3 py-1\"\n              />\n              <ComboboxButton\n                class=\"focus:outline-hidden cursor-default border-l bg-gray-100 px-1 text-indigo-600\"\n              >\n                <span class=\"pointer-events-none flex items-center px-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ComboboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ComboboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ComboboxOption\n                  v-for=\"person in filteredPeople\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <div\n                    :class=\"[\n                      'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ person.name }}\n                    </span>\n                    <span\n                      v-if=\"selected\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </div>\n                </ComboboxOption>\n              </ComboboxOptions>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/combobox-virtual-with-empty-states.vue",
    "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n  Combobox,\n  ComboboxLabel,\n  ComboboxInput,\n  ComboboxOption,\n  ComboboxOptions,\n  ComboboxButton,\n} from '@headlessui/vue'\n\ntype Option = {\n  name: string\n  disabled: boolean\n  empty?: boolean\n}\n\nlet list = ref([\n  { name: 'Alice', disabled: false },\n  { name: 'Bob', disabled: false },\n  { name: 'Charlie', disabled: false },\n  { name: 'David', disabled: false },\n  { name: 'Eve', disabled: false },\n  { name: 'Fred', disabled: false },\n  { name: 'George', disabled: false },\n  { name: 'Helen', disabled: false },\n  { name: 'Iris', disabled: false },\n  { name: 'John', disabled: false },\n  { name: 'Kate', disabled: false },\n  { name: 'Linda', disabled: false },\n  { name: 'Michael', disabled: false },\n  { name: 'Nancy', disabled: false },\n  { name: 'Oscar', disabled: true },\n  { name: 'Peter', disabled: false },\n  { name: 'Quentin', disabled: false },\n  { name: 'Robert', disabled: false },\n  { name: 'Sarah', disabled: false },\n  { name: 'Thomas', disabled: false },\n  { name: 'Ursula', disabled: false },\n  { name: 'Victor', disabled: false },\n  { name: 'Wendy', disabled: false },\n  { name: 'Xavier', disabled: false },\n  { name: 'Yvonne', disabled: false },\n  { name: 'Zachary', disabled: false },\n])\n\nlet emptyOption = { name: 'No results', disabled: true, empty: true }\n\nlet query = ref('')\nlet selectedPerson = ref<Option | null>(list.value[0])\nlet optionsRef = ref<HTMLUListElement | null>(null)\n\nlet filtered = computed(() => {\n  return query.value === ''\n    ? list.value\n    : list.value.filter((item) => item.name.toLowerCase().includes(query.value.toLowerCase()))\n})\n</script>\n<template>\n  <div class=\"mx-auto max-w-fit\">\n    <div class=\"py-8 font-mono text-xs\">Selected person: {{ selectedPerson?.name ?? 'N/A' }}</div>\n    <Combobox\n      :virtual=\"{\n        options: filtered.length > 0 ? filtered : [emptyOption],\n        disabled: (option) => option.disabled || option.empty,\n      }\"\n      v-model=\"selectedPerson\"\n      @update:modelValue=\"() => (query = '')\"\n      nullable\n      as=\"div\"\n    >\n      <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n        Person\n      </ComboboxLabel>\n\n      <div class=\"relative\">\n        <span class=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n          <ComboboxInput\n            @change=\"(e) => (query = e.target.value)\"\n            :displayValue=\"(option: Option | null) => option?.name ?? ''\"\n            class=\"outline-hidden border-none px-3 py-1\"\n          />\n          <ComboboxButton as=\"button\">\n            <span class=\"pointer-events-none flex items-center px-2\">\n              <svg\n                class=\"h-5 w-5 text-gray-400\"\n                viewBox=\"0 0 20 20\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n              >\n                <path\n                  d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                  strokeWidth=\"1.5\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                />\n              </svg>\n            </span>\n          </ComboboxButton>\n        </span>\n\n        <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n          <ComboboxOptions\n            :ref=\"optionsRef\"\n            :class=\"[\n              'shadow-2xs focus:outline-hidden max-h-60 rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5',\n              filtered.length === 0 ? 'overflow-hidden' : 'overflow-auto',\n            ]\"\n            v-slot=\"{ option }\"\n          >\n            <template v-if=\"option.empty\">\n              <ComboboxOption\n                :value=\"option\"\n                class=\"focus:outline-hidden relative w-full cursor-default select-none px-3 py-2 text-center\"\n                disabled\n              >\n                <div class=\"relative grid h-full grid-cols-1 grid-rows-1\">\n                  <div class=\"absolute inset-0\">\n                    <svg\n                      fill=\"none\"\n                      viewBox=\"0 0 24 24\"\n                      stroke-width=\"0.5\"\n                      stroke=\"currentColor\"\n                      class=\"-translate-y-1/4 text-gray-500/5\"\n                    >\n                      <path\n                        stroke-linecap=\"round\"\n                        stroke-linejoin=\"round\"\n                        d=\"M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456zM16.894 20.567L16.5 21.75l-.394-1.183a2.25 2.25 0 00-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 001.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 001.423 1.423l1.183.394-1.183.394a2.25 2.25 0 00-1.423 1.423z\"\n                      />\n                    </svg>\n                  </div>\n                  <div\n                    class=\"z-20 col-span-full col-start-1 row-span-full row-start-1 flex flex-col items-center justify-center p-8\"\n                  >\n                    <h3 class=\"mx-2 mb-4 text-xl font-semibold text-gray-400\">No people found</h3>\n                  </div>\n                </div>\n              </ComboboxOption>\n            </template>\n            <template v-else>\n              <ComboboxOption\n                as=\"template\"\n                v-slot=\"{ active }\"\n                :disabled=\"option.disabled\"\n                :value=\"option\"\n              >\n                <div\n                  :class=\"[\n                    'focus:outline-hidden relative w-full cursor-default select-none py-2 pl-3 pr-9',\n                    active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                  ]\"\n                >\n                  <span class=\"block truncate\">\n                    {{ option.name }}\n                  </span>\n                </div>\n              </ComboboxOption>\n            </template>\n          </ComboboxOptions>\n        </div>\n      </div>\n    </Combobox>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/combobox-virtualized.vue",
    "content": "<template>\n  <div className=\"flex flex-col p-12\">\n    <label class=\"mx-auto flex w-24 items-center gap-2\">\n      <span>Items:</span>\n      <select v-model=\"count\" class=\"mx-auto\">\n        <option :value=\"100\">100</option>\n        <option :value=\"1_000\">1000</option>\n        <option :value=\"10_000\">10k</option>\n        <option :value=\"100_000\">100k</option>\n      </select>\n    </label>\n    <div class=\"flex\">\n      <Example :data=\"list\" initial=\"Europe/Brussels #1\" :virtual=\"true\" />\n      <Example :data=\"list\" initial=\"Europe/Brussels #1\" :virtual=\"false\" />\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { ref, computed } from 'vue'\nimport { timezones as _allTimezones } from '../../data'\nimport Example from './_virtual-example.vue'\n\nlet count = ref(1_000)\nlet list = computed(() => {\n  console.time('Generating list')\n  let result = []\n\n  while (result.length < Number(count.value)) {\n    let batch = Math.floor(result.length / _allTimezones.length) + 1\n    result.push(`${_allTimezones[result.length % _allTimezones.length]} #${batch}`)\n  }\n  console.timeEnd('Generating list')\n\n  return result\n})\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/combobox-with-pure-tailwind.vue",
    "content": "<script>\nimport { ref, defineComponent, computed, onMounted, watch } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nlet everybody = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nexport default defineComponent({\n  components: {\n    Combobox,\n    ComboboxButton,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxOption,\n    ComboboxOptions,\n  },\n  setup() {\n    let query = ref('')\n    let activePerson = ref(everybody[2]) // everybody[Math.floor(Math.random() * everybody.length)]\n    let filteredPeople = computed(() => {\n      return query.value === ''\n        ? everybody\n        : everybody.filter((person) => {\n            return person.name.toLowerCase().includes(query.value.toLowerCase())\n          })\n    })\n\n    // Choose a random person on mount\n    onMounted(() => {\n      activePerson.value = everybody[Math.floor(Math.random() * everybody.length)]\n    })\n\n    watch(activePerson, () => {\n      query.value = ''\n    })\n\n    return {\n      query,\n      activePerson,\n      filteredPeople,\n    }\n  },\n})\n</script>\n\n<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <div class=\"py-8 font-mono text-xs\">\n        Selected person: {{ activePerson?.name ?? 'Nobody yet' }}\n      </div>\n      <div class=\"space-y-1\">\n        <Combobox v-model=\"activePerson\" as=\"div\">\n          <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n            Assigned to\n          </ComboboxLabel>\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs relative inline-flex flex-row overflow-hidden rounded-md border\">\n              <ComboboxInput\n                @change=\"query = $event.target.value\"\n                :displayValue=\"(person) => person?.name ?? ''\"\n                class=\"outline-hidden border-none px-3 py-1\"\n              />\n              <ComboboxButton\n                class=\"focus:outline-hidden cursor-default border-l bg-gray-100 px-1 text-indigo-600\"\n              >\n                <span class=\"pointer-events-none flex items-center px-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ComboboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ComboboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ComboboxOption\n                  v-for=\"person in filteredPeople\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <div\n                    :class=\"[\n                      'focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ person.name }}\n                    </span>\n                    <span\n                      v-if=\"selected\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </div>\n                </ComboboxOption>\n              </ComboboxOptions>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/command-palette-with-groups.vue",
    "content": "<script>\nimport { watch, ref, defineComponent, computed } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nlet everybody = [\n  { id: 1, img: 'https://github.com/adamwathan.png', name: 'Adam Wathan' },\n  { id: 2, img: 'https://github.com/sschoger.png', name: 'Steve Schoger' },\n  { id: 3, img: 'https://github.com/bradlc.png', name: 'Brad Cornes' },\n  { id: 4, img: 'https://github.com/simonswiss.png', name: 'Simon Vrachliotis' },\n  { id: 5, img: 'https://github.com/robinmalfait.png', name: 'Robin Malfait' },\n  {\n    id: 6,\n    img: 'https://pbs.twimg.com/profile_images/1478879681491394569/eV2PyCnm_400x400.jpg',\n    name: 'James McDonald',\n  },\n  { id: 7, img: 'https://github.com/reinink.png', name: 'Jonathan Reinink' },\n  { id: 8, img: 'https://github.com/thecrypticace.png', name: 'Jordan Pittman' },\n]\n\nexport default defineComponent({\n  components: {\n    Combobox,\n    ComboboxButton,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxOption,\n    ComboboxOptions,\n  },\n  setup() {\n    let query = ref('')\n    let activePerson = ref(everybody[2])\n\n    // Choose a random person on mount\n    activePerson.value = everybody[Math.floor(Math.random() * everybody.length)]\n\n    watch(\n      activePerson,\n      (person) => {\n        query.value = person?.name ?? ''\n      },\n      { mode: 'sync' }\n    )\n\n    function setPerson(person) {\n      setActivePerson(person)\n      setQuery(person.name ?? '')\n    }\n\n    let people = computed(() => {\n      return query.value === ''\n        ? everybody\n        : everybody.filter((person) =>\n            person.name.toLowerCase().includes(query.value.toLowerCase())\n          )\n    })\n\n    let groups = computed(() => {\n      return people.value.reduce((groups, person) => {\n        let lastNameLetter = person.name.split(' ')[1][0]\n\n        groups.set(lastNameLetter, [...(groups.get(lastNameLetter) || []), person])\n\n        return groups\n      }, new Map())\n    })\n\n    let sortedGroups = computed(() => {\n      return Array.from(groups.value.entries()).sort(([letterA], [letterZ]) =>\n        letterA.localeCompare(letterZ)\n      )\n    })\n\n    return {\n      query,\n      activePerson,\n      people,\n      groups,\n      sortedGroups,\n      displayValue: (item) => item?.name,\n    }\n  },\n})\n</script>\n\n<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-lg\">\n      <div class=\"space-y-1\">\n        <Combobox\n          as=\"div\"\n          v-model=\"activePerson\"\n          class=\"shadow-xs w-full overflow-hidden rounded-sm border border-black/5 bg-white bg-clip-padding\"\n          v-slot=\"{ activeOption }\"\n        >\n          <div class=\"flex w-full flex-col\">\n            <ComboboxInput\n              @change=\"query = $event.target.value\"\n              class=\"outline-hidden w-full rounded-none border-none bg-none px-3 py-1\"\n              placeholder=\"Search users…\"\n              :displayValue=\"displayValue\"\n            />\n            <div class=\"flex\">\n              <ComboboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 flex-1 overflow-auto text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <template v-for=\"[letter, people] of sortedGroups\" :key=\"letter\">\n                  <div class=\"bg-gray-100 px-4 py-2\">{{ letter }}</div>\n                  <ComboboxOption\n                    v-for=\"person in people\"\n                    :key=\"person.id\"\n                    :value=\"person\"\n                    v-slot=\"{ active, selected }\"\n                  >\n                    <div\n                      :class=\"[\n                        'focus:outline-hidden relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9',\n                        active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                      ]\"\n                    >\n                      <img :src=\"person.img\" class=\"h-6 w-6 overflow-hidden rounded-full\" />\n                      <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                        {{ person.name }}\n                      </span>\n                      <span\n                        v-if=\"active\"\n                        :class=\"[\n                          'absolute inset-y-0 right-0 flex items-center pr-4',\n                          active ? 'text-white' : 'text-indigo-600',\n                        ]\"\n                      >\n                        <svg class=\"h-5 w-5\" viewBox=\"0 0 25 24\" fill=\"none\">\n                          <path\n                            d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                            stroke=\"currentColor\"\n                            strokeWidth=\"1.5\"\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                          />\n                        </svg>\n                      </span>\n                    </div>\n                  </ComboboxOption>\n                </template>\n              </ComboboxOptions>\n\n              <div v-if=\"people.length === 0\" class=\"w-full py-4 text-center\">\n                No person selected\n              </div>\n              <div v-else-if=\"activeOption\" class=\"border-l\">\n                <div class=\"flex flex-col\">\n                  <div class=\"p-8 text-center\">\n                    <img\n                      :src=\"activeOption?.img\"\n                      class=\"mb-4 inline-block h-16 w-16 overflow-hidden rounded-full\"\n                    />\n                    <div class=\"font-bold text-gray-900\">{{ activeOption.name }}</div>\n                    <div class=\"text-gray-700\">Obviously cool person</div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/command-palette.vue",
    "content": "<script>\nimport { ref, defineComponent, computed } from 'vue'\nimport {\n  Combobox,\n  ComboboxButton,\n  ComboboxInput,\n  ComboboxLabel,\n  ComboboxOption,\n  ComboboxOptions,\n} from '@headlessui/vue'\n\nlet everybody = [\n  { id: 1, img: 'https://github.com/adamwathan.png', name: 'Adam Wathan' },\n  { id: 2, img: 'https://github.com/sschoger.png', name: 'Steve Schoger' },\n  { id: 3, img: 'https://github.com/bradlc.png', name: 'Brad Cornes' },\n  { id: 4, img: 'https://github.com/simonswiss.png', name: 'Simon Vrachliotis' },\n  { id: 5, img: 'https://github.com/robinmalfait.png', name: 'Robin Malfait' },\n  {\n    id: 6,\n    img: 'https://pbs.twimg.com/profile_images/1478879681491394569/eV2PyCnm_400x400.jpg',\n    name: 'James McDonald',\n  },\n  { id: 7, img: 'https://github.com/reinink.png', name: 'Jonathan Reinink' },\n  { id: 8, img: 'https://github.com/thecrypticace.png', name: 'Jordan Pittman' },\n]\n\nexport default defineComponent({\n  components: {\n    Combobox,\n    ComboboxButton,\n    ComboboxInput,\n    ComboboxLabel,\n    ComboboxOption,\n    ComboboxOptions,\n  },\n  setup() {\n    let query = ref('')\n    let activePerson = ref(everybody[2])\n\n    // Choose a random person on mount\n    activePerson.value = everybody[Math.floor(Math.random() * everybody.length)]\n\n    let people = computed(() => {\n      return query.value === ''\n        ? everybody\n        : everybody.filter((person) =>\n            person.name.toLowerCase().includes(query.value.toLowerCase())\n          )\n    })\n\n    return {\n      query,\n      activePerson,\n      people,\n\n      displayValue: (item) => item?.name,\n    }\n  },\n})\n</script>\n\n<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-lg\">\n      <div class=\"space-y-1\">\n        <Combobox\n          as=\"div\"\n          v-model=\"activePerson\"\n          class=\"shadow-xs w-full overflow-hidden rounded-sm border border-black/5 bg-white bg-clip-padding\"\n          v-slot=\"{ activeOption, open }\"\n        >\n          <div class=\"flex w-full flex-col\">\n            <ComboboxInput\n              @change=\"query = $event.target.value\"\n              class=\"outline-hidden w-full rounded-none border-none px-3 py-1\"\n              placeholder=\"Search users…\"\n              :displayValue=\"displayValue\"\n            />\n            <div\n              :class=\"[\n                'flex border-t',\n                activePerson && !open ? 'border-transparent' : 'border-gray-200',\n              ]\"\n            >\n              <ComboboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 flex-1 overflow-auto py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ComboboxOption\n                  v-for=\"person in people\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <div\n                    :class=\"[\n                      'focus:outline-hidden relative flex cursor-default select-none space-x-4 py-2 pl-3 pr-9',\n                      active ? 'bg-indigo-600 text-white' : 'text-gray-900',\n                    ]\"\n                  >\n                    <img :src=\"person.img\" class=\"h-6 w-6 overflow-hidden rounded-full\" />\n                    <span :class=\"['block truncate', selected ? 'font-semibold' : 'font-normal']\">\n                      {{ person.name }}\n                    </span>\n                    <span\n                      v-if=\"active\"\n                      :class=\"[\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600',\n                      ]\"\n                    >\n                      <svg class=\"h-5 w-5\" viewBox=\"0 0 25 24\" fill=\"none\">\n                        <path\n                          d=\"M11.25 8.75L14.75 12L11.25 15.25\"\n                          stroke=\"currentColor\"\n                          strokeWidth=\"1.5\"\n                          strokeLinecap=\"round\"\n                          strokeLinejoin=\"round\"\n                        />\n                      </svg>\n                    </span>\n                  </div>\n                </ComboboxOption>\n              </ComboboxOptions>\n\n              <div v-if=\"people.length === 0\" class=\"w-full py-4 text-center\">\n                No person selected\n              </div>\n              <div v-else-if=\"activeOption\" class=\"border-l\">\n                <div class=\"flex flex-col\">\n                  <div class=\"p-8 text-center\">\n                    <img\n                      :src=\"activeOption.img\"\n                      class=\"mb-4 inline-block h-16 w-16 overflow-hidden rounded-full\"\n                    />\n                    <div class=\"font-bold text-gray-900\">{{ activeOption.name }}</div>\n                    <div class=\"text-gray-700\">Obviously cool person</div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n        </Combobox>\n      </div>\n    </div>\n  </div>\n</template>\n"
  },
  {
    "path": "playgrounds/vue/src/components/combobox/multi-select.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n    <div class=\"w-full max-w-4xl\">\n      <div class=\"space-y-1\">\n        <form @submit=\"onSubmit\">\n          <Combobox v-model=\"activePersons\" name=\"people\" multiple>\n            <ComboboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </ComboboxLabel>\n\n            <div class=\"relative\">\n              <span class=\"shadow-xs inline-block w-full rounded-md\">\n                <div\n                  class=\"focus-within:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus-within:border-blue-700 focus-within:ring-1 focus-within:ring-blue-700 sm:text-sm sm:leading-5\"\n                >\n                  <span class=\"block flex flex-wrap gap-2\">\n                    <span v-if=\"activePersons.length === 0\" class=\"p-0.5\">Empty</span>\n                    <span\n                      v-for=\"person in activePersons\"\n                      :key=\"person.id\"\n                      class=\"flex items-center gap-1 rounded-sm bg-blue-50 px-2 py-0.5\"\n                    >\n                      <span>{{ person.name }}</span>\n                      <svg\n                        class=\"h-4 w-4 cursor-pointer\"\n                        fill=\"none\"\n                        stroke=\"currentColor\"\n                        viewBox=\"0 0 24 24\"\n                        xmlns=\"http://www.w3.org/2000/svg\"\n                        @click.stop.prevent=\"removePerson(person)\"\n                      >\n                        <path\n                          stroke-linecap=\"round\"\n                          stroke-linejoin=\"round\"\n                          stroke-width=\"2\"\n                          d=\"M6 18L18 6M6 6l12 12\"\n                        />\n                      </svg>\n                    </span>\n                    <ComboboxInput\n                      @change=\"query = $event.target.value\"\n                      @focus=\"query = ''\"\n                      class=\"border-none p-0 focus:ring-0\"\n                      placeholder=\"Search...\"\n                    />\n                  </span>\n                  <ComboboxButton class=\"absolute inset-y-0 right-0 flex items-center pr-2\">\n                    <svg\n                      class=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        stroke-width=\"1.5\"\n                        stroke-linecap=\"round\"\n                        stroke-linejoin=\"round\"\n                      />\n                    </svg>\n                  </ComboboxButton>\n                </div>\n              </span>\n\n              <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <ComboboxOptions\n                  class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n                >\n                  <ComboboxOption\n                    v-for=\"person in people.filter((person) =>\n                      person.name.toLowerCase().includes(query.toLowerCase())\n                    )\"\n                    :key=\"person.id\"\n                    :value=\"person\"\n                    as=\"template\"\n                    v-slot=\"{ active, selected }\"\n                  >\n                    <li\n                      class=\"focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9\"\n                      :class=\"active ? 'bg-indigo-600 text-white' : 'text-gray-900'\"\n                    >\n                      <span\n                        class=\"block truncate\"\n                        :class=\"{ 'font-semibold': selected, 'font-normal': !selected }\"\n                      >\n                        {{ person.name }}\n                      </span>\n                      <span\n                        v-if=\"selected\"\n                        class=\"absolute inset-y-0 right-0 flex items-center pr-4\"\n                        :class=\"{ 'text-white': active, 'text-indigo-600': !active }\"\n                      >\n                        <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                          <path\n                            fillRule=\"evenodd\"\n                            d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                            clipRule=\"evenodd\"\n                          />\n                        </svg>\n                      </span>\n                    </li>\n                  </ComboboxOption>\n                </ComboboxOptions>\n              </div>\n            </div>\n          </Combobox>\n          <button\n            class=\"shadow-xs focus:outline-hidden mt-2 inline-flex items-center rounded-sm border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2\"\n          >\n            Submit\n          </button>\n        </form>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport {\n  Combobox,\n  ComboboxLabel,\n  ComboboxInput,\n  ComboboxButton,\n  ComboboxOptions,\n  ComboboxOption,\n} from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet query = ref('')\n\nlet people = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nlet activePersons = ref([people[2], people[3]])\n\nfunction onSubmit(e) {\n  e.preventDefault()\n  console.log([...new FormData(e.currentTarget).entries()])\n}\n\nfunction removePerson(person) {\n  activePersons.value = activePersons.value.filter((p) => p !== person)\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/dialog/dialog.vue",
    "content": "<template>\n  <div className=\"flex gap-4 p-12\">\n    <Button @click=\"toggleIsOpen()\">Toggle!</Button>\n    <Button @click=\"nested = true\">Show nested</Button>\n  </div>\n  <Nested v-if=\"nested\" @close=\"nested = false\" />\n\n  <TransitionRoot :show=\"isOpen\" as=\"template\">\n    <Dialog @close=\"setIsOpen\">\n      <div class=\"fixed inset-0 z-10 overflow-y-auto\">\n        <div\n          class=\"flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0\"\n        >\n          <TransitionChild\n            as=\"template\"\n            enter=\"ease-out duration-300\"\n            enterFrom=\"opacity-0\"\n            enterTo=\"opacity-75\"\n            leave=\"ease-in duration-200\"\n            leaveFrom=\"opacity-75\"\n            leaveTo=\"opacity-0\"\n            entered=\"opacity-75\"\n          >\n            <div className=\"fixed inset-0 bg-gray-500 transition-opacity\" />\n          </TransitionChild>\n\n          <TransitionChild\n            enter=\"ease-out transform duration-300\"\n            enterFrom=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n            enterTo=\"opacity-100 translate-y-0 sm:scale-100\"\n            leave=\"ease-in transform duration-200\"\n            leaveFrom=\"opacity-100 translate-y-0 sm:scale-100\"\n            leaveTo=\"opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95\"\n          >\n            <!-- This element is to trick the browser into centering the modal contents. -->\n            <span class=\"hidden sm:inline-block sm:h-screen sm:align-middle\" aria-hidden=\"true\">\n              &#8203;\n            </span>\n            <DialogPanel\n              class=\"inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle\"\n            >\n              <div class=\"bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4\">\n                <div class=\"sm:flex sm:items-start\">\n                  <div\n                    class=\"mx-auto flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10\"\n                  >\n                    <!-- Heroicon name: exclamation -->\n                    <svg\n                      class=\"h-6 w-6 text-red-600\"\n                      xmlns=\"http://www.w3.org/2000/svg\"\n                      fill=\"none\"\n                      viewBox=\"0 0 24 24\"\n                      stroke=\"currentColor\"\n                      aria-hidden=\"true\"\n                    >\n                      <path\n                        strokeLinecap=\"round\"\n                        strokeLinejoin=\"round\"\n                        stroke-width=\"2\"\n                        d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n                      />\n                    </svg>\n                  </div>\n                  <div class=\"mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left\">\n                    <DialogTitle as=\"h3\" class=\"text-lg font-medium leading-6 text-gray-900\">\n                      Deactivate account\n                    </DialogTitle>\n                    <div class=\"mt-2\">\n                      <p class=\"text-sm text-gray-500\">\n                        Are you sure you want to deactivate your account? All of your data will be\n                        permanently removed. This action cannot be undone.\n                      </p>\n                      <div class=\"relative mt-10 inline-block text-left\">\n                        <Menu>\n                          <MenuButton\n                            ref=\"trigger\"\n                            class=\"focus:outline-hidden ui-focus-visible:ring-2 ui-focus-visible:ring-offset-2 flex items-center rounded-md border border-gray-300 bg-white px-2 py-1 ring-gray-500 ring-offset-gray-100\"\n                          >\n                            <span>Choose a reason</span>\n                            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                              <path\n                                fillRule=\"evenodd\"\n                                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                                clipRule=\"evenodd\"\n                              />\n                            </svg>\n                          </MenuButton>\n\n                          <TransitionRoot\n                            enter=\"transition duration-300 ease-out\"\n                            enterFrom=\"transform scale-95 opacity-0\"\n                            enterTo=\"transform scale-100 opacity-100\"\n                            leave=\"transition duration-75 ease-out\"\n                            leaveFrom=\"transform scale-100 opacity-100\"\n                            leaveTo=\"transform scale-95 opacity-0\"\n                          >\n                            <Portal>\n                              <MenuItems\n                                ref=\"container\"\n                                class=\"outline-hidden z-20 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n                              >\n                                <div class=\"px-4 py-3\">\n                                  <p class=\"text-sm leading-5\">Signed in as</p>\n                                  <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">\n                                    tom@example.com\n                                  </p>\n                                </div>\n\n                                <div class=\"py-1\">\n                                  <MenuItem\n                                    as=\"a\"\n                                    href=\"#account-settings\"\n                                    :className=\"resolveClass\"\n                                  >\n                                    Account settings\n                                  </MenuItem>\n                                  <MenuItem as=\"a\" href=\"#support\" :className=\"resolveClass\">\n                                    Support\n                                  </MenuItem>\n                                  <MenuItem\n                                    as=\"a\"\n                                    disabled\n                                    href=\"#new-feature\"\n                                    :className=\"resolveClass\"\n                                  >\n                                    New feature (soon)\n                                  </MenuItem>\n                                  <MenuItem as=\"a\" href=\"#license\" :className=\"resolveClass\">\n                                    License\n                                  </MenuItem>\n                                </div>\n\n                                <div class=\"py-1\">\n                                  <MenuItem as=\"a\" href=\"#sign-out\" :className=\"resolveClass\">\n                                    Sign out\n                                  </MenuItem>\n                                </div>\n                              </MenuItems>\n                            </Portal>\n                          </TransitionRoot>\n                        </Menu>\n                      </div>\n                      <Flatpickr v-model=\"date\" />\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <div class=\"bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6\">\n                <Button type=\"button\" @click=\"setIsOpen(false)\"> Deactivate </Button>\n                <Button @click=\"setIsOpen(false)\"> Cancel </Button>\n              </div>\n            </DialogPanel>\n          </TransitionChild>\n        </div>\n      </div>\n    </Dialog>\n  </TransitionRoot>\n</template>\n\n<script>\nimport { ref, defineComponent, h } from 'vue'\nimport {\n  Dialog,\n  DialogTitle,\n  DialogOverlay,\n  DialogPanel,\n  Menu,\n  MenuButton,\n  MenuItems,\n  MenuItem,\n  Portal,\n  TransitionRoot,\n  TransitionChild,\n} from '@headlessui/vue'\nimport Flatpickr from 'vue-flatpickr-component'\nimport { usePopper } from '../../playground-utils/hooks/use-popper'\nimport Button from '../Button.vue'\n\nimport 'flatpickr/dist/themes/light.css'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nfunction resolveClass({ active, disabled }) {\n  return classNames(\n    'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n    active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n    disabled && 'cursor-not-allowed opacity-50'\n  )\n}\n\nlet Nested = defineComponent({\n  components: { Dialog, DialogOverlay },\n  emits: ['close'],\n  props: ['level'],\n  setup(props, { emit }) {\n    let showChild = ref(false)\n    function onClose() {\n      emit('close', false)\n    }\n\n    return () => {\n      let level = props.level ?? 0\n      return h(Dialog, { open: true, onClose, class: 'fixed inset-0 z-10' }, () => [\n        h(DialogOverlay, { class: 'fixed inset-0 bg-gray-500 opacity-25' }),\n        h(\n          'div',\n          {\n            class: 'fixed left-12 top-24 z-10 w-96 bg-white p-4',\n            style: { transform: `translate(calc(50px * ${level}), calc(50px * ${level}))` },\n          },\n          [\n            h('p', `Level: ${level}`),\n            h('div', { class: 'flex gap-4' }, [\n              h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} a`),\n              h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} b`),\n              h(Button, { onClick: () => (showChild.value = true) }, () => `Open ${level + 1} c`),\n            ]),\n          ]\n        ),\n        showChild.value &&\n          h(Nested, {\n            onClose: () => (showChild.value = false),\n            level: level + 1,\n          }),\n      ])\n    }\n  },\n})\n\nexport default {\n  components: {\n    Button,\n    Nested,\n    Dialog,\n    DialogTitle,\n    DialogOverlay,\n    DialogPanel,\n    Menu,\n    MenuButton,\n    MenuItems,\n    MenuItem,\n    Portal,\n    Flatpickr,\n    TransitionRoot,\n    TransitionChild,\n  },\n  setup() {\n    let isOpen = ref(false)\n    let [trigger, container] = usePopper({\n      placement: 'bottom-end',\n      strategy: 'fixed',\n      modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n    })\n    let nested = ref(false)\n    let date = ref(new Date())\n\n    return {\n      Button,\n      nested,\n      date,\n      isOpen,\n      trigger,\n      container,\n      setIsOpen(value) {\n        isOpen.value = value\n      },\n      toggleIsOpen() {\n        isOpen.value = !isOpen.value\n      },\n      resolveClass,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/dialog/slide-over.vue",
    "content": "<template>\n  <TransitionRoot as=\"template\" :show=\"open\">\n    <Dialog as=\"div\" class=\"relative z-10\" @close=\"open = false\">\n      <TransitionChild\n        as=\"template\"\n        enter=\"ease-in-out duration-500\"\n        enter-from=\"opacity-0\"\n        enter-to=\"opacity-100\"\n        leave=\"ease-in-out duration-500\"\n        leave-from=\"opacity-100\"\n        leave-to=\"opacity-0\"\n      >\n        <div class=\"fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity\" />\n      </TransitionChild>\n\n      <div class=\"fixed inset-0 overflow-hidden\">\n        <div class=\"absolute inset-0 overflow-hidden\">\n          <div class=\"pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10\">\n            <TransitionChild\n              as=\"template\"\n              enter=\"transform transition ease-in-out duration-500 sm:duration-700\"\n              enter-from=\"translate-x-full\"\n              enter-to=\"translate-x-0\"\n              leave=\"transform transition ease-in-out duration-500 sm:duration-700\"\n              leave-from=\"translate-x-0\"\n              leave-to=\"translate-x-full\"\n            >\n              <DialogPanel class=\"pointer-events-auto w-screen max-w-md\">\n                <div class=\"flex h-full flex-col overflow-y-scroll bg-white shadow-xl\">\n                  <div class=\"flex-1 overflow-y-auto px-4 py-6 sm:px-6\">\n                    <div class=\"flex items-start justify-between\">\n                      <DialogTitle class=\"text-lg font-medium text-gray-900\">Title...</DialogTitle>\n                      <div class=\"ml-3 flex h-7 items-center\">\n                        <button\n                          type=\"button\"\n                          class=\"-m-2 p-2 text-gray-400 hover:text-gray-500\"\n                          @click=\"open = false\"\n                        >\n                          <span class=\"sr-only\">Close panel</span>\n                          <XIcon class=\"h-6 w-6\" aria-hidden=\"true\" />\n                        </button>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </DialogPanel>\n            </TransitionChild>\n          </div>\n        </div>\n      </div>\n    </Dialog>\n  </TransitionRoot>\n</template>\n\n<script>\nimport { ref, watchEffect } from 'vue'\nimport { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'\nimport { XIcon } from '@heroicons/vue/outline'\n\nconst products = [\n  {\n    id: 1,\n    name: 'Throwback Hip Bag',\n    href: '#',\n    color: 'Salmon',\n    price: '$90.00',\n    quantity: 1,\n    imageSrc: 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-01.jpg',\n    imageAlt:\n      'Salmon orange fabric pouch with match zipper, gray zipper pull, and adjustable hip belt.',\n  },\n  {\n    id: 2,\n    name: 'Medium Stuff Satchel',\n    href: '#',\n    color: 'Blue',\n    price: '$32.00',\n    quantity: 1,\n    imageSrc: 'https://tailwindui.com/img/ecommerce-images/shopping-cart-page-04-product-02.jpg',\n    imageAlt:\n      'Front of satchel with blue canvas body, black straps and handle, drawstring top, and front zipper pouch.',\n  },\n  // More products...\n]\n\nexport default {\n  components: {\n    Dialog,\n    DialogPanel,\n    DialogTitle,\n    TransitionChild,\n    TransitionRoot,\n    XIcon,\n  },\n  setup() {\n    const open = ref(true)\n\n    watchEffect(() => {\n      if (open.value === false) {\n        setTimeout(() => {\n          open.value = true\n        }, 1000)\n      }\n    })\n\n    return {\n      products,\n      open,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/disclosure/disclosure.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <Disclosure>\n        <DisclosureButton>Trigger</DisclosureButton>\n\n        <TransitionRoot\n          enter=\"transition duration-1000 ease-out\"\n          enterFrom=\"transform scale-95 opacity-0\"\n          enterTo=\"transform scale-100 opacity-100\"\n          leave=\"transition duration-1000 ease-out\"\n          leaveFrom=\"transform scale-100 opacity-100\"\n          leaveTo=\"transform scale-95 opacity-0\"\n        >\n          <DisclosurePanel class=\"mt-4 bg-white p-4\">Content</DisclosurePanel>\n        </TransitionRoot>\n      </Disclosure>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { Disclosure, DisclosureButton, DisclosurePanel, TransitionRoot } from '@headlessui/vue'\n\nexport default {\n  components: {\n    Disclosure,\n    DisclosureButton,\n    DisclosurePanel,\n    TransitionRoot,\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/focus-trap/focus-trap.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <button>Previous</button>\n      <FocusTrap>\n        <button>Trigger</button>\n      </FocusTrap>\n      <button>After</button>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport { FocusTrap } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { FocusTrap },\n  setup(props, context) {\n    return {}\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/listbox/listbox.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <div class=\"space-y-1\">\n        <Listbox v-model=\"active\">\n          <ListboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\"\n            >Assigned to</ListboxLabel\n          >\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs inline-block w-full rounded-md\">\n              <ListboxButton\n                class=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\"\n              >\n                <span class=\"block truncate\">{{ active.name }}</span>\n                <span class=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ListboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ListboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ListboxOption\n                  v-for=\"person in people\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  as=\"template\"\n                  :disabled=\"person.disabled\"\n                >\n                  <li\n                    class=\"focus:outline-hidden ui-active:bg-indigo-600 ui-active:text-white ui-not-active:text-gray-900 ui-disabled:bg-gray-50 ui-disabled:text-gray-300 relative cursor-default select-none py-2 pl-3 pr-9\"\n                  >\n                    <span\n                      class=\"ui-selected:font-semibold ui-not-selected:font-normal block truncate\"\n                    >\n                      {{ person.name }}\n                    </span>\n                    <span\n                      class=\"ui-selected:flex ui-not-selected:hidden ui-active:text-white ui-not-active:text-indigo-600 absolute inset-y-0 right-0 items-center pr-4\"\n                    >\n                      <svg class=\"h-5 w-5\" viewbox=\"0 0 20 20\" fill=\"currentColor\">\n                        <path\n                          fillRule=\"evenodd\"\n                          d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                          clipRule=\"evenodd\"\n                        />\n                      </svg>\n                    </span>\n                  </li>\n                </ListboxOption>\n              </ListboxOptions>\n            </div>\n          </div>\n        </Listbox>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { ref } from 'vue'\nimport {\n  Listbox,\n  ListboxLabel,\n  ListboxButton,\n  ListboxOptions,\n  ListboxOption,\n} from '@headlessui/vue'\n\nexport default {\n  components: { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption },\n  setup(props, context) {\n    let people = [\n      { id: 1, name: 'Wade Cooper' },\n      { id: 2, name: 'Arlene Mccoy' },\n      { id: 3, name: 'Devon Webb' },\n      { id: 4, name: 'Tom Cook' },\n      { id: 5, name: 'Tanya Fox', disabled: true },\n      { id: 6, name: 'Hellen Schmidt' },\n      { id: 7, name: 'Caroline Schultz' },\n      { id: 8, name: 'Mason Heaney' },\n      { id: 9, name: 'Claudie Smitham' },\n      { id: 10, name: 'Emil Schaefer' },\n    ]\n\n    let active = ref(people[Math.floor(Math.random() * people.length)])\n\n    return {\n      people,\n      active,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/listbox/multi-select.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n    <div class=\"w-full max-w-4xl\">\n      <div class=\"space-y-1\">\n        <form @submit=\"onSubmit\">\n          <Listbox v-model=\"activePersons\" name=\"people\" multiple>\n            <ListboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\">\n              Assigned to\n            </ListboxLabel>\n\n            <div class=\"relative\">\n              <span class=\"shadow-xs inline-block w-full rounded-md\">\n                <ListboxButton\n                  class=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-2 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\"\n                >\n                  <span class=\"block flex flex-wrap gap-2\">\n                    <span v-if=\"activePersons.length === 0\" class=\"p-0.5\">Empty</span>\n                    <span\n                      v-for=\"person in activePersons\"\n                      :key=\"person.id\"\n                      class=\"flex items-center gap-1 rounded-sm bg-blue-50 px-2 py-0.5\"\n                    >\n                      <span>{{ person.name }}</span>\n                      <svg\n                        class=\"h-4 w-4 cursor-pointer\"\n                        fill=\"none\"\n                        stroke=\"currentColor\"\n                        viewBox=\"0 0 24 24\"\n                        xmlns=\"http://www.w3.org/2000/svg\"\n                        @click.stop.prevent=\"removePerson(person)\"\n                      >\n                        <path\n                          stroke-linecap=\"round\"\n                          stroke-linejoin=\"round\"\n                          stroke-width=\"2\"\n                          d=\"M6 18L18 6M6 6l12 12\"\n                        />\n                      </svg>\n                    </span>\n                  </span>\n                  <span\n                    class=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\"\n                  >\n                    <svg\n                      class=\"h-5 w-5 text-gray-400\"\n                      viewBox=\"0 0 20 20\"\n                      fill=\"none\"\n                      stroke=\"currentColor\"\n                    >\n                      <path\n                        d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                        stroke-width=\"1.5\"\n                        stroke-linecap=\"round\"\n                        stroke-linejoin=\"round\"\n                      />\n                    </svg>\n                  </span>\n                </ListboxButton>\n              </span>\n\n              <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n                <ListboxOptions\n                  class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n                >\n                  <ListboxOption\n                    v-for=\"person in people\"\n                    :key=\"person.id\"\n                    :value=\"person\"\n                    as=\"template\"\n                    v-slot=\"{ active, selected }\"\n                  >\n                    <li\n                      class=\"focus:outline-hidden relative cursor-default select-none py-2 pl-3 pr-9\"\n                      :class=\"active ? 'bg-indigo-600 text-white' : 'text-gray-900'\"\n                    >\n                      <span\n                        class=\"block truncate\"\n                        :class=\"{ 'font-semibold': selected, 'font-normal': !selected }\"\n                      >\n                        {{ person.name }}\n                      </span>\n                      <span\n                        v-if=\"selected\"\n                        class=\"absolute inset-y-0 right-0 flex items-center pr-4\"\n                        :class=\"{ 'text-white': active, 'text-indigo-600': !active }\"\n                      >\n                        <svg class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n                          <path\n                            fillRule=\"evenodd\"\n                            d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                            clipRule=\"evenodd\"\n                          />\n                        </svg>\n                      </span>\n                    </li>\n                  </ListboxOption>\n                </ListboxOptions>\n              </div>\n            </div>\n          </Listbox>\n          <button\n            class=\"shadow-xs focus:outline-hidden mt-2 inline-flex items-center rounded-sm border border-gray-300 bg-white px-2.5 py-1.5 text-xs font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2\"\n          >\n            Submit\n          </button>\n        </form>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport {\n  Listbox,\n  ListboxLabel,\n  ListboxButton,\n  ListboxOptions,\n  ListboxOption,\n} from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet people = [\n  { id: 1, name: 'Wade Cooper' },\n  { id: 2, name: 'Arlene Mccoy' },\n  { id: 3, name: 'Devon Webb' },\n  { id: 4, name: 'Tom Cook' },\n  { id: 5, name: 'Tanya Fox' },\n  { id: 6, name: 'Hellen Schmidt' },\n  { id: 7, name: 'Caroline Schultz' },\n  { id: 8, name: 'Mason Heaney' },\n  { id: 9, name: 'Claudie Smitham' },\n  { id: 10, name: 'Emil Schaefer' },\n]\n\nlet activePersons = ref([people[2], people[3]])\n\nfunction onSubmit(e) {\n  e.preventDefault()\n  console.log([...new FormData(e.currentTarget).entries()])\n}\n\nfunction removePerson(person) {\n  activePersons.value = activePersons.value.filter((p) => p !== person)\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/listbox/multiple-elements.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n    <div class=\"w-64\">\n      <div class=\"space-y-1\">\n        <Listbox v-model=\"active\">\n          <ListboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\"\n            >Assigned to</ListboxLabel\n          >\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs inline-block w-full rounded-md\">\n              <ListboxButton\n                class=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\"\n              >\n                <span class=\"block truncate\">{{ active.name }}</span>\n                <span class=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ListboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ListboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ListboxOption\n                  v-for=\"person in people\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  :className=\"resolveListboxOptionClassName\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <span\n                    :class=\"\n                      classNames('block truncate', selected ? 'font-semibold' : 'font-normal')\n                    \"\n                  >\n                    {{ person.name }}\n                  </span>\n                  <span\n                    v-if=\"selected\"\n                    :class=\"\n                      classNames(\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600'\n                      )\n                    \"\n                  >\n                    <svg class=\"h-5 w-5\" viewbox=\"0 0 20 20\" fill=\"currentColor\">\n                      <path\n                        fillRule=\"evenodd\"\n                        d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                        clipRule=\"evenodd\"\n                      />\n                    </svg>\n                  </span>\n                </ListboxOption>\n              </ListboxOptions>\n            </div>\n          </div>\n        </Listbox>\n      </div>\n    </div>\n\n    <div>\n      <label for=\"email\" class=\"block text-sm font-medium leading-5 text-gray-700\"> Email </label>\n      <div class=\"shadow-xs relative mt-1 rounded-md\">\n        <input\n          class=\"form-input block w-full sm:text-sm sm:leading-5\"\n          placeholder=\"you@example.com\"\n        />\n      </div>\n    </div>\n\n    <div class=\"w-64\">\n      <div class=\"space-y-1\">\n        <Listbox v-model=\"active\">\n          <ListboxLabel class=\"block text-sm font-medium leading-5 text-gray-700\"\n            >Assigned to</ListboxLabel\n          >\n\n          <div class=\"relative\">\n            <span class=\"shadow-xs inline-block w-full rounded-md\">\n              <ListboxButton\n                class=\"focus:shadow-outline-blue focus:outline-hidden relative w-full cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out focus:border-blue-300 sm:text-sm sm:leading-5\"\n              >\n                <span class=\"block truncate\">{{ active.name }}</span>\n                <span class=\"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2\">\n                  <svg\n                    class=\"h-5 w-5 text-gray-400\"\n                    viewBox=\"0 0 20 20\"\n                    fill=\"none\"\n                    stroke=\"currentColor\"\n                  >\n                    <path\n                      d=\"M7 7l3-3 3 3m0 6l-3 3-3-3\"\n                      strokeWidth=\"1.5\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              </ListboxButton>\n            </span>\n\n            <div class=\"absolute mt-1 w-full rounded-md bg-white shadow-lg\">\n              <ListboxOptions\n                class=\"shadow-2xs focus:outline-hidden max-h-60 overflow-auto rounded-md py-1 text-base leading-6 sm:text-sm sm:leading-5\"\n              >\n                <ListboxOption\n                  v-for=\"person in people\"\n                  :key=\"person.id\"\n                  :value=\"person\"\n                  :className=\"resolveListboxOptionClassName\"\n                  v-slot=\"{ active, selected }\"\n                >\n                  <span\n                    :class=\"\n                      classNames('block truncate', selected ? 'font-semibold' : 'font-normal')\n                    \"\n                  >\n                    {{ person.name }}\n                  </span>\n                  <span\n                    v-if=\"selected\"\n                    :class=\"\n                      classNames(\n                        'absolute inset-y-0 right-0 flex items-center pr-4',\n                        active ? 'text-white' : 'text-indigo-600'\n                      )\n                    \"\n                  >\n                    <svg class=\"h-5 w-5\" viewbox=\"0 0 20 20\" fill=\"currentColor\">\n                      <path\n                        fillRule=\"evenodd\"\n                        d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\"\n                        clipRule=\"evenodd\"\n                      />\n                    </svg>\n                  </span>\n                </ListboxOption>\n              </ListboxOptions>\n            </div>\n          </div>\n        </Listbox>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport {\n  Listbox,\n  ListboxLabel,\n  ListboxButton,\n  ListboxOptions,\n  ListboxOption,\n} from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption },\n  setup(props, context) {\n    let people = [\n      { id: 1, name: 'Wade Cooper' },\n      { id: 2, name: 'Arlene Mccoy' },\n      { id: 3, name: 'Devon Webb' },\n      { id: 4, name: 'Tom Cook' },\n      { id: 5, name: 'Tanya Fox' },\n      { id: 6, name: 'Hellen Schmidt' },\n      { id: 7, name: 'Caroline Schultz' },\n      { id: 8, name: 'Mason Heaney' },\n      { id: 9, name: 'Claudie Smitham' },\n      { id: 10, name: 'Emil Schaefer' },\n    ]\n\n    let active = ref(people[Math.floor(Math.random() * people.length)])\n\n    return {\n      people,\n      active,\n      classNames,\n      resolveListboxOptionClassName({ active }) {\n        return classNames(\n          'relative py-2 pl-3 cursor-default select-none pr-9 focus:outline-hidden',\n          active ? 'text-white bg-indigo-600' : 'text-gray-900'\n        )\n      },\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/menu-with-floating-ui.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mt-64 inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs inline-flex rounded-md\">\n          <MenuButton\n            ref=\"reference\"\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <teleport to=\"body\">\n          <MenuItems\n            ref=\"floating\"\n            :style=\"floatingStyles\"\n            class=\"outline-hidden absolute right-0 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n          >\n            <div class=\"px-4 py-3\">\n              <p class=\"text-sm leading-5\">Signed in as</p>\n              <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n            </div>\n\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#account-settings\" :className=\"resolveClass\">\n                Account settings\n              </MenuItem>\n              <MenuItem v-slot=\"data\">\n                <a href=\"#support\" :class=\"resolveClass(data)\">Support</a>\n              </MenuItem>\n              <MenuItem as=\"a\" disabled href=\"#new-feature\" :className=\"resolveClass\">\n                New feature (soon)\n              </MenuItem>\n              <MenuItem as=\"a\" href=\"#license\" :className=\"resolveClass\">License</MenuItem>\n            </div>\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#sign-out\" :className=\"resolveClass\">Sign out</MenuItem>\n            </div>\n          </MenuItems>\n        </teleport>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch, computed } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\nimport { useFloating, offset } from '@floating-ui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { Menu, MenuButton, MenuItems, MenuItem },\n  setup(props, context) {\n    let reference = ref(null)\n    let floating = ref(null)\n\n    let { floatingStyles } = useFloating(\n      computed(() => reference.value?.el),\n      computed(() => floating.value?.el),\n      {\n        placement: 'bottom-end',\n        strategy: 'fixed',\n        middleware: [offset(10)],\n      }\n    )\n\n    function resolveClass({ active, disabled }) {\n      return classNames(\n        'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n        active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n        disabled && 'cursor-not-allowed opacity-50'\n      )\n    }\n\n    return {\n      reference,\n      floating,\n      floatingStyles,\n      resolveClass,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/menu-with-popper.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mt-64 inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs inline-flex rounded-md\">\n          <MenuButton\n            ref=\"trigger\"\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <teleport to=\"body\">\n          <MenuItems\n            ref=\"container\"\n            class=\"outline-hidden absolute right-0 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n          >\n            <div class=\"px-4 py-3\">\n              <p class=\"text-sm leading-5\">Signed in as</p>\n              <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n            </div>\n\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#account-settings\" :className=\"resolveClass\">\n                Account settings\n              </MenuItem>\n              <MenuItem v-slot=\"data\">\n                <a href=\"#support\" :class=\"resolveClass(data)\">Support</a>\n              </MenuItem>\n              <MenuItem as=\"a\" disabled href=\"#new-feature\" :className=\"resolveClass\">\n                New feature (soon)\n              </MenuItem>\n              <MenuItem as=\"a\" href=\"#license\" :className=\"resolveClass\">License</MenuItem>\n            </div>\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#sign-out\" :className=\"resolveClass\">Sign out</MenuItem>\n            </div>\n          </MenuItems>\n        </teleport>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\nimport { usePopper } from '../../playground-utils/hooks/use-popper'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { Menu, MenuButton, MenuItems, MenuItem },\n  setup(props, context) {\n    let [trigger, container] = usePopper({\n      placement: 'bottom-end',\n      strategy: 'fixed',\n      modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n    })\n\n    function resolveClass({ active, disabled }) {\n      return classNames(\n        'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n        active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n        disabled && 'cursor-not-allowed opacity-50'\n      )\n    }\n\n    return {\n      trigger,\n      container,\n      resolveClass,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/menu-with-transition-and-popper.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"relative mt-64 inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs inline-flex rounded-md\">\n          <MenuButton\n            ref=\"trigger\"\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <div ref=\"container\" class=\"w-56\">\n          <transition\n            enter-active-class=\"transition duration-100 ease-out\"\n            enter-from-class=\"transform scale-95 opacity-0\"\n            enter-to-class=\"transform scale-100 opacity-100\"\n            leave-active-class=\"transition duration-75 ease-out\"\n            leave-from-class=\"transform scale-100 opacity-100\"\n            leave-to-class=\"transform scale-95 opacity-0\"\n          >\n            <MenuItems\n              class=\"outline-hidden w-full divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg\"\n            >\n              <div class=\"px-4 py-3\">\n                <p class=\"text-sm leading-5\">Signed in as</p>\n                <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n              </div>\n\n              <div class=\"py-1\">\n                <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#account-settings\">\n                  Account settings\n                </MenuItem>\n                <MenuItem v-slot=\"data\">\n                  <a href=\"#support\" :class=\"resolveClass(data)\">Support</a>\n                </MenuItem>\n                <MenuItem as=\"a\" :className=\"resolveClass\" disabled href=\"#new-feature\">\n                  New feature (soon)\n                </MenuItem>\n                <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#license\">License</MenuItem>\n              </div>\n              <div class=\"py-1\">\n                <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#sign-out\">Sign out</MenuItem>\n              </div>\n            </MenuItems>\n          </transition>\n        </div>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\nimport { usePopper } from '../../playground-utils/hooks/use-popper'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { Menu, MenuButton, MenuItems, MenuItem },\n  setup(props, context) {\n    let [trigger, container] = usePopper({\n      placement: 'bottom-end',\n      strategy: 'fixed',\n      modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],\n    })\n\n    function resolveClass({ active, disabled }) {\n      return classNames(\n        'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n        active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n        disabled && 'cursor-not-allowed opacity-50'\n      )\n    }\n\n    return {\n      trigger,\n      container,\n      resolveClass,\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/menu-with-transition.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"relative inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs rounded-md\">\n          <MenuButton\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <transition\n          enter-active-class=\"transition duration-100 ease-out\"\n          enter-from-class=\"transform scale-95 opacity-0\"\n          enter-to-class=\"transform scale-100 opacity-100\"\n          leave-active-class=\"transition duration-75 ease-out\"\n          leave-from-class=\"transform scale-100 opacity-100\"\n          leave-to-class=\"transform scale-95 opacity-0\"\n        >\n          <MenuItems\n            class=\"outline-hidden 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\"\n          >\n            <div class=\"px-4 py-3\">\n              <p class=\"text-sm leading-5\">Signed in as</p>\n              <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n            </div>\n\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#account-settings\" :className=\"resolveClass\">\n                Account settings\n              </MenuItem>\n              <MenuItem as=\"a\" href=\"#support\" :className=\"resolveClass\">Support</MenuItem>\n              <MenuItem as=\"a\" disabled href=\"#new-feature\" :className=\"resolveClass\">\n                New feature (soon)\n              </MenuItem>\n              <MenuItem as=\"a\" href=\"#license\" :className=\"resolveClass\">License</MenuItem>\n            </div>\n            <div class=\"py-1\">\n              <MenuItem as=\"a\" href=\"#sign-out\" :className=\"resolveClass\">Sign out</MenuItem>\n            </div>\n          </MenuItems>\n        </transition>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: {\n    Menu,\n    MenuButton,\n    MenuItems,\n    MenuItem,\n  },\n  setup() {\n    return {\n      resolveClass({ active, disabled }) {\n        return classNames(\n          'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left',\n          active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',\n          disabled && 'cursor-not-allowed opacity-50'\n        )\n      },\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/menu.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"relative inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs rounded-md\">\n          <MenuButton\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <MenuItems\n          class=\"outline-hidden 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\"\n        >\n          <div class=\"px-4 py-3\">\n            <p class=\"text-sm leading-5\">Signed in as</p>\n            <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n          </div>\n\n          <div class=\"py-1\">\n            <CustomMenuItem href=\"#account-settings\">Account settings</CustomMenuItem>\n            <CustomMenuItem href=\"#support\">Support</CustomMenuItem>\n            <CustomMenuItem disabled href=\"#new-feature\">New feature (soon)</CustomMenuItem>\n            <CustomMenuItem href=\"#license\">License</CustomMenuItem>\n          </div>\n          <div class=\"py-1\">\n            <CustomMenuItem href=\"#sign-out\">Sign out</CustomMenuItem>\n          </div>\n        </MenuItems>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, watchEffect } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet CustomMenuItem = defineComponent({\n  components: { Menu, MenuButton, MenuItems, MenuItem },\n  setup(props, { slots }) {\n    return () => {\n      return h(MenuItem, ({ active, disabled }) => {\n        return h(\n          'a',\n          {\n            class: classNames(\n              'flex justify-between w-full text-left px-4 py-2 text-sm leading-5',\n              active ? 'bg-indigo-500 text-white' : 'text-gray-700',\n              disabled && 'cursor-not-allowed opacity-50'\n            ),\n          },\n          [\n            h('span', { class: classNames(active && 'font-bold') }, slots.default()),\n            h('kbd', { class: classNames('font-sans', active && 'text-indigo-50') }, '⌘K'),\n          ]\n        )\n      })\n    }\n  },\n})\n\nexport default {\n  components: {\n    Menu,\n    MenuButton,\n    MenuItems,\n    MenuItem,\n    CustomMenuItem,\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/menu/multiple-elements.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center space-x-4 bg-gray-50 p-12\">\n    <div class=\"relative inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs rounded-md\">\n          <MenuButton\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <MenuItems\n          class=\"outline-hidden 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\"\n        >\n          <div class=\"px-4 py-3\">\n            <p class=\"text-sm leading-5\">Signed in as</p>\n            <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n          </div>\n\n          <div class=\"py-1\">\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#account-settings\"\n              >Account settings</MenuItem\n            >\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#support\">Support</MenuItem>\n            <MenuItem as=\"a\" :className=\"resolveClass\" disabled href=\"#new-feature\"\n              >New feature (soon)</MenuItem\n            >\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#license\">License</MenuItem>\n          </div>\n          <div class=\"py-1\">\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#sign-out\">Sign out</MenuItem>\n          </div>\n        </MenuItems>\n      </Menu>\n    </div>\n\n    <div>\n      <div class=\"shadow-xs relative rounded-md\">\n        <input\n          class=\"form-input block w-full sm:text-sm sm:leading-5\"\n          placeholder=\"you@example.com\"\n        />\n      </div>\n    </div>\n\n    <div class=\"relative inline-block text-left\">\n      <Menu>\n        <span class=\"shadow-xs rounded-md\">\n          <MenuButton\n            class=\"focus:shadow-outline-blue focus:outline-hidden 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 active:bg-gray-50 active:text-gray-800\"\n          >\n            <span>Options</span>\n            <svg class=\"-mr-1 ml-2 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n              <path\n                fillRule=\"evenodd\"\n                d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"\n                clipRule=\"evenodd\"\n              />\n            </svg>\n          </MenuButton>\n        </span>\n\n        <MenuItems\n          class=\"outline-hidden 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\"\n        >\n          <div class=\"px-4 py-3\">\n            <p class=\"text-sm leading-5\">Signed in as</p>\n            <p class=\"truncate text-sm font-medium leading-5 text-gray-900\">tom@example.com</p>\n          </div>\n\n          <div class=\"py-1\">\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#account-settings\"\n              >Account settings</MenuItem\n            >\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#support\">Support</MenuItem>\n            <MenuItem as=\"a\" :className=\"resolveClass\" disabled href=\"#new-feature\"\n              >New feature (soon)</MenuItem\n            >\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#license\">License</MenuItem>\n          </div>\n          <div class=\"py-1\">\n            <MenuItem as=\"a\" :className=\"resolveClass\" href=\"#sign-out\">Sign out</MenuItem>\n          </div>\n        </MenuItems>\n      </Menu>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h } from 'vue'\nimport { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: {\n    Menu,\n    MenuButton,\n    MenuItems,\n    MenuItem,\n  },\n  setup() {\n    return {\n      resolveClass({ active, disabled }) {\n        return classNames(\n          'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700',\n          active && 'bg-gray-100 text-gray-900',\n          disabled && 'cursor-not-allowed opacity-50'\n        )\n      },\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/popover/popover.vue",
    "content": "<template>\n  <div class=\"flex items-center justify-center space-x-12 p-12\">\n    <button>Previous</button>\n\n    <PopoverGroup as=\"nav\" ar-label=\"Mythical University\" class=\"flex space-x-3\">\n      <Popover as=\"div\" class=\"relative\">\n        <transition\n          enter-active-class=\"transition ease-out duration-300 transform\"\n          enter-from-class=\"opacity-0\"\n          enter-to-class=\"opacity-100\"\n          leave-active-class=\"transition ease-in duration-300 transform\"\n          leave-from-class=\"opacity-100\"\n          leave-to-class=\"opacity-0\"\n        >\n          <PopoverOverlay class=\"fixed inset-0 z-20 bg-gray-500 bg-opacity-75\"></PopoverOverlay>\n        </transition>\n\n        <PopoverButton\n          class=\"focus:outline-hidden relative z-30 border-2 border-transparent bg-gray-300 px-3 py-2 focus:border-blue-900\"\n          >Normal</PopoverButton\n        >\n        <PopoverPanel class=\"absolute z-30 flex w-64 flex-col border-2 border-blue-900 bg-gray-100\">\n          <button\n            v-for=\"(link, i) of links\"\n            :key=\"i\"\n            :hidden=\"i === 2\"\n            class=\"focus:outline-hidden border-2 border-transparent px-3 py-2 text-left hover:bg-gray-200 focus:border-blue-900 focus:bg-gray-200\"\n          >\n            Normal - {{ link }}\n          </button>\n        </PopoverPanel>\n      </Popover>\n\n      <Popover as=\"div\" class=\"relative\">\n        <PopoverButton\n          class=\"focus:outline-hidden border-2 border-transparent bg-gray-300 px-3 py-2 focus:border-blue-900\"\n          >Focus</PopoverButton\n        >\n        <PopoverPanel\n          focus\n          class=\"absolute flex w-64 flex-col border-2 border-blue-900 bg-gray-100\"\n        >\n          <button\n            v-for=\"(link, i) of links\"\n            :key=\"i\"\n            class=\"focus:outline-hidden border-2 border-transparent px-3 py-2 text-left hover:bg-gray-200 focus:border-blue-900 focus:bg-gray-200\"\n          >\n            Focus - {{ link }}\n          </button>\n        </PopoverPanel>\n      </Popover>\n\n      <Popover as=\"div\" class=\"relative\">\n        <PopoverButton\n          ref=\"trigger1\"\n          class=\"focus:outline-hidden border-2 border-transparent bg-gray-300 px-3 py-2 focus:border-blue-900\"\n          >Portal</PopoverButton\n        >\n        <Portal>\n          <PopoverPanel\n            ref=\"container1\"\n            class=\"flex w-64 flex-col border-2 border-blue-900 bg-gray-100\"\n          >\n            <button\n              v-for=\"(link, i) of links\"\n              :key=\"i\"\n              class=\"focus:outline-hidden border-2 border-transparent px-3 py-2 text-left hover:bg-gray-200 focus:border-blue-900 focus:bg-gray-200\"\n            >\n              Portal - {{ link }}\n            </button>\n          </PopoverPanel>\n        </Portal>\n      </Popover>\n\n      <Popover as=\"div\" class=\"relative\">\n        <PopoverButton\n          ref=\"trigger2\"\n          class=\"focus:outline-hidden border-2 border-transparent bg-gray-300 px-3 py-2 focus:border-blue-900\"\n          >Focus in portal</PopoverButton\n        >\n        <Portal>\n          <PopoverPanel\n            ref=\"container2\"\n            focus\n            class=\"flex w-64 flex-col border-2 border-blue-900 bg-gray-100\"\n          >\n            <button\n              v-for=\"(link, i) of links\"\n              :key=\"i\"\n              class=\"focus:outline-hidden border-2 border-transparent px-3 py-2 text-left hover:bg-gray-200 focus:border-blue-900 focus:bg-gray-200\"\n            >\n              Focus in Portal - {{ link }}\n            </button>\n          </PopoverPanel>\n        </Portal>\n      </Popover>\n    </PopoverGroup>\n\n    <button>Next</button>\n  </div>\n</template>\n\n<script>\nimport { defineComponent } from 'vue'\nimport {\n  Popover,\n  PopoverOverlay,\n  PopoverPanel,\n  PopoverGroup,\n  PopoverButton,\n  Portal,\n} from '@headlessui/vue'\nimport { usePopper } from '../../playground-utils/hooks/use-popper'\n\nfunction html(templates) {\n  return templates.join('')\n}\n\nexport default {\n  components: {\n    Popover,\n    PopoverPanel,\n    PopoverOverlay,\n    PopoverGroup,\n    PopoverButton,\n    Portal,\n  },\n  setup() {\n    let links = ['First', 'Second', 'Third', 'Fourth']\n\n    let [trigger1, container1] = usePopper({\n      placement: 'bottom-start',\n      strategy: 'fixed',\n    })\n\n    let [trigger2, container2] = usePopper({\n      placement: 'bottom-start',\n      strategy: 'fixed',\n    })\n\n    return { links, trigger1, container1, trigger2, container2 }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/portal/portal.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen justify-center bg-gray-50 p-12\">\n    <div class=\"mx-auto w-full max-w-xs\">\n      <main>\n        <aside ref=\"container\" id=\"group-1\">A</aside>\n\n        <PortalGroup :target=\"container\">\n          <section id=\"group-2\">\n            <span>B</span>\n          </section>\n          <Portal>Next to A</Portal>\n        </PortalGroup>\n\n        <Portal>I am in the portal root</Portal>\n      </main>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport { Portal, PortalGroup } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { Portal, PortalGroup },\n  setup(props, context) {\n    let container = ref(null)\n    return { container }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/radio-group/radio-group.vue",
    "content": "<template>\n  <div class=\"max-w-xl p-12\">\n    <a href=\"/\">Link before</a>\n    <RadioGroup v-model=\"active\">\n      <fieldset class=\"space-y-4\">\n        <legend>\n          <h2 class=\"text-xl\">Privacy setting</h2>\n        </legend>\n\n        <div class=\"-space-y-px rounded-md bg-white\">\n          <RadioGroupOption\n            v-for=\"({ id, name, description }, i) in access\"\n            :key=\"id\"\n            :value=\"id\"\n            v-slot=\"{ active, checked }\"\n            :className=\"\n              ({ active }) =>\n                classNames(\n                  // Rounded corners\n                  i === 0 && 'rounded-tl-md rounded-tr-md',\n                  access.length - 1 === i && 'rounded-bl-md rounded-br-md',\n\n                  // Shared\n                  'relative border p-4 flex focus:outline-hidden',\n                  active ? 'bg-indigo-50 border-indigo-200 z-10' : 'border-gray-200'\n                )\n            \"\n          >\n            <div class=\"flex w-full items-center justify-between\">\n              <div class=\"ml-3 flex cursor-pointer flex-col\">\n                <span\n                  :class=\"[\n                    'block text-sm font-medium leading-5',\n                    active ? 'text-indigo-900' : 'text-gray-900',\n                  ]\"\n                >\n                  {{ name }}\n                </span>\n                <span\n                  :class=\"['block text-sm leading-5', active ? 'text-indigo-700' : 'text-gray-500']\"\n                >\n                  {{ description }}\n                </span>\n              </div>\n              <div>\n                <svg\n                  v-if=\"checked\"\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  fill=\"none\"\n                  viewBox=\"0 0 24 24\"\n                  stroke=\"currentColor\"\n                  class=\"h-5 w-5 text-indigo-500\"\n                >\n                  <path\n                    stroke-linecap=\"round\"\n                    stroke-linejoin=\"round\"\n                    stroke-width=\"2\"\n                    d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n                  />\n                </svg>\n              </div>\n            </div>\n          </RadioGroupOption>\n        </div>\n      </fieldset>\n    </RadioGroup>\n    <a href=\"/\">Link after</a>\n  </div>\n</template>\n\n<script>\nimport { ref } from 'vue'\nimport { RadioGroup, RadioGroupOption } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean)\n}\n\nexport default {\n  components: { RadioGroup, RadioGroupOption },\n  setup() {\n    let active = ref()\n    let access = ref([\n      {\n        id: 'access-1',\n        name: 'Public access',\n        description: 'This project would be available to anyone who has the link',\n      },\n      {\n        id: 'access-2',\n        name: 'Private to Project Members',\n        description: 'Only members of this project would be able to access',\n      },\n      {\n        id: 'access-3',\n        name: 'Private to you',\n        description: 'You are the only one able to access this project',\n      },\n    ])\n\n    return { active, access, classNames }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/switch/switch.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen items-start justify-center bg-gray-50 p-12\">\n    <SwitchGroup as=\"div\" class=\"flex items-center space-x-4\">\n      <SwitchLabel>Enable notifications</SwitchLabel>\n\n      <Switch\n        as=\"button\"\n        v-model=\"state\"\n        :class=\"resolveSwitchClass({ checked: state })\"\n        v-slot=\"{ checked }\"\n      >\n        <span\n          class=\"inline-block h-5 w-5 transform rounded-full bg-white transition duration-200 ease-in-out\"\n          :class=\"{ 'translate-x-5': checked, 'translate-x-0': !checked }\"\n        />\n      </Switch>\n    </SwitchGroup>\n  </div>\n</template>\n\n<script>\nimport { ref } from 'vue'\nimport { SwitchGroup, Switch, SwitchLabel } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nexport default {\n  components: { SwitchGroup, Switch, SwitchLabel },\n  setup() {\n    let state = ref(false)\n\n    return {\n      state,\n      resolveSwitchClass({ checked }) {\n        return classNames(\n          'focus:shadow-outline relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-hidden',\n          checked ? 'bg-indigo-600 hover:bg-indigo-800' : 'bg-gray-200 hover:bg-gray-400'\n        )\n      },\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/tabs/simple-tabs.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen flex-col items-start space-y-12 bg-gray-50 p-12\">\n    <TabGroup class=\"flex w-full max-w-3xl flex-col\" as=\"div\">\n      <TabList class=\"relative z-0 flex divide-x divide-gray-200 rounded-lg shadow-sm\">\n        <Tab\n          v-for=\"tab in tabs\"\n          :key=\"tab.name\"\n          class=\"group relative min-w-0 flex-1 overflow-hidden bg-white px-4 py-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10\"\n          v-slot=\"{ selected }\"\n        >\n          <span>{{ tab.name }}</span>\n          <small v-if=\"tab.disabled\" class=\"inline-block px-4 text-xs\">(disabled)</small>\n          <span\n            aria-hidden=\"true\"\n            class=\"absolute inset-x-0 bottom-0 h-0.5\"\n            :class=\"{ 'bg-indigo-500': selected, 'bg-transparent': !selected }\"\n          />\n        </Tab>\n      </TabList>\n\n      <TabPanels class=\"mt-4\">\n        <TabPanel v-for=\"tab in tabs\" class=\"rounded-lg bg-white p-4 shadow-sm\" :key=\"tab.name\">\n          {{ tab.content }}\n        </TabPanel>\n      </TabPanels>\n    </TabGroup>\n  </div>\n</template>\n\n<script setup>\nimport { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet tabs = [\n  { name: 'My Account', content: 'Tab content for my account' },\n  { name: 'Company', content: 'Tab content for company' },\n  { name: 'Team Members', content: 'Tab content for team members' },\n  { name: 'Billing', content: 'Tab content for billing' },\n]\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/components/tabs/tabs.vue",
    "content": "<template>\n  <div class=\"flex h-full w-screen flex-col items-start space-y-12 bg-gray-50 p-12\">\n    <SwitchGroup as=\"div\" class=\"flex items-center space-x-4\">\n      <SwitchLabel>Manual keyboard activation</SwitchLabel>\n\n      <Switch as=\"button\" v-model=\"manual\" :className=\"resolveSwitchClass\" v-slot=\"{ checked }\">\n        <span\n          class=\"inline-block h-5 w-5 transform rounded-full bg-white transition duration-200 ease-in-out\"\n          :class=\"{ 'translate-x-5': checked, 'translate-x-0': !checked }\"\n        />\n      </Switch>\n    </SwitchGroup>\n\n    <TabGroup class=\"flex w-full max-w-3xl flex-col\" as=\"div\" :manual=\"manual\">\n      <TabList class=\"relative z-0 flex divide-x divide-gray-200 rounded-lg shadow-sm\">\n        <Tab\n          v-for=\"(tab, tabIdx) in tabs\"\n          :key=\"tab.name\"\n          :disabled=\"tab.disabled\"\n          class=\"group relative min-w-0 flex-1 overflow-hidden bg-white px-4 py-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10\"\n          :class=\"{\n            'text-gray-900': selected,\n            'text-gray-500 hover:text-gray-700': !selected,\n            'rounded-l-lg': tabIdx === 0,\n            'rounded-r-lg': tabIdx === tabs.length - 1,\n            'opacity-50': tab.disabled,\n          }\"\n          v-slot=\"{ selected }\"\n        >\n          <span>{{ tab.name }}</span>\n          <small v-if=\"tab.disabled\" class=\"inline-block px-4 text-xs\">(disabled)</small>\n          <span\n            aria-hidden=\"true\"\n            class=\"absolute inset-x-0 bottom-0 h-0.5\"\n            :class=\"{ 'bg-indigo-500': selected, 'bg-transparent': !selected }\"\n          />\n        </Tab>\n      </TabList>\n\n      <TabPanels class=\"mt-4\">\n        <TabPanel v-for=\"tab in tabs\" class=\"rounded-lg bg-white p-4 shadow-sm\" key=\"tab.name\">\n          {{ tab.content }}\n        </TabPanel>\n      </TabPanels>\n    </TabGroup>\n  </div>\n</template>\n\n<script>\nimport { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'\nimport {\n  SwitchGroup,\n  Switch,\n  SwitchLabel,\n  TabGroup,\n  TabList,\n  Tab,\n  TabPanels,\n  TabPanel,\n} from '@headlessui/vue'\n\nfunction classNames(...classes) {\n  return classes.filter(Boolean).join(' ')\n}\n\nlet tabs = [\n  { name: 'My Account', content: 'Tab content for my account' },\n  { name: 'Company', content: 'Tab content for company', disabled: true },\n  { name: 'Team Members', content: 'Tab content for team members' },\n  { name: 'Billing', content: 'Tab content for billing' },\n]\n\nexport default {\n  components: { SwitchGroup, Switch, SwitchLabel, TabGroup, TabList, Tab, TabPanels, TabPanel },\n  setup(props, context) {\n    let manual = ref(false)\n\n    return {\n      tabs: ref(tabs),\n      manual,\n      resolveSwitchClass({ checked }) {\n        return classNames(\n          'relative inline-flex shrink-0 h-6 transition-colors duration-200 ease-in-out border-2 border-transparent rounded-full cursor-pointer w-11 focus:outline-hidden focus:shadow-outline',\n          checked ? 'bg-indigo-600' : 'bg-gray-200'\n        )\n      },\n    }\n  },\n}\n</script>\n"
  },
  {
    "path": "playgrounds/vue/src/data.ts",
    "content": "export let countries = [\n  'Afghanistan',\n  'Albania',\n  'Algeria',\n  'American Samoa',\n  'Andorra',\n  'Angola',\n  'Anguilla',\n  'Antarctica',\n  'Antigua and Barbuda',\n  'Argentina',\n  'Armenia',\n  'Aruba',\n  'Australia',\n  'Austria',\n  'Azerbaijan',\n  'Bahamas (the)',\n  'Bahrain',\n  'Bangladesh',\n  'Barbados',\n  'Belarus',\n  'Belgium',\n  'Belize',\n  'Benin',\n  'Bermuda',\n  'Bhutan',\n  'Bolivia (Plurinational State of)',\n  'Bonaire, Sint Eustatius and Saba',\n  'Bosnia and Herzegovina',\n  'Botswana',\n  'Bouvet Island',\n  'Brazil',\n  'British Indian Ocean Territory (the)',\n  'Brunei Darussalam',\n  'Bulgaria',\n  'Burkina Faso',\n  'Burundi',\n  'Cabo Verde',\n  'Cambodia',\n  'Cameroon',\n  'Canada',\n  'Cayman Islands (the)',\n  'Central African Republic (the)',\n  'Chad',\n  'Chile',\n  'China',\n  'Christmas Island',\n  'Cocos (Keeling) Islands (the)',\n  'Colombia',\n  'Comoros (the)',\n  'Congo (the Democratic Republic of the)',\n  'Congo (the)',\n  'Cook Islands (the)',\n  'Costa Rica',\n  'Croatia',\n  'Cuba',\n  'Curaçao',\n  'Cyprus',\n  'Czechia',\n  \"Côte d'Ivoire\",\n  'Denmark',\n  'Djibouti',\n  'Dominica',\n  'Dominican Republic (the)',\n  'Ecuador',\n  'Egypt',\n  'El Salvador',\n  'Equatorial Guinea',\n  'Eritrea',\n  'Estonia',\n  'Eswatini',\n  'Ethiopia',\n  'Falkland Islands (the) [Malvinas]',\n  'Faroe Islands (the)',\n  'Fiji',\n  'Finland',\n  'France',\n  'French Guiana',\n  'French Polynesia',\n  'French Southern Territories (the)',\n  'Gabon',\n  'Gambia (the)',\n  'Georgia',\n  'Germany',\n  'Ghana',\n  'Gibraltar',\n  'Greece',\n  'Greenland',\n  'Grenada',\n  'Guadeloupe',\n  'Guam',\n  'Guatemala',\n  'Guernsey',\n  'Guinea',\n  'Guinea-Bissau',\n  'Guyana',\n  'Haiti',\n  'Heard Island and McDonald Islands',\n  'Holy See (the)',\n  'Honduras',\n  'Hong Kong',\n  'Hungary',\n  'Iceland',\n  'India',\n  'Indonesia',\n  'Iran (Islamic Republic of)',\n  'Iraq',\n  'Ireland',\n  'Isle of Man',\n  'Israel',\n  'Italy',\n  'Jamaica',\n  'Japan',\n  'Jersey',\n  'Jordan',\n  'Kazakhstan',\n  'Kenya',\n  'Kiribati',\n  \"Korea (the Democratic People's Republic of)\",\n  'Korea (the Republic of)',\n  'Kuwait',\n  'Kyrgyzstan',\n  \"Lao People's Democratic Republic (the)\",\n  'Latvia',\n  'Lebanon',\n  'Lesotho',\n  'Liberia',\n  'Libya',\n  'Liechtenstein',\n  'Lithuania',\n  'Luxembourg',\n  'Macao',\n  'Madagascar',\n  'Malawi',\n  'Malaysia',\n  'Maldives',\n  'Mali',\n  'Malta',\n  'Marshall Islands (the)',\n  'Martinique',\n  'Mauritania',\n  'Mauritius',\n  'Mayotte',\n  'Mexico',\n  'Micronesia (Federated States of)',\n  'Moldova (the Republic of)',\n  'Monaco',\n  'Mongolia',\n  'Montenegro',\n  'Montserrat',\n  'Morocco',\n  'Mozambique',\n  'Myanmar',\n  'Namibia',\n  'Nauru',\n  'Nepal',\n  'Netherlands (the)',\n  'New Caledonia',\n  'New Zealand',\n  'Nicaragua',\n  'Niger (the)',\n  'Nigeria',\n  'Niue',\n  'Norfolk Island',\n  'Northern Mariana Islands (the)',\n  'Norway',\n  'Oman',\n  'Pakistan',\n  'Palau',\n  'Palestine, State of',\n  'Panama',\n  'Papua New Guinea',\n  'Paraguay',\n  'Peru',\n  'Philippines (the)',\n  'Pitcairn',\n  'Poland',\n  'Portugal',\n  'Puerto Rico',\n  'Qatar',\n  'Republic of North Macedonia',\n  'Romania',\n  'Russian Federation (the)',\n  'Rwanda',\n  'Réunion',\n  'Saint Barthélemy',\n  'Saint Helena, Ascension and Tristan da Cunha',\n  'Saint Kitts and Nevis',\n  'Saint Lucia',\n  'Saint Martin (French part)',\n  'Saint Pierre and Miquelon',\n  'Saint Vincent and the Grenadines',\n  'Samoa',\n  'San Marino',\n  'Sao Tome and Principe',\n  'Saudi Arabia',\n  'Senegal',\n  'Serbia',\n  'Seychelles',\n  'Sierra Leone',\n  'Singapore',\n  'Sint Maarten (Dutch part)',\n  'Slovakia',\n  'Slovenia',\n  'Solomon Islands',\n  'Somalia',\n  'South Africa',\n  'South Georgia and the South Sandwich Islands',\n  'South Sudan',\n  'Spain',\n  'Sri Lanka',\n  'Sudan (the)',\n  'Suriname',\n  'Svalbard and Jan Mayen',\n  'Sweden',\n  'Switzerland',\n  'Syrian Arab Republic',\n  'Taiwan',\n  'Tajikistan',\n  'Tanzania, United Republic of',\n  'Thailand',\n  'Timor-Leste',\n  'Togo',\n  'Tokelau',\n  'Tonga',\n  'Trinidad and Tobago',\n  'Tunisia',\n  'Turkey',\n  'Turkmenistan',\n  'Turks and Caicos Islands (the)',\n  'Tuvalu',\n  'Uganda',\n  'Ukraine',\n  'United Arab Emirates (the)',\n  'United Kingdom of Great Britain and Northern Ireland (the)',\n  'United States Minor Outlying Islands (the)',\n  'United States of America (the)',\n  'Uruguay',\n  'Uzbekistan',\n  'Vanuatu',\n  'Venezuela (Bolivarian Republic of)',\n  'Viet Nam',\n  'Virgin Islands (British)',\n  'Virgin Islands (U.S.)',\n  'Wallis and Futuna',\n  'Western Sahara',\n  'Yemen',\n  'Zambia',\n  'Zimbabwe',\n  'Åland Islands',\n]\n\nexport let timezones: string[] = Intl.supportedValuesOf('timeZone')\n"
  },
  {
    "path": "playgrounds/vue/src/main.ts",
    "content": "import { createApp } from 'vue'\n// @ts-expect-error TODO: Properly handle this\nimport App from './App.vue'\nimport router from './router'\n\nimport './styles.css'\n\ncreateApp(App).use(router).mount('#app')\n"
  },
  {
    "path": "playgrounds/vue/src/playground-utils/hooks/use-popper.js",
    "content": "import { createPopper } from '@popperjs/core'\nimport { onMounted, ref, watchEffect } from 'vue'\n\nexport function usePopper(options) {\n  let reference = ref(null)\n  let popper = ref(null)\n\n  onMounted(() => {\n    watchEffect((onInvalidate) => {\n      if (!popper.value) return\n      if (!reference.value) return\n\n      let popperEl = popper.value.el || popper.value\n      let referenceEl = reference.value.el || reference.value\n\n      if (!(referenceEl instanceof HTMLElement)) return\n      if (!(popperEl instanceof HTMLElement)) return\n\n      let { destroy } = createPopper(referenceEl, popperEl, options)\n\n      onInvalidate(destroy)\n    })\n  })\n\n  return [reference, popper]\n}\n"
  },
  {
    "path": "playgrounds/vue/src/router.ts",
    "content": "import { createRouter, createWebHistory, RouterView } from 'vue-router'\n\ntype Component = import('vue').Component\n\nfunction buildRoutes() {\n  function titleCase(str) {\n    return str\n      .toLocaleLowerCase()\n      .split('-')\n      .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n      .join(' ')\n  }\n\n  // 1. Get all the components in the src/components directory\n  let files = Object.entries(\n    import.meta.glob('./components/**/*.vue', {\n      eager: true,\n      import: 'default',\n    })\n  ) as [string, Component][]\n\n  // 2.a. Swap the file names for route urls\n  // 2.b. Resolve the default import for each component\n  files = files.map(([file, component]) => [\n    file\n      .replace('./components/', '/')\n      .replace(/\\.vue$/, '')\n      .toLocaleLowerCase()\n      .replace(/^\\/home$/g, '/'),\n    component,\n  ])\n\n  let alreadyAdded = new Set()\n\n  // 3. Add a route for each directory (if not already added)\n  files = files.flatMap((entry) => {\n    let dirs = entry[0].split('/').slice(1, -1)\n\n    let paths: [string, Component][] = []\n\n    for (const [idx] of dirs.entries()) {\n      let path = `/` + dirs.slice(0, idx + 1).join('/')\n      if (alreadyAdded.has(path)) {\n        continue\n      }\n\n      paths.push([path, RouterView])\n      alreadyAdded.add(path)\n    }\n\n    return [...paths, entry]\n  })\n\n  // 4. Sort the routes alphabetically and by length\n  files.sort((a, b) => a[0].localeCompare(b[0]))\n\n  // 5. Create the nested routes\n  let routes = []\n  let routesByPath = {}\n\n  for (let [path, component] of files) {\n    let prefix = path.split('/').slice(0, -1).join('/')\n    let parent = routesByPath[prefix]?.children ?? routes\n    let route = {\n      path,\n      component: component,\n      children: [],\n      meta: {\n        name: titleCase(path.match(/[^/]+$/)?.[0] ?? 'Home'),\n        isRoot: parent === routes,\n      },\n    }\n\n    parent.push((routesByPath[path] = route))\n  }\n\n  return routes\n}\n\nexport default createRouter({\n  history: createWebHistory(),\n  routes: buildRoutes(),\n})\n"
  },
  {
    "path": "playgrounds/vue/src/styles.css",
    "content": "@import 'tailwindcss';\n@plugin '@tailwindcss/forms';\n@plugin '@tailwindcss/typography';\n@plugin '@headlessui/tailwindcss';\n\n/*\n  The default border color has changed to `currentcolor` in Tailwind CSS v4,\n  so we've added these compatibility styles to make sure everything still\n  looks the same as it did with Tailwind CSS v3.\n\n  If we ever want to remove these styles, we need to add an explicit border\n  color utility to any element that depends on these defaults.\n*/\n@layer base {\n  *,\n  ::after,\n  ::before,\n  ::backdrop,\n  ::file-selector-button {\n    border-color: var(--color-gray-200, currentcolor);\n  }\n}\n"
  },
  {
    "path": "playgrounds/vue/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"types\": [\"vite/client\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"incremental\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"downlevelIteration\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\"\n  },\n  \"include\": [\"**/*.ts\", \"**/*.tsx\", \"**/*.vue\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "playgrounds/vue/vercel.json",
    "content": "{\n  \"rewrites\": [{ \"source\": \"/(.*)\", \"destination\": \"/index.html\" }]\n}\n"
  },
  {
    "path": "playgrounds/vue/vite.config.js",
    "content": "import vue from '@vitejs/plugin-vue'\nimport { defineConfig } from 'vite'\n\nexport default defineConfig({\n  server: { port: 3000 },\n  plugins: [vue()],\n})\n"
  },
  {
    "path": "scripts/build.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nSCRIPT_DIR=$(cd ${0%/*} && pwd -P)\n\n# Make Next.js barrel file optimizations happy. Using `@swc-node/reigster` because we need to handle\n# the TypeScript files.\n# TODO: make this script run automatically.\n# node -r @swc-node/register \"${SCRIPT_DIR}/make-nextjs-happy.js\"\n\n# Known variables\nSRC='./src'\nDST='./dist'\nname=\"headlessui\"\ninput=\"./${SRC}/index.ts\"\n\n# Find executables\nresolver=\"${SCRIPT_DIR}/resolve-files.js\"\nrewriteImports=\"${SCRIPT_DIR}/rewrite-imports.js\"\n\n# Setup shared options for esbuild\nsharedOptions=()\nsharedOptions+=(\"--platform=browser\")\nsharedOptions+=(\"--target=es2019\")\n\n# Generate actual builds\n# ESM\nresolverOptions=()\nresolverOptions+=($SRC)\nresolverOptions+=('/**/*.{ts,tsx}')\nresolverOptions+=('--ignore=.test.,__mocks__')\nINPUT_FILES=$($resolver ${resolverOptions[@]})\n\nNODE_ENV=production  npx esbuild $INPUT_FILES --format=esm --outdir=$DST               --outbase=$SRC --minify --pure:React.createElement --define:process.env.TEST_BYPASS_TRACKED_POINTER=\"false\" --define:__DEV__=\"false\" ${sharedOptions[@]} &\nNODE_ENV=production  npx esbuild $input       --format=esm --outfile=$DST/$name.esm.js --outbase=$SRC --minify --pure:React.createElement --define:process.env.TEST_BYPASS_TRACKED_POINTER=\"false\" --define:__DEV__=\"false\" ${sharedOptions[@]} &\n\n# Common JS\nNODE_ENV=production  npx esbuild $input --format=cjs --outfile=$DST/$name.prod.cjs --minify --bundle --pure:React.createElement --define:process.env.TEST_BYPASS_TRACKED_POINTER=\"false\" --define:__DEV__=\"false\" ${sharedOptions[@]} $@ &\nNODE_ENV=development npx esbuild $input --format=cjs --outfile=$DST/$name.dev.cjs           --bundle --pure:React.createElement --define:process.env.TEST_BYPASS_TRACKED_POINTER=\"false\" --define:__DEV__=\"true\" ${sharedOptions[@]} $@ &\n\n# Generate ESM types\ntsc --emitDeclarationOnly --outDir $DST &\n\nwait\n\n# Generate CJS types\n# This is a bit of a hack, but it works because the same output works for both\ncp $DST/index.d.ts $DST/index.d.cts\n\n# Copy build files over\ncp -rf ./build/* $DST/\n\n# Wait for all the scripts to finish\nwait\n\n# Rewrite ESM imports 😤\n$rewriteImports \"$DST\" '/**/*.js'\n$rewriteImports \"$DST\" '/**/*.d.ts'\n\n# Remove test related files\nrm -rf `$resolver \"$DST\" '/**/*.{test,__mocks__,}.*'`\nrm -rf `$resolver \"$DST\" '/**/test-utils/*'`\n"
  },
  {
    "path": "scripts/lint.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nROOT_DIR=\"$(git rev-parse --show-toplevel)/\"\nTARGET_DIR=\"$(pwd)\"\nRELATIVE_TARGET_DIR=\"${TARGET_DIR/$ROOT_DIR/}\"\n\n# INFO: This script is always run from the root of the repository. If we execute this script from a\n# package then the filters (in this case a path to $RELATIVE_TARGET_DIR) will be applied.\n\npushd $ROOT_DIR > /dev/null\n\nprettierArgs=()\n\nif ! [ -z \"$CI\" ]; then\n  prettierArgs+=(\"--check\")\nelse\n  prettierArgs+=(\"--write\")\nfi\n\n# Add default arguments\nprettierArgs+=('--ignore-unknown')\n\n# Passthrough arguments and flags\nprettierArgs+=($@)\n\n# Ensure that a path is passed, otherwise default to the current directory\nif [ -z \"$@\" ]; then\n  prettierArgs+=(\"$RELATIVE_TARGET_DIR\")\nfi\n\n# Execute\nnpx prettier \"${prettierArgs[@]}\"\n\npopd > /dev/null\n"
  },
  {
    "path": "scripts/make-nextjs-happy.js",
    "content": "import fs from 'fs'\nimport path from 'path'\nimport prettier from 'prettier'\nimport * as HUI from '../packages/@headlessui-react/src/index.ts'\n\nlet customRemaps = {\n  tab: 'tabs',\n  radio: 'radio-group',\n  data: 'data-interactive',\n  focus: 'focus-trap',\n}\n\nlet components = Object.keys(HUI)\n\nasync function run() {\n  for (let component of components) {\n    let name = component.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()\n    let module = name.split('-')[0]\n    module = customRemaps[module] ?? module\n\n    let filePath = path.resolve(\n      __dirname,\n      '..',\n      'packages',\n      '@headlessui-react',\n      'src',\n      'components',\n      name,\n      `${name}.tsx`\n    )\n\n    // Main module path already exists\n    if (!fs.existsSync(filePath)) {\n      fs.mkdirSync(path.dirname(filePath), { recursive: true })\n      fs.writeFileSync(filePath, await template(component, module))\n    }\n  }\n}\n\nasync function template(name, module) {\n  return await prettier.format(\n    [\n      '// Next.js barrel file improvements (GENERATED FILE)',\n      `export type * from '../${module}/${module}';`,\n      `export { ${name} } from '../${module}/${module}';`,\n    ].join('\\n'),\n    { parser: 'typescript' }\n  )\n}\n\nrun()\n"
  },
  {
    "path": "scripts/package-path.js",
    "content": "// Given a version, figure out what the release notes are so that we can use this to pre-fill the\n// relase notes on a GitHub release for the current version.\n\nlet path = require('path')\nlet { execSync } = require('child_process')\n\nlet tag = process.argv[2] || execSync(`git describe --tags --abbrev=0`).toString().trim()\nlet pkgPath = path.resolve(\n  __dirname,\n  '..',\n  'packages',\n  tag.slice(0, tag.indexOf('@', 1)).replace('/', '-')\n)\n\nconsole.log('./' + path.relative(process.cwd(), pkgPath))\n"
  },
  {
    "path": "scripts/release-channel.js",
    "content": "let path = require('path')\nlet { execSync } = require('child_process')\n\n// Given a version, figure out what the release channel is so that we can publish to the correct\n// channel on npm.\n//\n// E.g.:\n//\n//   1.2.3                  -> latest (default)\n//   0.0.0-insiders.ffaa88  -> insiders\n//   4.1.0-alpha.4          -> alpha\n\nlet tag = process.argv[2] || execSync(`git describe --tags --abbrev=0`).toString().trim()\nlet pkgPath = path.resolve(\n  __dirname,\n  '..',\n  'packages',\n  tag.slice(0, tag.indexOf('@', 1)).replace('/', '-')\n)\n\nlet version = require(path.resolve(pkgPath, 'package.json')).version\n\nlet match = /\\d+\\.\\d+\\.\\d+-(.*)\\.\\d+/g.exec(version)\nif (match) {\n  // We want to release alpha to the next channel because it will be the next version\n  if (match[1] === 'alpha') match[1] = 'next'\n  console.log(match[1])\n} else {\n  console.log('latest')\n}\n"
  },
  {
    "path": "scripts/release-notes.js",
    "content": "// Given a version, figure out what the release notes are so that we can use this to pre-fill the\n// relase notes on a GitHub release for the current version.\n\nlet path = require('path')\nlet fs = require('fs')\nlet { execSync } = require('child_process')\n\nlet tag = process.argv[2] || execSync(`git describe --tags --abbrev=0`).toString().trim()\nlet pkgPath = path.resolve(\n  __dirname,\n  '..',\n  'packages',\n  tag.slice(0, tag.indexOf('@', 1)).replace('/', '-')\n)\n\nlet version = require(path.resolve(pkgPath, 'package.json')).version\n\nlet changelog = fs.readFileSync(path.resolve(pkgPath, 'CHANGELOG.md'), 'utf8')\nlet match = new RegExp(\n  `## \\\\[${version}\\\\] - (.*)\\\\n\\\\n([\\\\s\\\\S]*?)\\\\n(?:(?:##\\\\s)|(?:\\\\[))`,\n  'g'\n).exec(changelog)\n\nif (match) {\n  let [, , notes] = match\n  console.log(notes.trim())\n} else {\n  console.log(`Placeholder release notes for version: v${version}`)\n}\n"
  },
  {
    "path": "scripts/resolve-files.js",
    "content": "#!/usr/bin/env node\nlet fastGlob = require('fast-glob')\n\nlet parts = process.argv.slice(2)\nlet [args, flags] = parts.reduce(\n  ([args, flags], part) => {\n    if (part.startsWith('--')) {\n      flags[part.slice(2, part.indexOf('='))] = part.slice(part.indexOf('=') + 1)\n    } else {\n      args.push(part)\n    }\n    return [args, flags]\n  },\n  [[], {}]\n)\n\nflags.ignore = flags.ignore ?? ''\nflags.ignore = flags.ignore.split(',').filter(Boolean)\n\nconsole.log(\n  fastGlob\n    .sync(args.join(''))\n    .filter((file) => {\n      for (let ignore of flags.ignore) {\n        if (file.includes(ignore)) {\n          return false\n        }\n      }\n      return true\n    })\n    .join('\\n')\n)\n"
  },
  {
    "path": "scripts/rewrite-imports.js",
    "content": "#!/usr/bin/env node\n\nlet fs = require('fs')\nlet path = require('path')\nlet fastGlob = require('fast-glob')\n\nconsole.time('Rewrote imports in')\nfastGlob.sync([process.argv.slice(2).join('')]).forEach((file) => {\n  file = path.resolve(process.cwd(), file)\n  let content = fs.readFileSync(file, 'utf8')\n  let result = content.replace(/(import|export)([^\"']*?)([\"'])\\.(.*?)\\3/g, (full, a, b, _, d) => {\n    // For idempotency reasons, if `.js` already exists, then we can skip this. This allows us to\n    // run this script over and over again without adding .js files every time.\n    if (d.endsWith('.js')) {\n      return full\n    }\n\n    return `${a}${b}'.${d}.js'`\n  })\n  if (result !== content) {\n    fs.writeFileSync(file, result, 'utf8')\n  }\n})\nconsole.timeEnd('Rewrote imports in')\n"
  },
  {
    "path": "scripts/test.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\njestArgs=()\n\n# Add default arguments\njestArgs+=(\"--passWithNoTests\")\n\n# Add arguments based on environment variables\nif ! [ -z \"$CI\" ]; then\n  jestArgs+=(\"--maxWorkers=4\")\n  jestArgs+=(\"--ci\")\nfi\n\n# Passthrough arguments and flags\njestArgs+=($@)\n\n# Execute\nnpx jest \"${jestArgs[@]}\"\n\n"
  },
  {
    "path": "scripts/watch.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\n# Known variables\noutdir=\"./dist\"\nname=\"headlessui\"\ninput=\"./src/index.ts\"\n\n# Setup shared options for esbuild\nsharedOptions=()\nsharedOptions+=(\"--bundle\")\nsharedOptions+=(\"--platform=browser\")\nsharedOptions+=(\"--target=es2020\")\n\n\n# Generate actual builds\nNODE_ENV=development npx esbuild $input --format=esm  --outfile=$outdir/$name.esm.js --sourcemap ${sharedOptions[@]} $@ --watch\n\n"
  }
]