Full Code of variantjs/vue for AI

main 8f3acc04cc66 cached
165 files
603.0 KB
148.9k tokens
87 symbols
1 requests
Download .txt
Showing preview only (648K chars total). Download the full file or copy to clipboard to get everything.
Repository: variantjs/vue
Branch: main
Commit: 8f3acc04cc66
Files: 165
Total size: 603.0 KB

Directory structure:
gitextract_omw15f4s/

├── .eslintrc.json
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── yarn.yml
├── .gitignore
├── README.md
├── index.html
├── jest.config.js
├── package.json
├── postcss.config.js
├── src/
│   ├── __tests/
│   │   ├── components/
│   │   │   ├── TAlert.spec.ts
│   │   │   ├── TButton.spec.ts
│   │   │   ├── TCard.spec.ts
│   │   │   ├── TCheckbox.integration.spec.ts
│   │   │   ├── TCheckbox.spec.ts
│   │   │   ├── TDialog.spec.ts
│   │   │   ├── TDropdown.spec.ts
│   │   │   ├── TInput.integration.spec.ts
│   │   │   ├── TInput.spec.ts
│   │   │   ├── TInputGroup.spec.ts
│   │   │   ├── TModal.spec.ts
│   │   │   ├── TRadio.integration.spec.ts
│   │   │   ├── TRadio.spec.ts
│   │   │   ├── TRichSelect/
│   │   │   │   ├── RichSelectClearButton.spec.ts
│   │   │   │   ├── RichSelectDropdown.spec.ts
│   │   │   │   ├── RichSelectOption.spec.ts
│   │   │   │   ├── RichSelectOptionsList.spec.ts
│   │   │   │   ├── RichSelectSearchInput.spec.ts
│   │   │   │   ├── RichSelectState.spec.ts
│   │   │   │   ├── RichSelectTrigger.spec.ts
│   │   │   │   ├── RichSelectTriggerTags.spec.ts
│   │   │   │   └── RichSelectTriggerTagsTag.spec.ts
│   │   │   ├── TRichSelect.spec.ts
│   │   │   ├── TSelect/
│   │   │   │   └── TSelectOption.spec.ts
│   │   │   ├── TSelect.integration.spec.ts
│   │   │   ├── TSelect.spec.ts
│   │   │   ├── TTag.spec.ts
│   │   │   ├── TTextarea.integration.spec.ts
│   │   │   ├── TTextarea.spec.ts
│   │   │   ├── TToggle.spec.ts
│   │   │   ├── icons/
│   │   │   │   └── CustomIcon.spec.ts
│   │   │   └── misc/
│   │   │       ├── TextPlaceholder.spec.ts
│   │   │       └── Transitionable.spec.ts
│   │   ├── index.spec.ts
│   │   ├── plugin.spec.ts
│   │   ├── testUtils.ts
│   │   ├── use/
│   │   │   ├── useActivableOption.spec.ts
│   │   │   ├── useConfiguration.spec.ts
│   │   │   ├── useConfigurationWithClassesList.spec.ts
│   │   │   ├── useFetchsOptions.spec.ts
│   │   │   ├── useInjectsClassesList.spec.ts
│   │   │   ├── useInjectsConfiguration.spec.ts
│   │   │   ├── useMulipleableVModel.spec.ts
│   │   │   ├── useMultioptions.spec.ts
│   │   │   ├── useSelectableOption.spec.ts
│   │   │   ├── useSetup.ts
│   │   │   └── useVModel.spec.ts
│   │   └── utils/
│   │       ├── createDialogProgramatically.spec.ts
│   │       ├── emitter.spec.ts
│   │       ├── getVariantProps.spec.ts
│   │       ├── popper.spec.ts
│   │       └── svgToVueComponent.spec.ts
│   ├── assets/
│   │   └── tailwind.css
│   ├── components/
│   │   ├── TAlert.vue
│   │   ├── TButton.vue
│   │   ├── TCard.vue
│   │   ├── TCheckbox.vue
│   │   ├── TDialog.vue
│   │   ├── TDropdown.vue
│   │   ├── TInput.vue
│   │   ├── TInputGroup.vue
│   │   ├── TModal.vue
│   │   ├── TRadio.vue
│   │   ├── TRichSelect/
│   │   │   ├── RichSelectClearButton.vue
│   │   │   ├── RichSelectDropdown.vue
│   │   │   ├── RichSelectOption.vue
│   │   │   ├── RichSelectOptionsList.vue
│   │   │   ├── RichSelectSearchInput.vue
│   │   │   ├── RichSelectState.vue
│   │   │   ├── RichSelectTrigger.vue
│   │   │   ├── RichSelectTriggerTags.vue
│   │   │   └── RichSelectTriggerTagsTag.vue
│   │   ├── TRichSelect.vue
│   │   ├── TSelect/
│   │   │   └── TSelectOption.vue
│   │   ├── TSelect.vue
│   │   ├── TTag.vue
│   │   ├── TTextarea.vue
│   │   ├── TToggle.vue
│   │   └── misc/
│   │       ├── TextPlaceholder.vue
│   │       └── Transitionable.vue
│   ├── development/
│   │   ├── About.vue
│   │   ├── Alert.vue
│   │   ├── App.vue
│   │   ├── AppMenu.vue
│   │   ├── Attributes.vue
│   │   ├── Check.vue
│   │   ├── Dialog.vue
│   │   ├── Dropdown.vue
│   │   ├── Home.vue
│   │   ├── Modal.vue
│   │   ├── Multioptions.vue
│   │   ├── Options.vue
│   │   ├── RichSelect.vue
│   │   ├── TSubmit.vue
│   │   ├── Theme.vue
│   │   └── router.ts
│   ├── icons/
│   │   ├── CheckCircleIcon.vue
│   │   ├── CheckmarkIcon.vue
│   │   ├── CloseIcon.vue
│   │   ├── CrossCircleIcon.vue
│   │   ├── CustomIcon.vue
│   │   ├── ExclamationIcon.vue
│   │   ├── InformationCircleIcon.vue
│   │   ├── LoadingIcon.vue
│   │   ├── QuestionMarkCircleIcon.vue
│   │   ├── SelectorIcon.vue
│   │   ├── SolidCheckCircleIcon.vue
│   │   ├── SolidCrossCircleIcon.vue
│   │   ├── SolidExclamationIcon.vue
│   │   ├── SolidInformationCircleIcon.vue
│   │   └── SolidQuestionMarkCircleIcon.vue
│   ├── index.ts
│   ├── main.ts
│   ├── plugin.ts
│   ├── shims-vue.d.ts
│   ├── types/
│   │   ├── components/
│   │   │   ├── t-alert.ts
│   │   │   ├── t-button.ts
│   │   │   ├── t-card.ts
│   │   │   ├── t-checkbox.ts
│   │   │   ├── t-dialog.ts
│   │   │   ├── t-dropdown.ts
│   │   │   ├── t-input-group.ts
│   │   │   ├── t-input.ts
│   │   │   ├── t-modal.ts
│   │   │   ├── t-radio.ts
│   │   │   ├── t-rich-select.ts
│   │   │   ├── t-select.ts
│   │   │   ├── t-tag.ts
│   │   │   ├── t-textarea.ts
│   │   │   └── t-toggle.ts
│   │   ├── helpers.ts
│   │   ├── index.ts
│   │   ├── misc.ts
│   │   ├── utils.ts
│   │   ├── variantCore.ts
│   │   └── vueRouter.ts
│   ├── use/
│   │   ├── useActivableOption.ts
│   │   ├── useConfiguration.ts
│   │   ├── useConfigurationWithClassesList.ts
│   │   ├── useFetchsOptions.ts
│   │   ├── useInjectsClassesList.ts
│   │   ├── useInjectsClassesListClass.ts
│   │   ├── useInjectsConfiguration.ts
│   │   ├── useMulipleableVModel.ts
│   │   ├── useMultioptions.ts
│   │   ├── useSelectableOption.ts
│   │   └── useVModel.ts
│   └── utils/
│       ├── createDialogProgramatically.ts
│       ├── emitter.ts
│       ├── getVariantProps.ts
│       ├── popper.ts
│       └── svgToVueComponent.ts
├── tailwind.config.js
├── tsconfig.json
├── vite.config.ts
└── vite.demo.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.json
================================================
{
  "env": {
      "browser": true,
      "es6": true
  },
  "extends": [
    "plugin:vue/vue3-recommended",
    "plugin:@typescript-eslint/recommended",
    "airbnb-typescript/base"
  ],
  "parser": "vue-eslint-parser",
  "parserOptions": {
      "ecmaVersion": 2018,
      "parser": "@typescript-eslint/parser",
      "sourceType": "module",
      "project": "tsconfig.json",
      "tsconfigRootDir": "./",
      "extraFileExtensions": [ ".vue" ]
  },
  "plugins": [
    "vue",
    "@typescript-eslint",
    "tree-shaking"
  ],
  "rules": {
    "max-len": "off",
    "import/extensions": "off",
    "import/prefer-default-export": "off",
    "import/no-extraneous-dependencies": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    "tree-shaking/no-side-effects-in-initialization": 2
  },
  "overrides": [
    {
      "files": ["*.spec.ts", "src/main.ts", "src/development/*"],
      "rules": {
        "tree-shaking/no-side-effects-in-initialization": "off"
      }
    }
  ]
}



================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: variantjs
custom: https://www.buymeacoffee.com/alfonsobries


================================================
FILE: .github/workflows/yarn.yml
================================================
name: CI
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Install modules
      run: yarn
    - name: Run tests
      run: yarn test --coverage

================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
yarn-error.log

================================================
FILE: README.md
================================================
# VariantJS/Vue (PREVIEW)


![CI](https://github.com/variantjs/vue/workflows/CI/badge.svg) [![CI](https://github.com/variantjs/vue/actions/workflows/yarn.yml/badge.svg?event=release)](https://github.com/variantjs/vue/actions/workflows/yarn.yml)


- Consider that this is a preview and all components are subject to change until I release the final version.
- If you find any error, typo or have comments feel free to create [an issue](https://github.com/variantjs/vue/issues) or open a PR.
- Also a friendly reminder to consider [sponsor this project](https://github.com/sponsors/variantjs) there is a lot of work behind, **a lot**.
- You can track the progress (or even see what tasks are pending if you want to contribute) [here](https://github.com/variantjs/vue/projects/1).
- Demo: [https://variantjs.netlify.app](https://variantjs.netlify.app)
  

## Whats new?

Main changes against VueTailwind: 

- Smaller bundle
- Better Typescript integration
- 100% test coverage
- Built from scratch with Vue 3 + Vite
- Simpler code and back to use template syntax

In general, initial components will be pretty much the same, but some components have new settings. As an example, TDropDown and TRichSelect now have a `teleport` and `teleportTo` prop that allows you to move the content of the dropdown to any place on the DOM by using the new teleport setting from Vue. Also, both now use PopperJS, meaning you can have more control over the dropdown position.

Another note is that the default theme for some components changed a bit. You can check the new default configuration (theme) in the [@variantjs/core](https://github.com/variantjs/core/blob/main/src/config) source code. [Example for the TRichSelect component.](https://github.com/variantjs/core/blob/main/src/config/TRichSelectConfig.ts)

Everything will be documented, of course, but meanwhile, you can check the source code of the components that is way cleaner and easier to understand.

*Components that are ready to be tested*

- [x] TInput
- [x] TButton
- [x] TTextarea
- [x] TSelect
- [x] TCheckbox
- [x] TRadio
- [x] TAlert
- [x] TCard
- [x] TDropdown
- [x] TInputGroup
- [x] TRichSelect
- [x] TTag
- [x] TModal (^0.0.5)
- [x] TDialog (^0.0.6)
- [x] TToggle (^0.0.9)


*Next ones:*

- [ ] TDatepicker
- [ ] TRadioGroup
- [ ] TCheckboxGroup
- [ ] TTable
- [ ] TPagination
- [ ] More to announce soon...


**VarianJS/Vue** is the next version of the [VueTailwind](https://github.com/alfonsobries/vue-tailwind) package built from scratch for Vue 3.

It includes a set of Vue components created to be customized to adapt to your application's unique design and some other useful function that will help you to make your own components.

### Another UI library?

Most component libraries come with:

- A predefined design that is not easy to adapt to your application (in the case it is possible)
- A big chunk of CSS files that may conflict with your project and increase the size of your build
- Some are used by hundreds (or even millions of projects) that end with the same design.

Those libraries are great and make our work easy, but hey, we made a beautiful custom design, right?
### So what are the alternatives?

We can create our own CSS framework or use something like [TailwindCSS](https://tailwindcss.com) to define our style. The problem?:
We need to repeat long CSS classes over and over
Some components like modals, date pickers, etc., are tricky.
We love to be productive.

### Best of both worlds

The **VariantJS** components are meant to be customized with custom CSS classes that you can define when you install the library.

Plus, most component settings are configurable, so using this library is like having your personal set of components for your particular needs.

All that means that with this library, you will be able to:

- Define your components look and feel by defining custom default CSS classes.
- Add unlimited variants for every specific use case.
- Override the default value of the props according to your needs.
- Create different versions of one component with different default settings.

## Installation

### 1. Install the dependencies 

```console
npm install @variantjs/vue @variantjs/core @popperjs/core body-scroll-lock --save
``` 

Or: 

```console
yarn add @variantjs/vue @variantjs/core @popperjs/core body-scroll-lock
``` 

Notes: 

* `@popperjs/core` is only need if you use the `TRichSelect` or `TDropdown` component.
* `body-scroll-lock` is only need if you use the `TModal` component and the incoming `TDialog` component.


## 2. Install TailwindCSS (Optional)

This library uses TailwindCSS classes by default. Still, it should work with any CSS framework since all the CSS classes are configurable.

To install TailwindCSS follow his official documentation: [https://tailwindcss.com/docs/installation](https://tailwindcss.com/docs/installation)

#### 2.1 Add the @tailwindcss/forms plugin

The default theme of this library depends on the `@tailwindcss/forms` plugin. To use it, follow the steps on the plugin source page.
[https://github.com/tailwindlabs/tailwindcss-forms](https://github.com/tailwindlabs/tailwindcss-forms)

#### 2.1 Add variants for disabled pseudo-class

Also needed for the default theme and strongly recommended since it adds the ability to use some classes like `disabled:opacity-50 disabled:cursor-not-allowed` to disabled inputs.

See [https://tailwindcss.com/docs/configuring-variants](https://tailwindcss.com/docs/configuring-variants) on the TailwindCSS docs for more info.

As a reference, your `tailwind.config.js` may look like this:

```js
module.exports = {
  variants: {
    extend: {
      opacity: ['disabled'],
      cursor: ['disabled'],
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
  ],
};
```

## 3. Configure Vue to use VariantJS

```js
import { createApp } from 'vue'
import App from './App.vue'
import { variantJS } from '@variantjs/vue'

const app = createApp(App)

const configuration = {
  //...
}

app.use(variantJS, configuration)

app.mount('#app')

```

You can also use `typescript` for type checking:

```ts
import { createApp } from 'vue'
import App from './App.vue'
import { variantJS, VariantJSConfiguration } from '@variantjs/vue'

const app = createApp(App)

const configuration: VariantJSConfiguration = {
  //...
}

app.use(variantJS, configuration)

app.mount('#app')

```

### 4. Configure your components

Consider this:

1. You can override the default value of all the props or even add any custom attributes.
2. The format of the configuration is the following:
```js
// Inside the main file 

const configuration = {
  {ComponentName}: {
    {propsOrAttribute}: {newDefautlValue}
  },
  {ComponentName2}: {
    {propsOrAttribute}: {newDefautlValue}
  },
  // ...
}

app.use(variantJS, configuration)
// ...
```
3. The official documentation for this package is stil a WIP. You can use the the [VueTailwind Docs](https://www.vue-tailwind.com/) as reference but consider that some props were removed or updated, you can also see the source code of the components for more information.

### 5. Import the components you need

```vue
<template>
  <t-input v-model="val">
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { TInput } from '@variantjs/vue'

export default defineComponent({
  components: {
    TInput,
  },
  data() {
    return {
      val: ''
    }
  }
});
</script>

```

## Theming

To apply a custom theme you should play with the `classes`, `fixedClasses`, and `variants` props.

The `classes` and `fixedClasses` props usually expects an `string` with a CSS class for single-tag components (inputs, button, etc.) and an `object` for more complex components (modals, datepicker, etc) (see component docs for details).

The `variants` props expects an object where every key represents the variant name and every value the classes that will be used when that variant is applied.

#### Example for a single-tag component: 

```js
const configuration = {
  TButton: {
    // The fixed classes will never change and will be merged with the `classes` value or the active variant
    fixedClasses: 'focus:outline-none focus:shadow-outline inline-flex items-center transition ease-in-out duration-150',
    // Classes used when any variant is active
    classes: 'text-white bg-blue-600 hover:bg-blue-500 focus:border-blue-700 active:bg-blue-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
    variants: {
      // A red variant of the button (applied when `<t-button variant="error" />`)
      error: {
        classes: 'text-white bg-red-600 hover:bg-red-500 focus:border-red-700 active:bg-red-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
      },
      // A green variant of the button (applied when `<t-button variant="success" />`)
      success: {
        classes: 'text-white bg-green-600 hover:bg-green-500 focus:border-green-700 active:bg-green-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
      },
      // ...unlimited variants
    }
    // ...More settings
  },
}
```

#### Example for a complex component: 

```js
const configuration = {
  TAlert: {
    // The fixed classes will never change and will be merged with the `classes` value or the active variant
    fixedClasses: {
      wrapper: 'rounded p-4 flex text-sm border-l-4',
      body: 'flex-grow',
      close: 'ml-4 rounded',
      closeIcon: 'h-5 w-5 fill-current'
    },
    classes: {
      wrapper: 'bg-blue-100 border-blue-500',
      body: 'text-blue-700',
      close: 'text-blue-700 hover:text-blue-500 hover:bg-blue-200',
      closeIcon: 'h-5 w-5 fill-current'
    },
    variants: {
      danger: {
        classes: {
          wrapper: 'bg-red-100 border-red-500',
          body: 'text-red-700',
          close: 'text-red-700 hover:text-red-500 hover:bg-red-200'
          // Notice that I am not defining the `closeIcon` class since we only
          // need to write the classes we want to override
        },
      },
    },
  },
}
```

Will continue...


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>


================================================
FILE: jest.config.js
================================================
module.exports = {
  testEnvironment: 'jsdom',
  preset: '@vue/cli-plugin-unit-jest/presets/typescript',
  collectCoverageFrom: [
    "src/**/*.{vue,ts}",
    "!**/node_modules/**",
    "!**/*.d.ts"
  ],
  coverageReporters: ["text", "json", "html"],
  testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ],
  coveragePathIgnorePatterns: [
    "/node_modules/",
    "/src/main.ts",
    "/src/App.vue",
    "/src/development/",
  ]
}


================================================
FILE: package.json
================================================
{
  "name": "@variantjs/vue",
  "version": "0.0.22",
  "description": "Vue VariantJS: Fully configurable Vue 3 components styled with TailwindCSS",
  "files": [
    "dist/**/*",
    "src/**/*"
  ],
  "types": "./dist/index.d.ts",
  "main": "./dist/index.umd.js",
  "module": "./dist/index.es.js",
  "exports": {
    ".": {
      "import": "./dist/index.es.js",
      "require": "./dist/index.umd.js"
    }
  },
  "sideEffects": false,
  "keywords": [
    "tailwindcss",
    "vue",
    "vue-tailwind",
    "variantjs",
    "vue3"
  ],
  "author": "Alfonso Bribiesca <alfonso@vexilo.com>",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/variantjs/vue"
  },
  "license": "MIT",
  "private": false,
  "scripts": {
    "build": "vue-tsc --noEmit && vite build",
    "serve": "vite preview",
    "test": "jest -t",
    "test:watch": "jest --watch -t",
    "lint": "eslint src --fix",
    "release": "release-it",
    "demo": "vite --config ./vite.demo.config.ts",
    "demo:build": "vite build --config ./vite.demo.config.ts",
    "demo:serve": "vite preview --config ./vite.demo.config.ts"
  },
  "devDependencies": {
    "@popperjs/core": "^2.11.0",
    "@rollup/plugin-typescript": "^8.3.0",
    "@tailwindcss/forms": "^0.4.0",
    "@testing-library/vue": "^6.4.2",
    "@types/body-scroll-lock": "^3.1.0",
    "@types/jest": "^27.0.3",
    "@typescript-eslint/eslint-plugin": "^5.8.0",
    "@typescript-eslint/parser": "^5.8.0",
    "@vitejs/plugin-vue": "^2.0.1",
    "@vue/cli-plugin-unit-jest": "^5.0.0-rc.1",
    "@vue/compiler-sfc": "^3.2.26",
    "@vue/test-utils": "^2.0.0-rc.6",
    "@vue/vue3-jest": "^27.0.0-alpha.4",
    "autoprefixer": "^10.4.0",
    "body-scroll-lock": "^4.0.0-beta.0",
    "eslint": "^8.5.0",
    "eslint-config-airbnb-typescript": "^16.1.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-import": "^2.25.3",
    "eslint-plugin-tree-shaking": "^1.9.2",
    "eslint-plugin-vue": "^8.2.0",
    "jest": "^27.4.5",
    "prettier": "^2.5.1",
    "release-it": "^14.11.8",
    "tailwindcss": "^3.0.7",
    "ts-jest": "^27.1.2",
    "ts-vue-plugin": "^0.1.3",
    "typescript": "^4.5.4",
    "vite": "^2.7.7",
    "vue": "^3.2.6",
    "vue-loader": "^16.7.0",
    "vue-router": "4",
    "vue-tsc": "^0.30.1",
    "vue3-jest": "^27.0.0-alpha.1"
  },
  "peerDependencies": {
    "@popperjs/core": "^2.11.0",
    "body-scroll-lock": "^4.0.0-beta.0",
    "vue": "^3.2.6"
  },
  "release-it": {
    "hooks": {
      "before:init": [
        "yarn lint",
        "yarn test"
      ],
      "after:bump": "yarn build"
    }
  },
  "dependencies": {
    "@variantjs/core": "^0.0.79"
  }
}


================================================
FILE: postcss.config.js
================================================
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}


================================================
FILE: src/__tests/components/TAlert.spec.ts
================================================
import { mount } from '@vue/test-utils';
import TAlert from '@/components/TAlert.vue';
import { scopedParamsAsString, parseScopedParams } from '../testUtils';

describe('TAlert.vue', () => {
  it('renders the component without errors', () => {
    const wrapper = mount(TAlert, {
      slots: {
        default: 'Hello World!',
      },
    });

    expect(wrapper.vm.$el.textContent).toEqual('Hello World!');
    expect(wrapper.vm.$refs.wrapper).toBeTruthy();
    expect(wrapper.vm.$refs.body).toBeTruthy();
    expect(wrapper.vm.$refs.close).toBeTruthy();
    expect(wrapper.vm.$refs.closeIcon).toBeTruthy();
  });

  it('renders the `text` prop on the alert body', () => {
    const wrapper = mount(TAlert, {
      props: {
        text: 'Hello World!',
      },
    });

    expect(wrapper.vm.$el.textContent).toEqual('Hello World!');
  });

  it('prioritized the slot over the `text` prop', () => {
    const wrapper = mount(TAlert, {
      props: {
        text: 'Goodbye World!',
      },
      slots: {
        default: 'Hello World!',
      },
    });

    expect(wrapper.vm.$el.textContent).toEqual('Hello World!');
  });

  it('accepts a custom `tagName` for the alert wrapper', () => {
    const wrapper = mount(TAlert, {
      props: {
        tagName: 'fieldset',
      },
    });

    expect(wrapper.vm.$refs.wrapper.tagName).toBe('FIELDSET');
  });

  it('accepts a custom `bodyTagName` for the alert wrapper', () => {
    const wrapper = mount(TAlert, {
      props: {
        bodyTagName: 'fieldset',
      },
    });

    expect(wrapper.vm.$refs.body.tagName).toBe('FIELDSET');
  });

  it('hides the closeButton if `dismissible` is set to `false`', () => {
    const wrapper = mount(TAlert, {
      props: {
        dismissible: false,
      },
    });

    expect(wrapper.vm.$refs.close).toBeUndefined();
  });

  it('hides the component when the close button is pressed ', async () => {
    const wrapper = mount(TAlert);

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('starts with the alert hidden if `show` if set to `false`', () => {
    const wrapper = mount(TAlert, {
      props: {
        show: false,
      },
    });

    expect(wrapper.vm.shown).toBe(false);
  });

  it('emits `update:show` when show property is updated', async () => {
    const wrapper = mount(TAlert);

    wrapper.vm.doHide();

    await wrapper.vm.$nextTick();

    // assert event has been emitted
    expect(wrapper.emitted()['update:show']).toBeTruthy();

    // assert event payload
    expect(wrapper.emitted()['update:show']).toEqual([[false]]);

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();

    // assert event has been emitted
    expect(wrapper.emitted()['update:show']).toBeTruthy();

    // assert event payload
    expect(wrapper.emitted()['update:show']).toEqual([[false], [true]]);
  });

  it('hides the element after the `timeout`', async () => {
    jest.useFakeTimers();

    const wrapper = mount(TAlert, {
      props: {
        timeout: 500,
      },
    });

    jest.advanceTimersByTime(499);

    expect(wrapper.vm.shown).toBe(true);

    jest.advanceTimersByTime(1);

    expect(wrapper.vm.shown).toBe(false);

    jest.useRealTimers();
  });

  it('doesnt clear the timeout if not defined', async () => {
    jest.useFakeTimers();

    const timeoutSpy = jest.spyOn(window, 'setTimeout');
    const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout');

    const wrapper = mount(TAlert);

    wrapper.unmount();

    expect(timeoutSpy).not.toHaveBeenCalled();
    expect(clearTimeoutSpy).not.toHaveBeenCalled();

    clearTimeoutSpy.mockRestore();
    timeoutSpy.mockRestore();
  });

  it('clears the `timeout` when the element is unmounted', async () => {
    jest.useFakeTimers();

    const wrapper = mount(TAlert, {
      props: {
        timeout: 500,
      },
    });

    const hideAction = jest.spyOn(wrapper.vm, 'doHide');

    jest.advanceTimersByTime(499);

    wrapper.unmount();

    jest.advanceTimersByTime(1);

    expect(hideAction).not.toHaveBeenCalled();

    jest.useRealTimers();
  });

  it('clears the `timeout` when the element is hidden manually', async () => {
    const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout');

    const wrapper = mount(TAlert, {
      props: {
        timeout: 500,
      },
    });

    wrapper.vm.doHide();

    expect(clearTimeoutSpy).toHaveBeenCalled();

    clearTimeoutSpy.mockRestore();
  });

  it('accepts a custom `closeIcon``', async () => {
    const closeIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M12.707" clip-rule="evenodd"></path>
  </svg>`;
    const wrapper = mount(TAlert, {
      props: {
        closeIcon,
      },
    });

    expect(wrapper.vm.$refs.close.innerHTML).toContain('<path fill-rule="evenodd" d="M12.707" clip-rule="evenodd"');
  });

  it('has a closeButton slot', async () => {
    // Supress "Property undefined was accessed during render but is not defined on instance." warning
    // @TODO consider an alternative to this
    jest.spyOn(console, 'warn').mockImplementation(() => {});

    const closeButton = '<button><span>x</span></button>';

    const wrapper = mount(TAlert, {
      slots: {
        closeButton,
      },
    });

    expect(wrapper.html()).toContain(closeButton);
  });

  it('exposes the `toggle`, `show` and `hide` methods and the configuration to the closeButton slot ', () => {
    const wrapper = mount(TAlert, {
      slots: {
        closeButton: (params) => scopedParamsAsString(params),
      },
    });

    const scopeParamKeys = parseScopedParams(wrapper.text());

    expect(scopeParamKeys).toEqual({
      show: 'function',
      hide: 'function',
      toggle: 'function',
      configuration: 'object',
    });
  });

  it('exposes the `toggle`, `show` and `hide` methods and the configuration to the default slot ', () => {
    const wrapper = mount(TAlert, {
      slots: {
        default: (params) => scopedParamsAsString(params),
      },
    });

    const scopeParamKeys = parseScopedParams(wrapper.text());

    expect(scopeParamKeys).toEqual({
      show: 'function',
      hide: 'function',
      toggle: 'function',
      configuration: 'object',
    });
  });

  it('syncs the `shown` value with the prop configuration', async () => {
    const wrapper = mount(TAlert);

    await wrapper.setProps({
      show: false,
    });

    expect(wrapper.vm.shown).toBe(false);

    await wrapper.setProps({
      show: true,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides or shows the component with the doToggle method', () => {
    const wrapper = mount(TAlert);

    wrapper.vm.doToggle();

    expect(wrapper.vm.shown).toBe(false);

    wrapper.vm.doToggle();

    expect(wrapper.vm.shown).toBe(true);
  });
});


================================================
FILE: src/__tests/components/TButton.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import { shallowMount } from '@vue/test-utils';
import { TButtonConfig } from '@variantjs/core';
import TButton from '../../components/TButton.vue';

describe('TButton.vue', () => {
  it('renders the button', () => {
    const wrapper = shallowMount(TButton);
    expect(wrapper.get('button')).toBeTruthy();
  });

  it('renders the button with a default set of classes', () => {
    const wrapper = shallowMount(TButton);

    expect(wrapper.html()).toBe(`<button class="${TButtonConfig.classes}"></button>`);
  });

  it('renders the button without attributes if no default theme', () => {
    const wrapper = shallowMount(TButton, {
      global: {
        provide: {
          configuration: {
            TButton: {
              classes: undefined,
            },
          },
        },
      },
    });

    expect(wrapper.html()).toBe('<button></button>');
  });

  it('renders the button with the text in the slot', () => {
    const wrapper = shallowMount(TButton, {
      props: {
        classes: undefined,
      },
      slots: {
        default: 'Press me!',
      },
    });

    expect(wrapper.html()).toBe('<button>Press me!</button>');
  });

  it('set the props.value as the button value', () => {
    const value = 'button value';
    const wrapper = shallowMount(TButton, {
      props: { value },
    });

    expect(wrapper.vm.$el.value).toBe(value);
  });

  it('doesnt add the tagName as attribute', () => {
    const wrapper = shallowMount(TButton, {
      props: { tagName: 'a' },
    });

    expect(wrapper.vm.$el.attributes.tagName).toBeUndefined();
  });

  it('disables the button', async () => {
    const wrapper = shallowMount(TButton, {
      props: { disabled: false },
    });
    expect(wrapper.vm.$el.disabled).toBe(false);

    await wrapper.setProps({ disabled: true });

    expect(wrapper.vm.$el.disabled).toBe(true);
  });

  it('adds the configuration attribute', async () => {
    const wrapper = shallowMount(TButton, {
      global: {
        provide: {
          configuration: {
            TButton: {
              type: 'button',
              'data-id': 'something',
            },
          },
        },
      },
    });

    const button = wrapper.vm.$el as HTMLButtonElement;
    expect(button.type).toBe('button');
    expect(button.dataset.id).toBe('something');
  });

  it('prioritizes the attribute over the configuratiion', async () => {
    const wrapper = shallowMount(TButton, {
      global: {
        provide: {
          configuration: {
            TButton: {
              type: 'button',
            },
          },
        },
      },
      attrs: {
        type: 'submit',
      },
    });

    const button = wrapper.vm.$el;
    expect(button.type).toBe('submit');
  });

  it('accepts misc button attributes', async () => {
    const wrapper = shallowMount(TButton);

    const values = {
      id: {
        default: '',
        new: 'new-id',
      },
      autofocus: {
        default: false,
        new: true,
      },
      disabled: {
        default: false,
        new: true,
      },
      name: {
        default: '',
        new: 'new-name',
      },
      title: {
        default: '',
        new: 'new-title',
      },
      type: {
        default: 'submit',
        new: 'button',
      },
    };

    const newProps: any = {};
    // Check for the default values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.default);

      newProps[key as any] = elementValue.new;
    });

    await wrapper.setProps(newProps);

    // Check for the new values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.new);
    });
  });

  it('emits native button events', () => {
    const onClick = jest.fn();
    const onBlur = jest.fn();
    const onFocus = jest.fn();
    const onKeyup = jest.fn();

    const wrapper = shallowMount(TButton, {
      attrs: {
        onClick,
        onBlur,
        onFocus,
        onKeyup,
      },
    });

    const button = wrapper.vm.$el;

    button.dispatchEvent(new MouseEvent('click'));
    expect(onClick).toHaveBeenCalled();

    button.dispatchEvent(new FocusEvent('focus'));
    expect(onFocus).toHaveBeenCalled();

    button.dispatchEvent(new FocusEvent('blur'));
    expect(onBlur).toHaveBeenCalled();

    button.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' }));
    expect(onKeyup).toHaveBeenCalled();
  });

  it('has native button methods', () => {
    const wrapper = shallowMount(TButton);

    const button = wrapper.vm.$el;

    expect(typeof button.click).toBe('function');
    expect(typeof button.focus).toBe('function');
    expect(typeof button.blur).toBe('function');
  });

  it('triggers custom events', async () => {
    const onCustom = jest.fn();

    const wrapper = shallowMount(TButton, {
      attrs: {
        onCustom,
      },
    });
    const button = wrapper.vm.$el as HTMLButtonElement;

    const evt = new CustomEvent('custom', { detail: 'my-custom-event' });
    button.dispatchEvent(evt);

    expect(onCustom).toHaveBeenCalled();
  });

  it('default to button tag', () => {
    const wrapper = shallowMount(TButton);

    expect(wrapper.vm.$el.tagName).toBe('BUTTON');
  });

  it('accepts anchor tag', () => {
    const wrapper = shallowMount(TButton, {
      props: { tagName: 'a' },
    });

    expect(wrapper.vm.$el.tagName).toBe('A');
  });

  it('uses anchor tag when has href attribute', () => {
    const wrapper = shallowMount(TButton, {
      props: { href: 'https://www.vexilo.com/' },
    });

    expect(wrapper.vm.$el.tagName).toBe('A');
    expect(wrapper.vm.$el.href).toBe('https://www.vexilo.com/');
  });

  describe('Router Link', () => {
    it('has router link related props', () => {
      const props = {
        to: '/something',
        replace: true,
        activeClass: 'activeClass',
        exactActiveClass: 'exactActiveClass',
        custom: true,
        ariaCurrentValue: 'location',
      };

      type RouterProps = typeof props;

      const wrapper = shallowMount(TButton, {
        props: props as any,
      });

      Object.keys(props).forEach((key) => {
        expect((wrapper.vm as any)[key]).toBe(props[key as keyof RouterProps]);
      });
    });

    it('doesnt have a router link component by default', () => {
      const wrapper = shallowMount(TButton);
      expect(wrapper.vm.routerLinkComponentAvailable).toBe(false);
    });

    it('can determine when a router link component is available', () => {
      const RouterLink = {
        name: 'RouterLink',
        template: '',
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            RouterLink,
          },
        },
      });

      expect(wrapper.vm.routerLinkComponentAvailable).toBe(true);
    });

    it('determine that a router link component is available if has a NuxtLink', () => {
      const NuxtLink = {
        name: 'NuxtLink',
        template: '',
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            NuxtLink,
          },
        },
      });

      (wrapper.vm.$options.components as any).NuxtLink = {};
      expect(wrapper.vm.routerLinkComponentAvailable).toBe(true);
    });

    it('uses a router-link when `to` prop is defined and the route link component is available', () => {
      const RouterLink = {
        name: 'RouterLink',
        template: '',
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            RouterLink,
          },
        },
        props: {
          to: '/some-place',
          classes: undefined,
        },
      });

      expect(wrapper.vm.useRouterLink).toBe(true);
      expect(wrapper.getComponent(RouterLink)).toBeTruthy();
    });

    it('doesnt use a router-link when `to` prop is not defined even if route link component is available', () => {
      const RouterLink = {
        name: 'RouterLink',
        template: '',
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            RouterLink,
          },
        },
      });

      expect(wrapper.vm.useRouterLink).toBe(false);
    });

    it('uses a nuxt-link when `to` prop is defined and the nuxt-link component is available', () => {
      const NuxtLink = {
        name: 'NuxtLink',
        template: '',
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            NuxtLink,
          },
        },
        props: {
          to: '/some-place',
          classes: undefined,
        },
      });

      expect(wrapper.vm.useRouterLink).toBe(true);
      expect(wrapper.getComponent(NuxtLink)).toBeTruthy();
    });

    it('adds the routerLink related prop', () => {
      const props: any = {
        to: '/something',
        replace: true,
        activeClass: 'activeClass',
        exactActiveClass: 'exactActiveClass',
        custom: true,
        ariaCurrentValue: 'location',
      };

      const RouterLink = {
        name: 'RouterLink',
        template: '',
        props: Object.keys(props),
      };

      const wrapper = shallowMount(TButton, {
        global: {
          components: {
            RouterLink,
          },
        },
        props: {
          ...props,
          href: '/something',
        },
      });

      const component = wrapper.getComponent(RouterLink);
      expect(component.props()).toEqual(props);
    });
  });
});


================================================
FILE: src/__tests/components/TCard.spec.ts
================================================
import { shallowMount } from '@vue/test-utils';
import { TCardConfig } from '@variantjs/core';
import TCard from '@/components/TCard.vue';

describe('TCard.vue', () => {
  it('renders the component without errors', () => {
    const wrapper = shallowMount(TCard);
    expect(wrapper.vm.$el.tagName).toBe('DIV');
  });

  it('accepts a different tag for the wrapper', () => {
    const wrapper = shallowMount(TCard, {
      props: {
        tagName: 'table',
      },
    });
    expect(wrapper.vm.$el.tagName).toBe('TABLE');
  });

  it('renders the default slot content', () => {
    const wrapper = shallowMount(TCard, {
      slots: {
        default: 'Im a card!',
      },
    });

    expect(wrapper.vm.$el.querySelector('div').innerHTML).toBe('Im a card!');
  });

  it('doesnt have title or footer by default', () => {
    const wrapper = shallowMount(TCard, {
      slots: {
        default: 'Im a card!',
      },
    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(1);
  });

  it('adds the header slot', () => {
    const wrapper = shallowMount(TCard, {
      slots: {
        header: 'Im a header!',
        default: 'Im a card!',
      },
    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(2);
    expect(wrapper.vm.$el.querySelectorAll('div')[0].innerHTML).toBe('Im a header!');
  });

  it('adds the footer slot', () => {
    const wrapper = shallowMount(TCard, {
      slots: {
        default: 'Im a card!',
        footer: 'Im a footer!',
      },
    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(2);
    expect(wrapper.vm.$el.querySelectorAll('div')[1].innerHTML).toBe('Im a footer!');
  });

  it('adds the header prop', () => {
    const wrapper = shallowMount(TCard, {
      props: {
        header: 'Im a header!',
      },
      slots: {
        default: 'Im a card!',
      },
    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(2);
    expect(wrapper.vm.$el.querySelectorAll('div')[0].innerHTML).toBe('Im a header!');
  });

  it('adds the footer prop', () => {
    const wrapper = shallowMount(TCard, {
      props: {
        footer: 'Im a footer!',
      },
      slots: {
        default: 'Im a card!',
      },

    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(2);
    expect(wrapper.vm.$el.querySelectorAll('div')[1].innerHTML).toBe('Im a footer!');
  });

  it('adds the body prop', () => {
    const wrapper = shallowMount(TCard, {
      props: {
        body: 'Im a card!',
      },
    });

    expect(wrapper.vm.$el.querySelectorAll('div').length).toBe(1);
    expect(wrapper.vm.$el.querySelector('div').innerHTML).toBe('Im a card!');
  });

  it('prioritizes slots over props', () => {
    const wrapper = shallowMount(TCard, {
      props: {
        header: 'Im a header!',
        body: 'Im a card!',
        footer: 'Im a footer!',
      },
      slots: {
        default: 'default slot',
        header: 'header slot',
        footer: 'footer slot',
      },
    });

    const els = wrapper.vm.$el.querySelectorAll('div');
    expect(els.length).toBe(3);
    expect(els[0].innerHTML).toBe('header slot');
    expect(els[1].innerHTML).toBe('default slot');
    expect(els[2].innerHTML).toBe('footer slot');
  });

  it('has a default theme', () => {
    const wrapper = shallowMount(TCard, {
      slots: {
        default: 'default slot',
        header: 'header slot',
        footer: 'footer slot',
      },
    });

    const wrap = wrapper.vm.$el as HTMLDivElement;
    const els = wrapper.vm.$el.querySelectorAll('div');
    const header = els[0];
    const body = els[1];
    const footer = els[2];

    expect(wrap.className).toBe(TCardConfig.classes.wrapper);
    expect(header.className).toBe(TCardConfig.classes.header);
    expect(body.className).toBe(TCardConfig.classes.body);
    expect(footer.className).toBe(TCardConfig.classes.footer);
  });

  it('adds html attributes', () => {
    const wrapper = shallowMount(TCard, {
      attrs: {
        id: 'my-id',
      },
    });

    const wrap = wrapper.vm.$el as HTMLDivElement;

    expect(wrap.getAttribute('id')).toBe('my-id');
  });

  it('adds attributes from global configuration', () => {
    const wrapper = shallowMount(TCard, {
      global: {
        provide: {
          configuration: {
            TCard: {
              id: 'my-id',
            },
          },
        },
      },
    });

    const wrap = wrapper.vm.$el as HTMLDivElement;

    expect(wrap.getAttribute('id')).toBe('my-id');
  });

  it('used the props from global configuration', () => {
    const wrapper = shallowMount(TCard, {
      global: {
        provide: {
          configuration: {
            TCard: {
              tagName: 'table',
              footer: 'Copyright @alfonsobires',
            },
          },
        },
      },
    });

    expect(wrapper.vm.$el.tagName).toBe('TABLE');
    expect(wrapper.vm.$el.querySelector('div').innerHTML).toBe('Copyright @alfonsobires');
  });
});


================================================
FILE: src/__tests/components/TCheckbox.integration.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { render, fireEvent } from '@testing-library/vue';
import TCheckbox from '@/components/TCheckbox.vue';

describe('TCheckbox.vue', () => {
  it('handles the v-model', async () => {
    const modelValue = ['A'];
    const { container } = render(TCheckbox, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'checkbox-input',
        value: 'A',
      },
    });
    const { container: container2 } = render(TCheckbox, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'checkbox-input',
        value: 'B',
      },
    });

    const input = container.querySelector('input')!;
    const input2 = container2.querySelector('input')!;

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(false);

    await fireEvent.click(input2!);

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(true);

    await fireEvent.click(input!);

    expect(input.checked).toBe(false);
    expect(input2.checked).toBe(true);
  });

  it('handles the v-model with not regular value types', async () => {
    const modelValue = [[123, 'A']];
    const { container } = render(TCheckbox, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'checkbox-input',
        value: [123, 'A'],
      },
    });
    const { container: container2 } = render(TCheckbox, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'checkbox-input',
        value: () => {},
      },
    });
    const { container: container3 } = render(TCheckbox, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'checkbox-input',
        value: { A: 'B' },
      },
    });

    const input = container.querySelector('input')!;
    const input2 = container2.querySelector('input')!;
    const input3 = container3.querySelector('input')!;

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(false);
    expect(input3.checked).toBe(false);

    await fireEvent.click(input2!);

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(true);
    expect(input3.checked).toBe(false);

    await fireEvent.click(input3!);

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(true);
    expect(input3.checked).toBe(true);
  });
});


================================================
FILE: src/__tests/components/TCheckbox.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { shallowMount } from '@vue/test-utils';
import { TCheckboxConfig } from '@variantjs/core';
import TCheckbox from '@/components/TCheckbox.vue';

describe('TCheckbox.vue', () => {
  it('renders the input', () => {
    const wrapper = shallowMount(TCheckbox);
    expect(wrapper.get('input')).toBeTruthy();
  });

  it('renders the radio input with a default set of classes', () => {
    const wrapper = shallowMount(TCheckbox);

    expect(wrapper.html()).toBe(`<input type="checkbox" class="${TCheckboxConfig.classes}">`);
  });

  it('renders the input without attributes if no default theme', () => {
    const wrapper = shallowMount(TCheckbox, {
      global: {
        provide: {
          configuration: {
            TCheckbox: {
              classes: undefined,
            },
          },
        },
      },
    });

    expect(wrapper.html()).toBe('<input type="checkbox">');
  });

  it('adds the value attribute', () => {
    const value = 'input value';
    const wrapper = shallowMount(TCheckbox, {
      attrs: { value },
    });

    expect(wrapper.vm.$el.value).toBe(value);
  });

  it('doesnt add the modelValue as attribute', () => {
    const value = ['input value'];
    const wrapper = shallowMount(TCheckbox, {
      props: { modelValue: value },
      attrs: { value },
    });

    expect(wrapper.vm.$el.attributes.modelValue).toBeUndefined();
  });

  it('set as checked if model value contains the value ', async () => {
    const value = 'input value';
    const wrapper = shallowMount(TCheckbox, {
      props: { modelValue: ['something else'] },
      attrs: { value },
    });

    const radio = wrapper.vm.$el as HTMLInputElement;
    expect(radio.value).toBe(value);
    expect(radio.checked).toBe(false);

    await wrapper.setProps({
      modelValue: [value, 'something else'],
    });

    expect(radio.value).toBe(value);
    expect(radio.checked).toBe(true);
  });

  it('disables the input', async () => {
    const wrapper = shallowMount(TCheckbox, {
      props: { disabled: false },
    });
    expect(wrapper.vm.$el.disabled).toBe(false);

    await wrapper.setProps({ disabled: true });

    expect(wrapper.vm.$el.disabled).toBe(true);
  });

  it('accepts misc input attributes', async () => {
    const wrapper = shallowMount(TCheckbox);

    const values = {
      id: {
        default: '',
        new: 'new-id',
      },
      autocomplete: {
        default: '',
        new: 'on',
      },
      autofocus: {
        default: false,
        new: true,
      },
      disabled: {
        default: false,
        new: true,
      },
      max: {
        default: '',
        new: '10',
      },
      maxlength: {
        keyName: 'maxLength',
        default: 524288,
        new: 12,
      },
      minlength: {
        keyName: 'minLength',
        default: 0,
        new: 2,
      },
      min: {
        default: '',
        new: '3',
      },
      multiple: {
        default: false,
        new: true,
      },
      name: {
        default: '',
        new: 'new-name',
      },
      pattern: {
        default: '',
        new: '[A-Za-z]{3}',
      },
      placeholder: {
        default: '',
        new: 'new placeholder',
      },
      readonly: {
        keyName: 'readOnly',
        default: false,
        new: true,
      },
      required: {
        default: false,
        new: true,
      },
    };

    const netProps: any = {};
    // Check for the default values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.default);

      netProps[key as any] = elementValue.new;
    });

    await wrapper.setProps(netProps);

    // Check for the new values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.new);
    });
  });

  it('emits an update event with the input value', () => {
    const modelValue = 'original value';

    const wrapper = shallowMount(TCheckbox, {
      props: {
        modelValue,
      },
    });

    const inputValue = 'new value';

    wrapper.setValue(inputValue);

    expect(wrapper.emitted('update:modelValue')).toBeTruthy();

    // assert event count
    expect(wrapper.emitted('update:modelValue')?.length).toBe(1);

    // assert event payload
    expect(wrapper.emitted('update:modelValue')![0]).toEqual([inputValue]);
  });

  it('emits native input events', () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();
    const onFocus = jest.fn();
    const onKeyup = jest.fn();
    const onInput = jest.fn();

    const wrapper = shallowMount(TCheckbox, {
      attrs: {
        onChange,
        onBlur,
        onFocus,
        onKeyup,
        onInput,
      },
    });

    const input = wrapper.vm.$el;

    input.dispatchEvent(new Event('change'));
    expect(onChange).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('focus'));
    expect(onFocus).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('blur'));
    expect(onBlur).toHaveBeenCalled();

    input.dispatchEvent(new InputEvent('input'));
    expect(onInput).toHaveBeenCalled();

    input.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' }));
    expect(onKeyup).toHaveBeenCalled();
  });

  it('has native input methods', () => {
    const wrapper = shallowMount(TCheckbox);

    const input = wrapper.vm.$el;

    expect(typeof input.click).toBe('function');
    expect(typeof input.focus).toBe('function');
  });

  it('triggers custom events', async () => {
    const onCustom = jest.fn();

    const wrapper = shallowMount(TCheckbox, {
      attrs: {
        onCustom,
      },
    });
    const input = wrapper.vm.$el as HTMLInputElement;

    const evt = new CustomEvent('custom', { detail: 'my-custom-event' });
    input.dispatchEvent(evt);

    expect(onCustom).toHaveBeenCalled();
  });
});


================================================
FILE: src/__tests/components/TDialog.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { mount } from '@vue/test-utils';
import { h } from 'vue';
import { DialogHideReason, DialogType } from '@variantjs/core';
import TDialog from '@/components/TDialog.vue';
import { Emitter } from '../../utils/emitter';
import plugin from '../../plugin';
// eslint-disable-next-line no-async-promise-executor
const waitUntilModalIsShown = (wrapper: any): Promise<void> => new Promise(async (resolve) => {
  do {
    // eslint-disable-next-line no-await-in-loop
    await wrapper.vm.$nextTick();
  } while (wrapper.vm.$refs.modalRef.showModal === false);
  await wrapper.vm.$nextTick();

  resolve();
});

// eslint-disable-next-line no-async-promise-executor
const waitUntilModalIsHidden = (wrapper: any): Promise<void> => new Promise(async (resolve) => {
  do {
    // eslint-disable-next-line no-await-in-loop
    await wrapper.vm.$nextTick();
  } while (wrapper.vm.$refs.modalRef.showComponent === true);
  await wrapper.vm.$nextTick();

  resolve();
});

describe('TDialog.vue', () => {
  beforeEach(() => {
    TDialog.props.teleport.default = false;
    TDialog.props.modelValue.default = true;
  });

  afterEach(() => {
    TDialog.props.teleport.default = true;
    TDialog.props.modelValue.default = false;
  });

  it('mount the component without errors', async () => {
    const wrapper = mount(TDialog);

    expect(wrapper.get('div').attributes('tabindex')).toEqual('0');
  });

  describe('Props passed to the modal', () => {
    it('passes the dialogAttributes to the modalAttributes ', () => {
      const dialogAttributes = {
        'data-foo': 'bar',
        class: 'bg-red-500',
      };

      const wrapper = mount(TDialog, {
        props: {
          dialogAttributes,
        },
      });

      expect(wrapper.vm.$refs.modalRef.modalAttributes).toEqual(dialogAttributes);
    });

    it('passes the clickToClose setting ', () => {
      const wrapper = mount(TDialog, {
        props: {
          clickToClose: false,
        },
      });

      expect(wrapper.vm.$refs.modalRef.clickToClose).toBe(false);
    });

    it('passes the escToClose setting ', () => {
      const wrapper = mount(TDialog, {
        props: {
          escToClose: false,
        },
      });

      expect(wrapper.vm.$refs.modalRef.escToClose).toBe(false);
    });

    it('passes the disableBodyScroll setting ', () => {
      const wrapper = mount(TDialog, {
        props: {
          disableBodyScroll: false,
        },
      });

      expect(wrapper.vm.$refs.modalRef.disableBodyScroll).toBe(false);
    });

    it('passes the bodyScrollLockOptions setting ', () => {
      const bodyScrollLockOptions = {
        reserveScrollBarGap: true,
      };
      const wrapper = mount(TDialog, {
        props: {
          bodyScrollLockOptions,
        },
      });

      expect(wrapper.vm.$refs.modalRef.bodyScrollLockOptions).toEqual(bodyScrollLockOptions);
    });

    it('passes the negative value of showCloseButton setting to the hideCloseButton ', () => {
      const wrapper = mount(TDialog, {
        props: {
          showCloseButton: false,
        },
      });

      expect(wrapper.vm.$refs.modalRef.hideCloseButton).toBe(true);
    });

    it('passes the teleport setting ', () => {
      const wrapper = mount(TDialog, {
        props: {
          teleport: false,
        },
      });

      expect(wrapper.vm.$refs.modalRef.teleport).toBe(false);
    });

    it('passes the teleportTo setting ', () => {
      const div = document.createElement('div');
      div.id = 'app';

      document.body.append(div);

      const wrapper = mount(TDialog, {
        props: {
          teleportTo: '#app',
        },
      });

      expect(wrapper.vm.$refs.modalRef.teleportTo).toBe('#app');
    });
  });

  describe('Title', () => {
    it('adds the title', () => {
      const wrapper = mount(TDialog, {
        props: {
          title: 'Are you sure?',
        },
      });

      expect(wrapper.find('h3').text()).toEqual('Are you sure?');
    });

    it('adds the title trough the slot', () => {
      const wrapper = mount(TDialog, {
        slots: {
          title: 'Are you sure?',
        },
      });

      expect(wrapper.find('h3').text()).toEqual('Are you sure?');
    });

    it('accepts a title tag', () => {
      const wrapper = mount(TDialog, {
        props: {
          title: 'Are you sure?',
          titleTag: 'h1',
        },
      });

      expect(wrapper.find('h1').text()).toEqual('Are you sure?');
    });

    it('doesnt add the title if not set', () => {
      const wrapper = mount(TDialog);

      expect(wrapper.find('h3').exists()).toBe(false);
    });
  });

  describe('Text', () => {
    it('adds the text', () => {
      const wrapper = mount(TDialog, {
        props: {
          text: 'This action cannot be undone',
        },
      });

      expect(wrapper.find('p').text()).toEqual('This action cannot be undone');
    });

    it('adds the text trough the slot', () => {
      const wrapper = mount(TDialog, {
        slots: {
          text: 'This action cannot be undone',
        },
      });

      expect(wrapper.find('p').text()).toEqual('This action cannot be undone');
    });

    it('accepts a text tag', () => {
      const wrapper = mount(TDialog, {
        props: {
          text: 'This action cannot be undone',
          textTag: 'blockquote',
        },
      });

      expect(wrapper.find('blockquote').text()).toEqual('This action cannot be undone');
    });

    it('doesnt add the text if not set', () => {
      const wrapper = mount(TDialog);

      expect(wrapper.find('p').exists()).toBe(false);
    });
  });

  describe('Cancel Button', () => {
    const props = {
      type: 'confirm',
    };

    it('has a default label', () => {
      const wrapper = mount(TDialog, { props });

      const cancelButton = wrapper.vm.$refs.cancelButton as HTMLButtonElement;

      expect(cancelButton.textContent).toEqual('Cancel');
    });

    it('has a default aria-label attribute', () => {
      const wrapper = mount(TDialog, { props });

      const cancelButton = wrapper.vm.$refs.cancelButton as HTMLButtonElement;

      expect(cancelButton.getAttribute('aria-label')).toEqual('Cancel');
    });

    it('accepts a different label and aria-label', () => {
      const wrapper = mount(TDialog, {
        props: {
          ...props,
          cancelButtonText: 'Nah',
          cancelButtonAriaLabel: 'Nah Button',
        },
      });

      const cancelButton = wrapper.vm.$refs.cancelButton as HTMLButtonElement;

      expect(cancelButton.textContent).toEqual('Nah');
      expect(cancelButton.getAttribute('aria-label')).toEqual('Nah Button');
    });

    it('calls the cancel method when clicked', () => {
      const wrapper = mount(TDialog, { props });

      const cancelFnMock = jest.fn();

      wrapper.vm.cancel = cancelFnMock;

      const cancelButton = wrapper.vm.$refs.cancelButton as HTMLButtonElement;

      cancelButton.dispatchEvent(new Event('click'));

      expect(cancelFnMock).toHaveBeenCalled();
    });

    it('closes the modal when cancel button is clicked', async () => {
      const wrapper = mount(TDialog, { props });

      wrapper.vm.cancel();

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');
      expect(wrapper.emitted('hidden')).toEqual([[{
        hideReason: 'cancel',
        isCancel: true,
        isDismissed: false,
        isOk: false,
      }]]);
    });
  });

  describe('OK Button', () => {
    const props = {
      type: 'confirm',
    };

    it('has a default label', () => {
      const wrapper = mount(TDialog, { props });

      const okButton = wrapper.vm.$refs.okButton as HTMLButtonElement;

      expect(okButton.textContent).toEqual('OK');
    });

    it('has a default aria-label attribute', () => {
      const wrapper = mount(TDialog, { props });

      const okButton = wrapper.vm.$refs.okButton as HTMLButtonElement;

      expect(okButton.getAttribute('aria-label')).toEqual('OK');
    });

    it('accepts a different label and aria-label', () => {
      const wrapper = mount(TDialog, {
        props: {
          ...props,
          okButtonText: 'Yei!',
          okButtonAriaLabel: 'Ok Button',
        },
      });

      const okButton = wrapper.vm.$refs.okButton as HTMLButtonElement;

      expect(okButton.textContent).toEqual('Yei!');
      expect(okButton.getAttribute('aria-label')).toEqual('Ok Button');
    });

    it('calls the ok method when clicked', () => {
      const wrapper = mount(TDialog, { props });

      const okFnMock = jest.fn();

      wrapper.vm.ok = okFnMock;

      const okButton = wrapper.vm.$refs.okButton as HTMLButtonElement;

      okButton.dispatchEvent(new Event('click'));

      expect(okFnMock).toHaveBeenCalled();
    });
  });

  describe('Dialog types', () => {
    describe('Alert type', () => {
      const props = {
        type: 'alert',
      };
      it('has an ok button', () => {
        const wrapper = mount(TDialog, { props });

        const { okButton } = wrapper.vm.$refs;

        expect(okButton).toBeTruthy();
      });

      it('doesnt have a cancel button', () => {
        const wrapper = mount(TDialog, { props });

        const { cancelButton } = wrapper.vm.$refs;

        expect(cancelButton).toBeUndefined();
      });
    });

    describe('Confirm type', () => {
      const props = {
        type: 'confirm',
      };

      it('has an ok button', () => {
        const wrapper = mount(TDialog, { props });

        const { okButton } = wrapper.vm.$refs;

        expect(okButton).toBeTruthy();
      });

      it('has a cancel button', () => {
        const wrapper = mount(TDialog, { props });

        const { cancelButton } = wrapper.vm.$refs;

        expect(cancelButton).toBeTruthy();
      });
    });

    describe('Prompt type', () => {
      const props = {
        type: 'prompt',
      };

      it('has an ok button', () => {
        const wrapper = mount(TDialog, { props });

        const { okButton } = wrapper.vm.$refs;

        expect(okButton).toBeTruthy();
      });

      it('has a cancel button', () => {
        const wrapper = mount(TDialog, { props });

        const { cancelButton } = wrapper.vm.$refs;

        expect(cancelButton).toBeTruthy();
      });
    });
  });

  describe('Dialog Icon', () => {
    it('doesnt add an icon by default', () => {
      const wrapper = mount(TDialog);

      expect(wrapper.vm.$refs.iconWrapperRef).toBeFalsy();
    });

    it.each(['success', 'question', 'info', 'warning', 'error'])('adds the icon', (icon) => {
      const wrapper = mount(TDialog, {
        props: {
          icon,
        },
      });

      expect(wrapper.vm.$refs.iconWrapperRef).toBeTruthy();

      expect((wrapper.vm.$refs.iconWrapperRef as HTMLDivElement).children[0].tagName).toBe('svg');
    });

    it.each(['success', 'question', 'info', 'warning', 'error'])('adds the icon when useSolidIcon is iset', (icon) => {
      const wrapper = mount(TDialog, {
        props: {
          icon,
          useSolidIcon: true,
        },
      });

      expect(wrapper.vm.$refs.iconWrapperRef).toBeTruthy();

      expect((wrapper.vm.$refs.iconWrapperRef as HTMLDivElement).children[0].tagName).toBe('svg');
    });
  });

  describe('promise behaviour on reject', () => {
    const emitter = new Emitter();

    const params = {
      props: {
        type: 'confirm',
        name: 'my-dialog',
      },
      global: {
        provide: {
          // Emulates the plugin system
          emitter,
        },
      },
    };

    it('resolves the promise when is ok by default', async () => {
      const wrapper = mount(TDialog, params);

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Ok);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).toHaveBeenCalled();
      expect(reject).not.toHaveBeenCalled();
    });

    it('rejects the promise when is dismissed by default', async () => {
      const wrapper = mount(TDialog, params);

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Outside);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).not.toHaveBeenCalled();
      expect(reject).toHaveBeenCalled();
    });

    it('doesnt rejects the promise when is dismissed for alert dialogs', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...params.props,
          type: 'alert',
        },
        global: params.global,
      });

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Outside);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).toHaveBeenCalled();
      expect(reject).not.toHaveBeenCalled();
    });

    it('rejects the promise when rejectOnDismiss is set', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...params.props,
          rejectOnDismiss: true,
        },
        global: params.global,
      });

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Outside);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).not.toHaveBeenCalled();
      expect(reject).toHaveBeenCalled();
    });

    it('rejects the promise when rejectOnDismiss is set for alerts', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...params.props,
          type: 'alert',
          rejectOnDismiss: true,
        },
        global: params.global,
      });

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Outside);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).not.toHaveBeenCalled();
      expect(reject).toHaveBeenCalled();
    });
  });

  describe('Promise behaviour on cancel', () => {
    const emitter = new Emitter();

    const params = {
      props: {
        type: 'confirm',
        name: 'my-dialog',
      },
      global: {
        provide: {
          // Emulates the plugin system
          emitter,
        },
      },
    };

    it('rejects the promise when is cancel by default', async () => {
      const wrapper = mount(TDialog, params);

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Cancel);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).not.toHaveBeenCalled();
      expect(reject).toHaveBeenCalled();
    });

    it('rejects the promise when rejectOnCancel is set', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...params.props,
          rejectOnCancel: true,
        },
        global: params.global,
      });

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Cancel);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).not.toHaveBeenCalled();
      expect(reject).toHaveBeenCalled();
    });

    it('resolves the promise when rejectOnCancel is set to `false`', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...params.props,
          rejectOnCancel: false,
        },
        global: params.global,
      });

      // @see vue/src/plugin.ts show helper
      const resolve = jest.fn();
      const reject = jest.fn();

      emitter.emit('dialog:show', 'my-dialog', resolve, reject);
      await waitUntilModalIsShown(wrapper);

      wrapper.vm.hide(DialogHideReason.Cancel);
      await waitUntilModalIsHidden(wrapper);

      expect(resolve).toHaveBeenCalled();
      expect(reject).not.toHaveBeenCalled();
    });
  });

  describe('preconfirm', () => {
    it('closes the dialog with the response of the confirm method', async () => {
      const response = { success: true };
      const preConfirm = () => new Promise((resolve) => {
        resolve(response);
      });

      const wrapper = mount(TDialog, {
        props: {
          preConfirm,
        },
      });

      wrapper.vm.ok();

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');

      expect(wrapper.emitted('hidden')).toEqual([[{
        hideReason: 'ok',
        isCancel: false,
        isDismissed: false,
        isOk: true,
        response,
      }]]);

      expect(wrapper.vm.busy).toBe(false);
    });

    it('uses the result of function that is not a promise as result', async () => {
      const response = { success: true };
      const preConfirm = () => response;

      const wrapper = mount(TDialog, {
        props: {
          preConfirm,
        },
      });

      wrapper.vm.ok();

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');

      expect(wrapper.emitted('hidden')).toEqual([[{
        hideReason: 'ok',
        isCancel: false,
        isDismissed: false,
        isOk: true,
        response,
      }]]);

      expect(wrapper.vm.busy).toBe(false);
    });

    it('doesnt closes the dialog when response fails', async () => {
      const error = new Error('something went wrong!');

      const preConfirm = () => new Promise((_resolve, reject) => {
        reject(error);
      });

      const wrapper = mount(TDialog, {
        props: {
          preConfirm,
        },
      });

      wrapper.vm.ok();

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.emitted()).toHaveProperty('error');

      expect(wrapper.emitted('error')).toEqual([[error]]);

      expect(wrapper.vm.busy).toBe(false);

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.vm.modelValue).toBe(true);
    });
  });

  describe('named dialog', () => {
    const settings = {
      props: {
        name: 'named-dialog',
        modelValue: false,
      },
      global: {
        plugins: [plugin],
      },
    };

    it('opens the dialog by his name', async () => {
      const wrapper = mount(TDialog, settings);

      wrapper.vm.$dialog.show('named-dialog');

      await waitUntilModalIsShown(wrapper);

      expect(wrapper.emitted()).toHaveProperty('shown');
    });

    it('doesnt opens the dialog if name is different', async () => {
      const wrapper = mount(TDialog, settings);

      wrapper.vm.$dialog.show('another-dialog');

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.emitted()).not.toHaveProperty('shown');
    });

    it('return a promise', async () => {
      const wrapper = mount(TDialog, settings);

      const promise = wrapper.vm.$dialog.show('named-dialog');

      expect(promise).toBeInstanceOf(Promise);
    });

    it('hides the dialog by his name', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...settings.props,
          modelValue: true,
        },
        global: settings.global,
      });

      wrapper.vm.$dialog.hide('named-dialog');

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');
    });

    it('doesnt hides the dialog if using another name', async () => {
      const wrapper = mount(TDialog, {
        props: {
          ...settings.props,
          modelValue: true,
        },
        global: settings.global,
      });

      wrapper.vm.$dialog.hide('another-dialog');

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.emitted()).not.toHaveProperty('hidden');
    });
  });

  describe('vModel', () => {
    it('shows the component when updated the v-model to true', async () => {
      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
        },
      });

      wrapper.setProps({
        modelValue: true,
      });

      await waitUntilModalIsShown(wrapper);

      expect(wrapper.emitted()).toHaveProperty('shown');
    });

    it('hides the component when updated the v-model to false', async () => {
      const wrapper = mount(TDialog, {
        props: {
          modelValue: true,
        },
      });

      wrapper.setProps({
        modelValue: false,
      });

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');
    });
  });

  describe('focus on open', () => {
    let originalDivFocus: any;
    let originalInputFocus: any;
    let originalTextareaFocus: any;
    beforeEach(() => {
      originalInputFocus = window.HTMLInputElement.prototype.focus;
      originalTextareaFocus = window.HTMLTextAreaElement.prototype.focus;
      originalDivFocus = window.HTMLInputElement.prototype.focus;
    });

    afterEach(() => {
      window.HTMLTextAreaElement.prototype.focus = originalTextareaFocus;
      window.HTMLInputElement.prototype.focus = originalInputFocus;
      window.HTMLInputElement.prototype.focus = originalDivFocus;
    });

    it('focus the div when focus on open is set an is not a prompt type', async () => {
      const focusInputMock = jest.fn();
      const focusDivMock = jest.fn();
      window.HTMLInputElement.prototype.focus = focusInputMock;
      window.HTMLDivElement.prototype.focus = focusDivMock;

      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
          type: DialogType.Alert,
        },
      });

      wrapper.vm.show();

      await waitUntilModalIsShown(wrapper);

      expect(focusInputMock).not.toHaveBeenCalled();
      expect(focusDivMock).toHaveBeenCalled();
    });

    it('focus the input when focus on open is set and is a prompt type', async () => {
      const focusInputMock = jest.fn();
      const focusDivMock = jest.fn();
      window.HTMLInputElement.prototype.focus = focusInputMock;
      window.HTMLDivElement.prototype.focus = focusDivMock;

      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
          type: DialogType.Prompt,
        },
      });

      wrapper.vm.show();

      await waitUntilModalIsShown(wrapper);

      expect(focusInputMock).toHaveBeenCalled();
      expect(focusDivMock).not.toHaveBeenCalled();
    });

    it('focus first focusable item when using the slot and is a prompt type', async () => {
      const focusDivMock = jest.fn();
      const focusTextareaMock = jest.fn();
      window.HTMLDivElement.prototype.focus = focusDivMock;
      window.HTMLTextAreaElement.prototype.focus = focusTextareaMock;

      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
          type: DialogType.Prompt,
        },
        slots: {
          input: () => h('textarea'),
        },
      });

      wrapper.vm.show();

      await waitUntilModalIsShown(wrapper);

      expect(focusTextareaMock).toHaveBeenCalled();
      expect(focusDivMock).not.toHaveBeenCalled();
    });

    it('focus the dialog if no focusable item when using the slot and is a prompt type', async () => {
      const focusDivMock = jest.fn();
      window.HTMLDivElement.prototype.focus = focusDivMock;

      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
          type: DialogType.Prompt,
        },
        slots: {
          input: () => h('div'),
        },
      });

      wrapper.vm.show();

      await waitUntilModalIsShown(wrapper);

      expect(focusDivMock).toHaveBeenCalled();
    });

    it('doesnt focus anything if `focusOnOpen` is set to `false', async () => {
      const focusInputMock = jest.fn();
      const focusDivMock = jest.fn();
      window.HTMLInputElement.prototype.focus = focusInputMock;
      window.HTMLDivElement.prototype.focus = focusDivMock;

      const wrapper = mount(TDialog, {
        props: {
          modelValue: false,
          type: DialogType.Prompt,
          focusOnOpen: false,
        },
      });

      wrapper.vm.show();

      await waitUntilModalIsShown(wrapper);

      expect(focusInputMock).not.toHaveBeenCalled();
      expect(focusDivMock).not.toHaveBeenCalled();
    });
  });

  describe('Input related props', () => {
    it('assigns the inputValue to the inputModel', () => {
      const wrapper = mount(TDialog, {
        props: {
          inputValue: 'hello',
          type: DialogType.Prompt,
        },
      });

      expect(wrapper.vm.inputModel).toBe('hello');
      expect(wrapper.find('input').element.value).toBe('hello');
    });

    it('resets to the value on the inputValue when modal is closed', async () => {
      const wrapper = mount(TDialog, {
        props: {
          inputValue: 'hello',
          type: DialogType.Prompt,
        },
      });

      expect(wrapper.vm.inputModel).toBe('hello');
      wrapper.find('input').element.value = 'world';
      wrapper.find('input').trigger('input');
      expect(wrapper.vm.inputModel).toBe('world');

      wrapper.vm.hide();

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.vm.inputModel).toBe('hello');
    });

    it('passes the inputType to the input', async () => {
      const wrapper = mount(TDialog, {
        props: {
          inputType: 'password',
          type: DialogType.Prompt,
        },
      });

      expect(wrapper.find('input').attributes('type')).toBe('password');
    });

    it('passes the inputAttributes to the input', async () => {
      const wrapper = mount(TDialog, {
        props: {
          inputAttributes: {
            'data-foo': 'bar',
            width: '100',
          },
          type: DialogType.Prompt,
        },
      });

      expect(wrapper.find('input').attributes('data-foo')).toBe('bar');
      expect(wrapper.find('input').attributes('width')).toBe('100');
    });
  });

  describe('Input validation', () => {
    it('doesnt hides the dialog when validation returns a message', async () => {
      const inputValidator = () => 'error!';
      const wrapper = mount(TDialog, {
        props: {
          type: DialogType.Prompt,
          inputValidator,
        },
      });

      expect(wrapper.html()).not.toContain('error!');

      wrapper.vm.ok();

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.emitted()).toHaveProperty('validation-error');

      expect(wrapper.emitted('validation-error')).toEqual([['error!']]);

      expect(wrapper.vm.busy).toBe(false);

      expect(wrapper.html()).toContain('error!');

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.vm.modelValue).toBe(true);
    });

    it('resets the validation error when ok', async () => {
      const inputValidator = () => null;
      const wrapper = mount(TDialog, {
        props: {
          type: DialogType.Prompt,
          inputValidator,
        },
      });

      wrapper.vm.setValidationError('error!');

      await wrapper.vm.$nextTick();

      expect(wrapper.html()).toContain('error!');

      wrapper.vm.ok();

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.html()).not.toContain('error!');
    });

    it('validates on input', async () => {
      const inputValidator = (val: string) => (val === 'fail' ? 'fail' : null);
      const wrapper = mount(TDialog, {
        props: {
          type: DialogType.Prompt,
          inputValidator,
        },
      });

      wrapper.find('input').element.value = 'fail';
      wrapper.find('input').trigger('input');

      await wrapper.vm.$nextTick();

      expect(wrapper.html()).toContain('fail');
    });

    it('resets the validation when input', async () => {
      const inputValidator = () => null;
      const wrapper = mount(TDialog, {
        props: {
          type: DialogType.Prompt,
          inputValidator,
        },
      });

      wrapper.vm.setValidationError('error!');

      await wrapper.vm.$nextTick();

      expect(wrapper.html()).toContain('error!');

      wrapper.find('input').element.value = 'something';
      wrapper.find('input').trigger('input');

      await wrapper.vm.$nextTick();

      expect(wrapper.html()).not.toContain('error!');
    });
  });

  describe('Misc validations', () => {
    it('doesnt closes the dialog if is busy', async () => {
      const wrapper = mount(TDialog);

      wrapper.vm.busy = true;

      wrapper.vm.hide();

      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();
      await wrapper.vm.$nextTick();

      expect(wrapper.emitted()).not.toHaveProperty('hidden');
    });

    it('closes the dialog when is busy when hide reason is "ok"', async () => {
      const wrapper = mount(TDialog);

      wrapper.vm.busy = true;

      wrapper.vm.hide(DialogHideReason.Ok);

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted()).toHaveProperty('hidden');
    });

    it('uses the modal hide reason', async () => {
      const wrapper = mount(TDialog);

      wrapper.vm.$refs.modalRef.$refs.overlay.dispatchEvent(new KeyboardEvent('keydown', {
        key: 'Escape',
      }));

      // wrapper.vm.$refs.modalRef.onKeydownEscapeHandler();

      await waitUntilModalIsHidden(wrapper);

      expect(wrapper.emitted('hidden')).toEqual([[{
        hideReason: 'esc',
        isCancel: false,
        isDismissed: true,
        isOk: false,
      }]]);
    });
  });
});


================================================
FILE: src/__tests/components/TDropdown.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */

import { mount, VueWrapper } from '@vue/test-utils';
import { TDropdownConfig, TDropdownPopperDefaultOptions } from '@variantjs/core';
import { h } from 'vue';
import TDropdown from '@/components/TDropdown.vue';
import { scopedParamsAsString, parseScopedParams } from '../testUtils';

describe('TDropdown.vue', () => {
  let updatePopperMock: jest.Mock<any, any>;
  const originalUpdatePopperMethod = TDropdown.methods!.updatePopper;

  beforeEach(() => {
    updatePopperMock = jest.fn().mockReturnValue(Promise.resolve());

    TDropdown.methods!.updatePopper = updatePopperMock;
  });

  afterEach(() => {
    updatePopperMock.mockRestore();
    TDropdown.methods!.updatePopper = originalUpdatePopperMethod;
  });

  it('renders the component', () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.find('button').exists()).toBe(true);
    expect(wrapper.find('button').isVisible()).toBe(true);
    expect(wrapper.find('div').exists()).toBe(true);
    expect(wrapper.vm.$refs.trigger).toBeTruthy();
    expect(wrapper.vm.$refs.dropdown).toBeTruthy();
  });

  it('has default classes', () => {
    const wrapper = mount(TDropdown);
    const { trigger, dropdown } = wrapper.vm.$refs;

    expect(trigger.className).toBe(TDropdownConfig.classes.trigger);
    expect(dropdown.className).toBe(TDropdownConfig.classes.dropdown);
    expect(wrapper.vm.configuration.classesList).toEqual(TDropdownConfig.classes);
  });

  it('initializes the dropdown', async () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.vm.shown).toBe(false);
    expect(wrapper.vm.popperIsAdjusted).toBe(false);
    expect(wrapper.vm.popper).toBe(null);
  });

  it('uses the content of the trigger slot inside the trigger button', () => {
    const wrapper = mount(TDropdown, {
      slots: {
        trigger: 'Press me!',
      },
    });
    expect(wrapper.find('button').text()).toBe('Press me!');
  });

  it('uses the content of the text prop inside the trigger button', () => {
    const wrapper = mount(TDropdown, {
      props: {
        text: 'Press me!',
      },
    });

    expect(wrapper.find('button').text()).toBe('Press me!');
  });

  it('exposes the `isShow` variable and the configuration ', () => {
    const wrapper = mount(TDropdown, {
      slots: {
        trigger: (params) => scopedParamsAsString(params),
      },
    });

    const scopeParamKeys = parseScopedParams(wrapper.text());

    expect(scopeParamKeys).toEqual({
      isShow: 'boolean',
      configuration: 'object',
      popper: 'object',
    });
  });

  it('exposes the `toggle`, `show` and `hide` methods and the configuration to the dropdown slot ', () => {
    const wrapper = mount(TDropdown, {
      slots: {
        default: (params) => scopedParamsAsString(params),
      },
    });

    const scopeParamKeys = parseScopedParams(wrapper.text());

    expect(scopeParamKeys).toEqual({
      show: 'function',
      hide: 'function',
      toggle: 'function',
      configuration: 'object',
      popper: 'object',
    });
  });

  it('renders an empty button if no slot or text', () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.find('button').text()).toBe('');
  });

  it('prioritizes the slot over the text prop', () => {
    const wrapper = mount(TDropdown, {
      props: {
        text: 'Press me2!',
      },
      slots: {
        trigger: 'Press me!',
      },
    });

    expect(wrapper.find('button').text()).toBe('Press me!');
  });

  it('uses the content of the default slot inside the dropdown', () => {
    const wrapper = mount(TDropdown, {
      slots: {
        default: 'Dropdown stuffy',
      },
    });

    const { dropdown } = wrapper.vm.$refs;
    expect(dropdown.innerHTML).toBe('Dropdown stuffy');
  });

  it('shows the dropdown when the trigger is pressed', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnClick: true,
        toggleOnFocus: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides the dropdown when the trigger is pressed and is openede ', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnClick: true,
        show: true,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('doesnt teleports the dropdown by default', () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.find('div').exists()).toBe(true);
  });

  it('teleports the dropdown to the body if teleport option is set', () => {
    mount(TDropdown, {
      props: {
        teleport: true,
      },
      slots: {
        default: 'The body',
      },
    });

    expect(document.body.children[0].textContent).toBe('The body');
  });

  it('teleports the dropdown to the selector in the teleportTo prop', () => {
    const div = document.createElement('div');
    div.id = 'teleport-here';

    document.body.appendChild(div);

    mount(TDropdown, {
      props: {
        teleport: true,
        teleportTo: '#teleport-here',
      },
      slots: {
        default: 'The body',
      },
    });

    expect(document.querySelector('#teleport-here')!.textContent).toBe('The body');
  });

  it('teleports the dropdown to the element in the teleportTo prop', () => {
    const div = document.createElement('div');
    div.id = 'dont-teleport-here';

    document.body.appendChild(div);

    mount(TDropdown, {
      props: {
        teleport: true,
        teleportTo: div,
      },
      slots: {
        default: 'The body',
      },
    });

    expect(document.querySelector('#teleport-here')!.textContent).toBe('The body');
  });

  it('the trigger is a button with type `button`', async () => {
    const wrapper = mount(TDropdown);

    const trigger = wrapper.get('button');

    expect(trigger.attributes().type).toBe('button');
    expect(trigger.element.tagName).toBe('BUTTON');
  });

  it('disables the dropdown', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        disabled: true,
      },
    });

    const trigger = wrapper.get('button');

    const { dropdown } = wrapper.vm.$refs;

    expect(trigger.attributes().disabled).toBeDefined();

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);

    expect(dropdown.style.display).toBe('none');
  });

  it('applies the dropdownAttributes to the dropdown', () => {
    const wrapper = mount(TDropdown, {
      props: {
        dropdownAttributes: {
          id: 'my-id',
          'data-foo': 'bar',
        },
      },
    });

    const { dropdown } = wrapper.vm.$refs;

    expect(dropdown.getAttribute('id')).toBe('my-id');
    expect(dropdown.getAttribute('data-foo')).toBe('bar');
  });

  it('applies the attributes to the trigger button', () => {
    const wrapper = mount(TDropdown, {
      attrs: {
        id: 'my-id',
        'data-foo': 'bar',
      },
    });

    const trigger = wrapper.get('button');

    expect(trigger.attributes().id).toBe('my-id');
    expect(trigger.attributes()['data-foo']).toBe('bar');
  });

  it('applies the attributes that comes from the configuration the trigger button', () => {
    const wrapper = mount(TDropdown, {
      global: {
        provide: {
          configuration: {
            TDropdown: {
              id: 'my-id',
              'data-foo': 'bar',
            },
          },
        },
      },
    });

    const trigger = wrapper.get('button');

    expect(trigger.attributes().id).toBe('my-id');
    expect(trigger.attributes()['data-foo']).toBe('bar');
  });

  it('prioritizes the local attributes', () => {
    const wrapper = mount(TDropdown, {
      global: {
        provide: {
          configuration: {
            TDropdown: {
              id: 'my-id',
            },
          },
        },
      },
      attrs: {
        id: 'my-local-id',
      },
    });

    const trigger = wrapper.get('button');

    expect(trigger.attributes().id).toBe('my-local-id');
  });

  it('uses the set tagName ', () => {
    const wrapper = mount(TDropdown, {
      props: {
        tagName: 'a',
      },
    });

    const { trigger } = wrapper.vm.$refs;

    expect(trigger.tagName).toBe('A');
  });

  it('uses the set dropdownTagName ', () => {
    const wrapper = mount(TDropdown, {
      props: {
        dropdownTagName: 'ul',
      },
    });

    const { dropdown } = wrapper.vm.$refs;

    expect(dropdown.tagName).toBe('UL');
  });

  it('doesnt toggle the dropdown on click if toggleOnClick is set to `false`', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnClick: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('shows the dropdown on click by default', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        // To avoid false positives
        toggleOnFocus: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides the dropdown on click by default', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        // To avoid false positives
        toggleOnFocus: false,
        show: true,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('click');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('shows the dropdown on focus by default', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        // To avoid false positives
        toggleOnClick: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('focus');

    expect(wrapper.vm.shown).toBe(true);
  });

  it('doesnt hide the dropdown when focus when doesnt have the `toggleOnFocus` options', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('focus');

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides the dropdown on blur by default', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        // To avoid false positives
        toggleOnClick: false,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('doesnt shows the dropdown on hover by default', async () => {
    const wrapper = mount(TDropdown);

    const trigger = wrapper.get('button');

    await trigger.trigger('hover');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('doesnt hides the drodown on hoverout by default', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('hover');

    expect(wrapper.vm.shown).toBe(true);
  });

  it('toggles the dropdown on focus if option is set', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnFocus: true,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('focus');

    expect(wrapper.vm.shown).toBe(true);

    await trigger.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('doesnt hides the dropdown if blur in the the dropdown', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
    });

    const triggerButton = wrapper.get('button');

    const { dropdown } = wrapper.vm.$refs;

    await triggerButton.trigger('blur', {
      relatedTarget: dropdown,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('adds and remove blur listener to focusable elements inside the dropdown when shown', async () => {
    const addEventListenerMock = jest.fn();
    const removeEventListenerMock = jest.fn();
    window.HTMLInputElement.prototype.addEventListener = addEventListenerMock;
    window.HTMLInputElement.prototype.removeEventListener = removeEventListenerMock;
    const wrapper = mount(TDropdown, {
      slots: {
        default: h('input'),
      },
    });

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    expect(addEventListenerMock).toHaveBeenCalled();

    wrapper.vm.doHide();

    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    expect(removeEventListenerMock).toHaveBeenCalled();
  });

  it('doesnt hides the dropdown if blur in the the trigger', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
    });

    const triggerButton = wrapper.get('button');

    const { trigger } = wrapper.vm.$refs;

    await triggerButton.trigger('blur', {
      relatedTarget: trigger,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('doesnt hides the dropdown if blur in an element inside the dropdown', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
      slots: {
        default: h('button', 'focus me'),
      },
    });

    const triggerButton = wrapper.get('button');

    const { dropdown } = wrapper.vm.$refs;

    const button = dropdown.querySelector('button');

    await triggerButton.trigger('blur', {
      relatedTarget: button,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides the dropdown on trigger blur', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('hides the dropdown on dropdown blur', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
    });

    const dropdown = wrapper.get('div');

    await dropdown.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('hides the dropdown if blur on an element that is not a child', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
      slots: {
        default: h('button', {}, 'blur me'),
      },
    });

    const button = wrapper.find('button') as any;

    button.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('removes the child element blur handler if toggleOnFocus changes', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnFocus: true,
      },
      slots: {
        default: h('button', {}, 'blur me'),
      },
    });

    wrapper.setProps({
      toggleOnFocus: false,
    });

    const { dropdown } = wrapper.vm.$refs;

    const button = dropdown.querySelector('button') as HTMLButtonElement;

    button.dispatchEvent(new FocusEvent('blur'));

    expect(wrapper.vm.shown).toBe(true);
    expect(wrapper.vm.focusableElements).toEqual([]);
  });

  it('doesnt toggle the dropdown on hover by default', async () => {
    const wrapper = mount(TDropdown);

    const trigger = wrapper.get('button');

    await trigger.trigger('hover');

    expect(wrapper.vm.shown).toBe(false);

    await trigger.trigger('blur');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('toggles the dropdown on hover immediatly if option is set', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnHover: true,
        hideOnLeaveTimeout: null,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('mouseover');

    expect(wrapper.vm.shown).toBe(true);

    await trigger.trigger('mouseleave');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('doesnt toggles the dropdown on hover if option is not set', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        toggleOnHover: false,
        hideOnLeaveTimeout: null,
      },
    });

    const trigger = wrapper.get('button');

    await trigger.trigger('mouseover');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('hides the dropdown if mouseleave the dropdown', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnHover: true,
        hideOnLeaveTimeout: null,
      },
    });

    const dropdown = wrapper.get('div');

    await dropdown.trigger('mouseleave');

    expect(wrapper.vm.shown).toBe(false);
  });

  it('hides the dropdown after the timeout', async () => {
    jest.useFakeTimers();

    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnHover: true,
        hideOnLeaveTimeout: 500,
      },
    });

    const dropdown = wrapper.get('div');

    await dropdown.trigger('mouseleave');

    expect(wrapper.vm.shown).toBe(true);

    jest.advanceTimersByTime(499);

    expect(wrapper.vm.shown).toBe(true);

    jest.advanceTimersByTime(1);

    expect(wrapper.vm.shown).toBe(false);

    jest.useRealTimers();
  });

  it('clears the hidetimeout if something receives mouseover ', async () => {
    jest.useFakeTimers();

    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnHover: true,
        hideOnLeaveTimeout: 500,
      },
    });

    const button = wrapper.get('button');
    const dropdown = wrapper.get('div');

    await dropdown.trigger('mouseleave');

    expect(wrapper.vm.shown).toBe(true);

    jest.advanceTimersByTime(499);

    await button.trigger('mouseover');

    expect(wrapper.vm.shown).toBe(true);

    jest.advanceTimersByTime(500);

    expect(wrapper.vm.shown).toBe(true);

    jest.useRealTimers();
  });

  it('doesnt hides the dropdown if mouseleave the dropdown', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnHover: true,
        hideOnLeaveTimeout: null,
      },
    });

    const triggerButton = wrapper.get('button');

    const { dropdown } = wrapper.vm.$refs;

    await triggerButton.trigger('mouseleave', {
      relatedTarget: dropdown,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('doesnt hides the dropdown if mouseleave the trigger', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
        toggleOnHover: true,
        hideOnLeaveTimeout: null,
      },
    });

    const triggerButton = wrapper.get('button');

    const { trigger } = wrapper.vm.$refs;

    await triggerButton.trigger('mouseleave', {
      relatedTarget: trigger,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('has a focus method that focus the trigger', async () => {
    const wrapper = mount(TDropdown);
    const { trigger } = wrapper.vm.$refs;

    const focusMock = jest.fn();
    trigger.focus = focusMock;

    wrapper.vm.focus();

    expect(focusMock).toHaveBeenCalled();
  });

  it('emits native button events', () => {
    const onClick = jest.fn();
    const onBlur = jest.fn();
    const onFocus = jest.fn();

    const wrapper = mount(TDropdown, {
      attrs: {
        onClick,
        onBlur,
        onFocus,
      },
    });

    const { trigger } = wrapper.vm.$refs;

    trigger.dispatchEvent(new MouseEvent('click'));
    expect(onClick).toHaveBeenCalled();

    trigger.dispatchEvent(new FocusEvent('focus'));
    expect(onFocus).toHaveBeenCalled();

    trigger.dispatchEvent(new FocusEvent('blur'));
    expect(onBlur).toHaveBeenCalled();
  });

  it('triggers custom events', async () => {
    const onCustom = jest.fn();

    const wrapper = mount(TDropdown, {
      attrs: {
        onCustom,
      },
    });
    const { trigger } = wrapper.vm.$refs;

    const evt = new CustomEvent('custom', { detail: 'my-custom-event' });
    trigger.dispatchEvent(evt);

    expect(onCustom).toHaveBeenCalled();
  });

  it('display the dropdown if `show` prop is set ', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
      },
    });

    const { dropdown } = wrapper.vm.$refs;

    expect(wrapper.vm.shown).toBe(true);
    expect(dropdown.style.display).toBe('');
  });

  it('emits `update:show` when show property is updated', async () => {
    const wrapper = mount(TDropdown);

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();

    await wrapper.vm.$nextTick();

    // assert event has been emitted
    expect(wrapper.emitted()['update:show']).toBeTruthy();

    // assert event payload
    expect(wrapper.emitted()['update:show']).toEqual([[true]]);

    wrapper.vm.doHide();

    await wrapper.vm.$nextTick();

    // assert event has been emitted
    expect(wrapper.emitted()['update:show']).toBeTruthy();

    // assert event payload
    expect(wrapper.emitted()['update:show']).toEqual([[true], [false]]);
  });

  it('shows the modal if the `show` props changes', async () => {
    const wrapper = mount(TDropdown);

    await wrapper.setProps({
      show: true,
    });

    expect(wrapper.vm.shown).toBe(true);
  });

  it('hides the modal if the `show` props changes', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        show: true,
      },
    });

    await wrapper.setProps({
      show: false,
    });

    expect(wrapper.vm.shown).toBe(false);
  });

  it('clears the hidetimeout when unmounted', async () => {
    jest.useFakeTimers();

    const jestMock = jest.fn();

    const wrapper = mount(TDropdown);

    wrapper.vm.hideTimeout = setTimeout(jestMock, 100);

    wrapper.unmount();

    jest.advanceTimersByTime(100);

    expect(jestMock).not.toHaveBeenCalled();

    jest.useRealTimers();
  });

  it('invalidates invalid dropdown placements', () => {
    const { validator } = TDropdown.props.placement;
    expect(validator('invalid')).toBe(false);
  });

  it.each([
    'auto',
    'auto-start',
    'auto-end',
    'top',
    'top-start',
    'top-end',
    'bottom',
    'bottom-start',
    'bottom-end',
    'right',
    'right-start',
    'right-end',
    'left',
    'left-start',
    'left-end',
  ])('accept valid dropdown placements', (placement) => {
    const { validator } = TDropdown.props.placement;
    expect(validator(placement)).toBe(true);
  });

  it('has a default the popper configuration', async () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.vm.popperOptions).toEqual(TDropdownPopperDefaultOptions);
  });

  it('assigns the default popper configuration if undefined', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        popperOptions: undefined,
      },
    });

    expect(wrapper.vm.popperOptions).toEqual(TDropdownPopperDefaultOptions);
  });

  it('the dropdownAfterLeave method removes the `visibility` property', async () => {
    const wrapper = mount(TDropdown);

    const { dropdown } = wrapper.vm.$refs;

    dropdown.style.visibility = 'hidden';

    expect(dropdown.style.visibility).toBe('hidden');

    wrapper.vm.dropdownAfterLeave();

    expect(dropdown.style.visibility).toBe('');
  });

  describe('touch-only devices', () => {
    let windowSpy: any;

    beforeAll(() => {
      windowSpy = jest.spyOn(window, 'window', 'get');
      const windowImplementation = Object.assign(window, {
        matchMedia: () => ({
          matches: true,
        }),
      });

      windowSpy.mockImplementation(() => windowImplementation);
    });

    afterAll(() => {
      windowSpy.mockRestore();
    });

    it('detects touch only devces', () => {
      expect(window.matchMedia('(any-hover: none)')).toEqual({
        matches: true,
      });

      const wrapper = mount(TDropdown);

      expect(wrapper.vm.isTouchOnlyDevice).toBe(true);
    });

    it('ignores mouseoverHandler action in touch-only devices', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnHover: true,
        },
      });

      wrapper.vm.isTouchOnlyDevice = true;

      const action = jest.spyOn(wrapper.vm, 'doShow');

      const trigger = wrapper.get('button');

      await trigger.trigger('mouseover');

      expect(action).not.toHaveBeenCalled();
    });

    it('ignores mouseleaveHandler action in touch-only devices', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnHover: true,
        },
      });

      wrapper.vm.isTouchOnlyDevice = true;

      const action = jest.spyOn(wrapper.vm, 'targetIsChild');

      const trigger = wrapper.get('button');

      await trigger.trigger('mouseleave');

      expect(action).not.toHaveBeenCalled();
    });

    it('ignores focusHandler action in touch-only devices', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: true,
        },
      });

      const trigger = wrapper.get('button');

      await trigger.trigger('focus');

      expect(wrapper.vm.shown).toBe(false);
    });

    it('ignores blurHandler action in touch-only devices', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: true,
        },
      });

      // doHide should not be called
      const action = jest.spyOn(wrapper.vm, 'doHide');

      const trigger = wrapper.get('button');

      await trigger.trigger('blur');

      expect(action).not.toHaveBeenCalled();
    });

    it('shows the dropdown when clicked on touch-only devices if `toggleOnFocus` is set even if `toggleOnClick` is false', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: true,
          toggleOnClick: false,
        },
      });

      const trigger = wrapper.get('button');

      await trigger.trigger('click');

      expect(wrapper.vm.shown).toBe(true);
    });

    it('shows the dropdown when clicked on touch-only devices if `toggleOnHover` is set even if `toggleOnClick` is false', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnHover: true,
          toggleOnFocus: false,
          toggleOnClick: false,
        },
      });

      const trigger = wrapper.get('button');

      await trigger.trigger('click');

      expect(wrapper.vm.shown).toBe(true);
    });

    it('adds the `touchstartHandler` to the current window when dropdown is shown and is isTouchOnlyDevice', async () => {
      const wrapper = mount(TDropdown);

      const addSpy = jest.spyOn(window, 'addEventListener');
      const removeSpy = jest.spyOn(window, 'removeEventListener');

      wrapper.vm.doShow();

      await wrapper.vm.$nextTick();

      expect(addSpy).toHaveBeenCalledWith('touchstart', wrapper.vm.touchstartHandler);

      wrapper.vm.doHide();

      await wrapper.vm.$nextTick();

      expect(removeSpy).toHaveBeenCalledWith('touchstart', wrapper.vm.touchstartHandler);
    });

    it('adds the `touchstartHandler` if the component is shown when mounted', async () => {
      const addSpy = jest.spyOn(window, 'addEventListener');

      const wrapper = mount(TDropdown, {
        props: {
          show: true,
        },
      });

      expect(addSpy).toHaveBeenCalledWith('touchstart', wrapper.vm.touchstartHandler);
    });

    it('removes the `touchstartHandler` if the component when component is unmounted', async () => {
      const removeSpy = jest.spyOn(window, 'removeEventListener');

      const wrapper = mount(TDropdown, {
        props: {
          show: true,
        },
      });

      wrapper.unmount();

      expect(removeSpy).toHaveBeenCalledWith('touchstart', wrapper.vm.touchstartHandler);
    });

    it('hides the dropdown if toggle on focus is set and when touch outside', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: true,
          toggleOnClick: false,
          show: true,
        },
      });

      window.dispatchEvent(new TouchEvent('touchstart'));

      await wrapper.vm.$nextTick();

      expect(wrapper.vm.shown).toBe(false);
    });

    it('doesnt hides the dropdown if touch a children even if toggle on focus is set', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: true,
          toggleOnClick: false,
          show: true,
        },
      });

      window.dispatchEvent(new TouchEvent('touchstart', {
        targetTouches: [
          {
            identifier: 1,
            target: wrapper.vm.$refs.dropdown as EventTarget,
          } as Touch,
        ],
      }));

      await wrapper.vm.$nextTick();

      expect(wrapper.vm.shown).toBe(true);
    });

    it('hides the dropdown if toggle on hover is set and when touch outside', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: false,
          toggleOnClick: false,
          toggleOnHover: true,
          show: true,
        },
      });

      window.dispatchEvent(new TouchEvent('touchstart'));

      await wrapper.vm.$nextTick();

      expect(wrapper.vm.shown).toBe(false);
    });

    it('doesnt hides the dropdown if toggle on hover and toggle on focus is not set when touch outside', async () => {
      const wrapper = mount(TDropdown, {
        props: {
          toggleOnFocus: false,
          toggleOnClick: false,
          toggleOnHover: false,
          show: true,
        },
      });

      window.dispatchEvent(new TouchEvent('touchstart'));

      await wrapper.vm.$nextTick();

      expect(wrapper.vm.shown).toBe(true);
    });

    it('calls the `enablePopperNeedsAdjustmentListener` when popperIsAdjusted is set', async () => {
      const wrapper = mount(TDropdown);

      const enablePopperNeedsAdjustmentListenerSpy = jest.spyOn(wrapper.vm, 'enablePopperNeedsAdjustmentListener');

      wrapper.vm.popperIsAdjusted = true;

      await wrapper.vm.$nextTick();

      expect(enablePopperNeedsAdjustmentListenerSpy).toHaveBeenCalled();
    });

    it('calls the `disablePopperNeedsAdjustmentListener` when popperIsAdjusted is set to false', async () => {
      const wrapper = mount(TDropdown);

      const disablePopperNeedsAdjustmentListenerSpy = jest.spyOn(wrapper.vm, 'disablePopperNeedsAdjustmentListener');

      wrapper.vm.popperIsAdjusted = true;

      await wrapper.vm.$nextTick();

      wrapper.vm.popperIsAdjusted = false;

      await wrapper.vm.$nextTick();

      expect(disablePopperNeedsAdjustmentListenerSpy).toHaveBeenCalled();
    });

    it('set popperIsAdjusted to false when scroll event after is adjusted', async () => {
      jest.useFakeTimers();

      const wrapper = mount(TDropdown);

      wrapper.vm.popperIsAdjusted = true;

      await wrapper.vm.$nextTick();

      window.dispatchEvent(new Event('scroll'));

      expect(wrapper.vm.popperIsAdjusted).toBe(true);

      // need to wait because is throttled
      jest.advanceTimersByTime(200);

      expect(wrapper.vm.popperIsAdjusted).toBe(false);

      jest.useRealTimers();
    });

    it('set popperIsAdjusted to false when resize event after is adjusted', async () => {
      jest.useFakeTimers();

      const wrapper = mount(TDropdown);

      wrapper.vm.popperIsAdjusted = true;

      await wrapper.vm.$nextTick();

      window.dispatchEvent(new Event('resize'));

      expect(wrapper.vm.popperIsAdjusted).toBe(true);

      // need to wait because is throttled
      jest.advanceTimersByTime(200);

      expect(wrapper.vm.popperIsAdjusted).toBe(false);

      jest.useRealTimers();
    });

    it('removes the listener to resize and scroll after component is unmounted', async () => {
      const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');

      const wrapper = mount(TDropdown);

      wrapper.unmount();

      expect(removeEventListenerSpy).toHaveBeenCalledWith('resize', wrapper.vm.popperAdjusterListener);
      expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', wrapper.vm.popperAdjusterListener);
    });
  });
});

describe('TDropdown popper instance', () => {
  const popperWasCreated = async (wrapper: VueWrapper<any>) => {
    do {
      // eslint-disable-next-line no-await-in-loop
      await wrapper.vm.$nextTick();
    } while (wrapper.vm.popper === null);

    return Promise.resolve();
  };

  const popperIsAdjusted = async (wrapper: VueWrapper<any>) => {
    do {
      // eslint-disable-next-line no-await-in-loop
      await wrapper.vm.$nextTick();
    } while (wrapper.vm.popperIsAdjusted === false);

    return Promise.resolve();
  };

  it('creates a popper instance when shown', async () => {
    const wrapper = mount(TDropdown);

    expect(wrapper.vm.popper).toBeNull();

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    expect(wrapper.vm.popper).toBeTruthy();

    await popperIsAdjusted(wrapper);

    expect(wrapper.vm.popperIsAdjusted).toBe(true);
  });

  it('doesnt create popper if already exists', async () => {
    const wrapper = mount(TDropdown);

    const createPopperSpy = jest.spyOn(wrapper.vm, 'createPopper');

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    expect(createPopperSpy).toHaveBeenCalledTimes(1);

    expect(wrapper.vm.popper).toBeTruthy();

    wrapper.vm.doHide();

    await wrapper.vm.$nextTick();

    expect(wrapper.vm.shown).toBe(false);

    expect(createPopperSpy).toHaveBeenCalledTimes(1);
  });

  it('adjust popper if marked as not adjusted', async () => {
    const wrapper = mount(TDropdown);

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    const popperUpdateSpy = jest.spyOn(wrapper.vm.popper, 'update');

    wrapper.vm.doHide();

    wrapper.vm.popperIsAdjusted = false;

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).toHaveBeenCalled();
  });

  it('doesnt adjust popper if marked as adjusted', async () => {
    const wrapper = mount(TDropdown);

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    const popperUpdateSpy = jest.spyOn(wrapper.vm.popper, 'update');

    wrapper.vm.doHide();

    wrapper.vm.popperIsAdjusted = true;

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).not.toHaveBeenCalled();

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).not.toHaveBeenCalled();
  });

  it('doesnt adjust popper if marked as adjusted (method check)', async () => {
    const wrapper = mount(TDropdown);

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    const popperUpdateSpy = jest.spyOn(wrapper.vm, 'adjustPopper');

    wrapper.vm.doHide();

    wrapper.vm.popperIsAdjusted = true;

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).not.toHaveBeenCalled();

    wrapper.vm.doShow();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).not.toHaveBeenCalled();
  });

  it('doesnt adjust popper if is hidden and adjustingPopper is false', async () => {
    const wrapper = mount(TDropdown);

    // Show the dropdown to initialize popper
    wrapper.vm.doShow();
    await popperWasCreated(wrapper);
    wrapper.vm.doHide();
    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    wrapper.vm.adjustingPopper = false;

    const popperUpdateSpy = jest.spyOn(wrapper.vm.popper, 'update');

    await wrapper.vm.adjustPopper();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).not.toHaveBeenCalled();
  });

  it('adjust popper if is hidden but adjustingPopper is true', async () => {
    const wrapper = mount(TDropdown);

    // Show the dropdown to initialize popper
    wrapper.vm.doShow();
    await popperWasCreated(wrapper);
    wrapper.vm.doHide();
    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    wrapper.vm.adjustingPopper = true;

    const popperUpdateSpy = jest.spyOn(wrapper.vm.popper, 'update');

    await wrapper.vm.adjustPopper();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).toHaveBeenCalled();
  });

  it('adjust popper if not is hidden and adjustingPopper is false', async () => {
    const wrapper = mount(TDropdown);

    // Show the dropdown to initialize popper
    wrapper.vm.doShow();
    await popperWasCreated(wrapper);
    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    wrapper.vm.adjustingPopper = true;

    const popperUpdateSpy = jest.spyOn(wrapper.vm.popper, 'update');

    await wrapper.vm.adjustPopper();

    await wrapper.vm.$nextTick();

    expect(popperUpdateSpy).toHaveBeenCalled();
  });

  it('accepts undefined as the placement', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        placement: undefined,
      },
    });

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    expect(wrapper.vm.popper).toBeTruthy();
    expect(wrapper.vm.popper.state.placement).toBe(TDropdownPopperDefaultOptions.placement);
  });

  it('overrides the popper placement if placement is set', async () => {
    const wrapper = mount(TDropdown, {
      props: {
        placement: 'top',
      },
    });

    wrapper.vm.doShow();

    await popperWasCreated(wrapper);

    expect(wrapper.vm.popper).toBeTruthy();
    expect(wrapper.vm.popper.state.placement).toBe('top');
  });
});


================================================
FILE: src/__tests/components/TInput.integration.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { render, fireEvent } from '@testing-library/vue';
import TInput from '@/components/TInput.vue';

describe('TInput.vue', () => {
  it('handles the v-model', async () => {
    const { container, getByDisplayValue } = render(TInput);

    const input = container.querySelector('input')!;

    await fireEvent.update(input!, 'Alfonso');

    getByDisplayValue('Alfonso');
  });

  it('contains the class + classes + fixedClasses', async () => {
    const { container } = render(TInput, {
      props: {
        fixedClasses: 'text-red-500',
        classes: 'border-red-500',
        class: 'font-semibold',
      },
    });

    const input = container.querySelector('.text-red-500.border-red-500.font-semibold');
    expect(input).not.toBeNull();
  });

  it('adds the html attributes', async () => {
    const { getByPlaceholderText, getByRole, getByTitle } = render(TInput, {
      props: {
        placeholder: 'Write something',
        role: 'text-field',
        title: 'my title',
      },
    });

    getByPlaceholderText('Write something');

    getByRole('text-field');

    getByTitle('my title');
  });

  it('adds the classes on the variant', async () => {
    const { container } = render(TInput, {
      props: {
        variants: {
          error: {
            classes: 'text-red-500',
          },
        },
        variant: 'error',
        classes: 'text-blue-500',
      },
    });

    let input = container.querySelector('.text-red-500');
    expect(input).not.toBeNull();

    input = container.querySelector('.text-blue-500');
    expect(input).toBeNull();
  });

  it('keeps the fixedClasses when using a variant', async () => {
    const { container } = render(TInput, {
      props: {
        variants: {
          error: {
            classes: 'text-red-500',
          },
        },
        variant: 'error',
        fixedClasses: 'text-blue-500',
      },
    });

    let input = container.querySelector('.text-red-500');
    expect(input).not.toBeNull();

    input = container.querySelector('.text-blue-500');
    expect(input).not.toBeNull();
  });

  it('overrides the fixedClasses when using a variant', async () => {
    const { container } = render(TInput, {
      props: {
        variants: {
          error: {
            fixedClasses: 'text-red-500',
          },
        },
        variant: 'error',
        fixedClasses: 'text-blue-500',
      },
    });

    let input = container.querySelector('.text-red-500');
    expect(input).not.toBeNull();

    input = container.querySelector('.text-blue-500');
    expect(input).toBeNull();
  });
});


================================================
FILE: src/__tests/components/TInput.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */

import { shallowMount } from '@vue/test-utils';
import { TInputConfig } from '@variantjs/core';
import TInput from '@/components/TInput.vue';

describe('TInput.vue', () => {
  it('renders the input', () => {
    const wrapper = shallowMount(TInput);
    expect(wrapper.get('input')).toBeTruthy();
  });

  it('renders the input with a default set of classes', () => {
    const wrapper = shallowMount(TInput);

    expect(wrapper.html()).toBe(`<input class="${TInputConfig.classes}">`);
  });

  it('adds the value attribute', () => {
    const wrapper = shallowMount(TInput,
      {
        global: {
          provide: {
            configuration: {
              TInput: {
                classes: undefined,
              },
            },
          },
        },
        attrs: {
          value: 'foo bar',
        },
      });

    expect(wrapper.vm.$el.value).toBe('foo bar');
  });

  it('renders the input without attributes if no default theme', () => {
    const wrapper = shallowMount(TInput, {
      global: {
        provide: {
          configuration: {
            TInput: {
              classes: undefined,
            },
          },
        },
      },
    });

    expect(wrapper.html()).toBe('<input>');
  });

  it('set the props.value into the input value', () => {
    const value = 'input value';
    const wrapper = shallowMount(TInput, {
      props: { modelValue: value },
    });

    expect(wrapper.vm.$el.value).toBe(value);
  });

  it('doesnt add the modelValue as attribute', () => {
    const value = 'input value';
    const wrapper = shallowMount(TInput, {
      props: { modelValue: value },
    });

    expect(wrapper.vm.$el.attributes.modelValue).toBeUndefined();
  });

  it('disables the input', async () => {
    const wrapper = shallowMount(TInput, {
      props: { disabled: false },
    });
    expect(wrapper.vm.$el.disabled).toBe(false);

    await wrapper.setProps({ disabled: true });

    expect(wrapper.vm.$el.disabled).toBe(true);
  });

  it('has input attributes', async () => {
    const wrapper = shallowMount(TInput);

    const values = {
      id: {
        default: '',
        new: 'new-id',
      },
      autocomplete: {
        default: '',
        new: 'on',
      },
      autofocus: {
        default: false,
        new: true,
      },
      disabled: {
        default: false,
        new: true,
      },
      max: {
        default: '',
        new: '10',
      },
      maxlength: {
        keyName: 'maxLength',
        default: 524288,
        new: 12,
      },
      minlength: {
        keyName: 'minLength',
        default: 0,
        new: 2,
      },
      min: {
        default: '',
        new: '3',
      },
      multiple: {
        default: false,
        new: true,
      },
      name: {
        default: '',
        new: 'new-name',
      },
      pattern: {
        default: '',
        new: '[A-Za-z]{3}',
      },
      placeholder: {
        default: '',
        new: 'new placeholder',
      },
      readonly: {
        keyName: 'readOnly',
        default: false,
        new: true,
      },
      required: {
        default: false,
        new: true,
      },
      value: {
        default: '',
        new: 'my value',
      },
      type: {
        default: 'text',
        new: 'email',
      },
    };

    const newProps: any = {};
    // Check for the default values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.default);

      newProps[key as any] = elementValue.new;
    });

    await wrapper.setProps(newProps);

    // Check for the new values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.new);
    });
  });

  it('set the model value', () => {
    const modelValue = 'original value';

    const wrapper = shallowMount(TInput, {
      props: {
        modelValue,
      },
    });

    expect(wrapper.vm.$el.value).toBe(modelValue);
  });

  it('emits an update event with the input value', () => {
    const modelValue = 'original value';

    const wrapper = shallowMount(TInput, {
      props: {
        modelValue,
      },
    });

    const inputValue = 'new value';

    wrapper.setValue(inputValue);

    expect(wrapper.emitted('update:modelValue')).toBeTruthy();

    // assert event count
    expect(wrapper.emitted('update:modelValue')?.length).toBe(1);

    // assert event payload
    expect(wrapper.emitted('update:modelValue')![0]).toEqual([inputValue]);
  });

  it('emits native input events', () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();
    const onFocus = jest.fn();
    const onKeyup = jest.fn();
    const onInput = jest.fn();

    const wrapper = shallowMount(TInput, {
      attrs: {
        onChange,
        onBlur,
        onFocus,
        onKeyup,
        onInput,
      },
    });

    const input = wrapper.vm.$el;

    input.dispatchEvent(new Event('change'));
    expect(onChange).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('focus'));
    expect(onFocus).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('blur'));
    expect(onBlur).toHaveBeenCalled();

    input.dispatchEvent(new InputEvent('input'));
    expect(onInput).toHaveBeenCalled();

    input.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' }));
    expect(onKeyup).toHaveBeenCalled();
  });

  it('has native input methods', () => {
    const wrapper = shallowMount(TInput);

    const input = wrapper.vm.$el;

    expect(typeof input.click).toBe('function');
    expect(typeof input.select).toBe('function');
    expect(typeof input.setSelectionRange).toBe('function');
    expect(typeof input.setRangeText).toBe('function');
  });

  it('triggers custom events', async () => {
    const onCustom = jest.fn();

    const wrapper = shallowMount(TInput, {
      attrs: {
        onCustom,
      },
    });
    const input = wrapper.vm.$el as HTMLInputElement;

    const evt = new CustomEvent('custom', { detail: 'my-custom-event' });
    input.dispatchEvent(evt);

    expect(onCustom).toHaveBeenCalled();
  });
});


================================================
FILE: src/__tests/components/TInputGroup.spec.ts
================================================
import { shallowMount } from '@vue/test-utils';
import { TInputGroupConfig } from '@variantjs/core';
import TInputGroup from '@/components/TInputGroup.vue';

describe('TInputGroup.vue', () => {
  it('renders the component without errors', () => {
    const wrapper = shallowMount(TInputGroup);
    expect(wrapper.vm.$el.tagName).toBe('DIV');
  });

  it('adds the elements in default order', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
      slots: {
        default: 'the body',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(els.length).toBe(4);
    expect(label.innerHTML).toBe('the label');
    expect(body.innerHTML).toBe('the body');
    expect(feedback.innerHTML).toBe('the feedback');
    expect(description.innerHTML).toBe('the description');
  });

  it('adds the elements from the slots', () => {
    const wrapper = shallowMount(TInputGroup, {
      slots: {
        default: 'the body',
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(els.length).toBe(4);
    expect(label.innerHTML).toBe('the label');
    expect(body.innerHTML).toBe('the body');
    expect(feedback.innerHTML).toBe('the feedback');
    expect(description.innerHTML).toBe('the description');
  });

  it('doesnt add empty elements', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        label: 'the label',
      },
      slots: {
        default: 'the body',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];

    expect(els.length).toBe(2);
    expect(label.innerHTML).toBe('the label');
    expect(body.innerHTML).toBe('the body');
  });

  it('adds only the elements defined on the sortedElements prop', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        sortedElements: [
          'label', 'description',
        ],
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
      slots: {
        default: 'the body',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const description = els[1];

    expect(els.length).toBe(2);
    expect(label.innerHTML).toBe('the label');
    expect(description.innerHTML).toBe('the description');
  });

  it('adds the elements in different order', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        sortedElements: [
          'feedback', 'description', 'label', 'default',
        ],
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
      slots: {
        default: 'the body',
      },
    });

    const els = wrapper.vm.$el.children;
    const feedback = els[0];
    const description = els[1];
    const label = els[2];
    const body = els[3];

    expect(els.length).toBe(4);
    expect(label.innerHTML).toBe('the label');
    expect(body.innerHTML).toBe('the body');
    expect(feedback.innerHTML).toBe('the feedback');
    expect(description.innerHTML).toBe('the description');
  });

  it('prioritizes the slots over the props', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        body: 'not',
        label: 'not',
        feedback: 'not',
        description: 'not',
      },
      slots: {
        default: 'the body',
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(els.length).toBe(4);
    expect(label.innerHTML).toBe('the label');
    expect(body.innerHTML).toBe('the body');
    expect(feedback.innerHTML).toBe('the feedback');
    expect(description.innerHTML).toBe('the description');
  });

  it('adds the text on the body props to the default slot', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        body: 'from the prop',
      },
    });

    const els = wrapper.vm.$el.children;
    const body = els[0];

    expect(els.length).toBe(1);
    expect(body.innerHTML).toBe('from the prop');
  });

  it('uses default tagNames for elements', () => {
    const wrapper = shallowMount(TInputGroup, {
      slots: {
        default: 'the body',
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
    });

    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(label.tagName).toBe('LABEL');
    expect(body.tagName).toBe('DIV');
    expect(feedback.tagName).toBe('DIV');
    expect(description.tagName).toBe('DIV');
  });

  it('accepts different tagNames for elements', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        tagName: 'fieldset',
        bodyTagName: 'table',
        labelTagName: 'span',
        feedbackTagName: 'a',
        descriptionTagName: 'p',
      },
      slots: {
        default: 'the body',
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
    });

    const wrap = wrapper.vm.$el;
    const els = wrap.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(wrap.tagName).toBe('FIELDSET');
    expect(label.tagName).toBe('SPAN');
    expect(body.tagName).toBe('TABLE');
    expect(feedback.tagName).toBe('A');
    expect(description.tagName).toBe('P');
  });

  it('uses a `div` for the element', () => {
    const wrapper = shallowMount(TInputGroup, {
      props: {
        label: 'im a label',
      },
    });

    expect(wrapper.vm.$el.children[0].tagName).toBe('LABEL');
  });

  it('has a default theme', () => {
    const wrapper = shallowMount(TInputGroup, {
      slots: {
        default: 'the body',
        label: 'the label',
        feedback: 'the feedback',
        description: 'the description',
      },
    });

    const wrap = wrapper.vm.$el;
    const els = wrapper.vm.$el.children;
    const label = els[0];
    const body = els[1];
    const feedback = els[2];
    const description = els[3];

    expect(wrap.className).toBe(TInputGroupConfig.classes.wrapper);
    expect(label.className).toBe(TInputGroupConfig.classes.label);
    expect(body.className).toBe(TInputGroupConfig.classes.body);
    expect(feedback.className).toBe(TInputGroupConfig.classes.feedback);
    expect(description.className).toBe(TInputGroupConfig.classes.description);
  });

  it('adds html attributes', () => {
    const wrapper = shallowMount(TInputGroup, {
      attrs: {
        id: 'my-id',
      },
    });

    const wrap = wrapper.vm.$el as HTMLDivElement;

    expect(wrap.getAttribute('id')).toBe('my-id');
  });

  it('adds attributes from global configuration', () => {
    const wrapper = shallowMount(TInputGroup, {
      global: {
        provide: {
          configuration: {
            TInputGroup: {
              id: 'my-id',
            },
          },
        },
      },
    });

    const wrap = wrapper.vm.$el as HTMLDivElement;

    expect(wrap.getAttribute('id')).toBe('my-id');
  });

  it('used the props from global configuration', () => {
    const wrapper = shallowMount(TInputGroup, {
      global: {
        provide: {
          configuration: {
            TInputGroup: {
              description: 'hello hello',
            },
          },
        },
      },
    });

    expect(wrapper.vm.$el.querySelector('div').innerHTML).toBe('hello hello');
  });
});


================================================
FILE: src/__tests/components/TModal.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { shallowMount, mount } from '@vue/test-utils';
import * as bodyScrollLockModule from 'body-scroll-lock';
import { TModalConfig } from '@variantjs/core';
import TModal from '@/components/TModal.vue';
import plugin from '../../plugin';

const waitUntilModalIsVisible = (wrapper: any) : Promise<void> => new Promise((resolve) => {
  // 1. Component is added to the DOM
  wrapper.vm.$nextTick().then(() => {
    // 2. Overlay is about to show
    wrapper.vm.$nextTick().then(() => {
      // 3. Overlay is shown, modal is about to show
      wrapper.vm.$nextTick().then(() => {
        // 4 Modal is shown
        wrapper.vm.$nextTick().then(() => {
          resolve();
        });
      });
    });
  });
});

describe('TModal.vue', () => {
  it('doesnt show the component by default', () => {
    const wrapper = shallowMount(TModal);
    expect(wrapper.vm.$el.tagName).toBeUndefined();
  });

  describe('opening modal', () => {
    const props = {
      teleport: false,
    };

    describe('with the `show` method', () => {
      it('show the component when calling the show method', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.vm.show();

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(true);
      });

      it('pass parameters from the show method to the before-show event', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        const params = {
          foo: 'bar',
        };

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.vm.show(params);

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(true);

        expect((wrapper.emitted('before-show')![0] as any)[0].params).toEqual(params);
      });

      it('cancel the show if the cancel method is called', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
          },
        });

        const emitSpy = jest.spyOn(wrapper.vm.$, 'emit').mockImplementation((name, ...params) => {
          if (name === 'before-show') {
            (params as any).cancel();
          }
        });

        wrapper.vm.show();

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(false);

        emitSpy.mockRestore();
      });
    });

    describe('with the $modal global property', () => {
      it('show the component when calling the show method', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'modal-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.vm.$modal.show('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(true);
      });

      it('doesnt show the component when calling the show method if different name', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'other-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.vm.$modal.show('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(false);
      });

      it('pass parameters from the show method to the before-show event', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'modal-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        const params = {
          foo: 'bar',
        };

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.vm.$modal.show('modal-name', params);

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(true);

        expect((wrapper.emitted('before-show')![0] as any)[0].params).toEqual(params);
      });

      it('cancel the show if the cancel method is called', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'modal-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        const emitSpy = jest.spyOn(wrapper.vm.$, 'emit').mockImplementation((name, ...params) => {
          if (name === 'before-show') {
            (params as any).cancel();
          }
        });

        wrapper.vm.$modal.show('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(false);

        emitSpy.mockRestore();
      });
    });

    describe('with the vModel', () => {
      it('show the component when updated the v-model to true', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        expect(wrapper.vm.showComponent).toBe(false);

        wrapper.setProps({
          modelValue: true,
        });

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showComponent).toBe(true);
      });
    });
  });

  describe('hiding modal', () => {
    const props = {
      teleport: false,
      modelValue: true,
    };

    describe('with the `hide` method', () => {
      it('hides the component when calling the hide method', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        expect(wrapper.vm.showModal).toBe(true);

        wrapper.vm.hide();

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(false);
      });

      it('cancel the hide if the cancel method is called', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        const emitSpy = jest.spyOn(wrapper.vm.$, 'emit').mockImplementation((name, ...params) => {
          if (name === 'before-hide') {
            (params as any).cancel();
          }
        });

        wrapper.vm.hide();

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(true);

        emitSpy.mockRestore();
      });
    });

    describe('with the $modal global property', () => {
      it('show the component when calling the show method', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'modal-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        expect(wrapper.vm.showModal).toBe(true);

        wrapper.vm.$modal.hide('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(false);
      });

      it('doesnt show the component when calling the show method if different name', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'other-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        expect(wrapper.vm.showModal).toBe(true);

        wrapper.vm.$modal.hide('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(true);
      });

      it('cancel the show if the cancel method is called', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            name: 'modal-name',
          },
          global: {
            plugins: [plugin],
          },
        });

        const emitSpy = jest.spyOn(wrapper.vm.$, 'emit').mockImplementation((name, ...params) => {
          if (name === 'before-hide') {
            (params as any).cancel();
          }
        });

        wrapper.vm.$modal.hide('modal-name');

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(true);

        emitSpy.mockRestore();
      });
    });

    describe('with the vModel', () => {
      it('hides the component when updated the v-model to false', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        expect(wrapper.vm.showModal).toBe(true);

        wrapper.setProps({
          modelValue: false,
        });

        await wrapper.vm.$nextTick();

        expect(wrapper.vm.showModal).toBe(false);
      });
    });
  });

  describe('modal is shown initially', () => {
    const props = {
      modelValue: true,
      teleport: false,
    };

    it('show the component if modelValue is set to `true`', () => {
      const wrapper = mount(TModal, {
        props,
        slots: {
          default: 'Hello World',
        },
      });

      expect(wrapper.html()).toContain('Hello World');
    });

    describe('disable body scroll', () => {
      it('disables the body scroll', () => {
        const disableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'disableBodyScroll');

        mount(TModal, {
          props,
        });

        expect(disableBodyScrollSpy).toHaveBeenCalled();

        disableBodyScrollSpy.mockRestore();
      });

      it('doesnt disabled the body scroll if `disableBodyScroll` is set to `false`', () => {
        const disableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'disableBodyScroll');

        mount(TModal, {
          props: {
            ...props,
            disableBodyScroll: false,
          },
        });

        expect(disableBodyScrollSpy).not.toHaveBeenCalled();

        disableBodyScrollSpy.mockRestore();
      });
    });

    describe('enable body scroll', () => {
      it('enables body scroll when unmounted', () => {
        const enableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'enableBodyScroll');

        const wrapper = mount(TModal, {
          props,
        });

        wrapper.unmount();

        expect(enableBodyScrollSpy).toHaveBeenCalled();

        enableBodyScrollSpy.mockRestore();
      });

      it('doesnt enables body scroll when unmounted if `disableBodyScroll` is set to `false`', () => {
        const enableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'enableBodyScroll');

        const wrapper = mount(TModal, {
          props: {
            ...props,
            disableBodyScroll: false,
          },
        });

        wrapper.unmount();

        expect(enableBodyScrollSpy).not.toHaveBeenCalled();

        enableBodyScrollSpy.mockRestore();
      });

      it('enables body scroll when modal is closed', async () => {
        const enableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'enableBodyScroll');

        const wrapper = mount(TModal, {
          props,
        });

        wrapper.vm.hide();

        // Whole close lifecycle
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(enableBodyScrollSpy).toHaveBeenCalled();

        enableBodyScrollSpy.mockRestore();
      });

      it('doesnt enables body scroll when modal is closed if `disableBodyScroll` is set to `false`', async () => {
        const enableBodyScrollSpy = jest.spyOn(bodyScrollLockModule, 'enableBodyScroll');

        const wrapper = mount(TModal, {
          props: {
            ...props,
            disableBodyScroll: false,
          },
        });

        wrapper.vm.hide();

        // Whole close lifecycle
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();
        await wrapper.vm.$nextTick();

        expect(enableBodyScrollSpy).not.toHaveBeenCalled();

        enableBodyScrollSpy.mockRestore();
      });
    });

    describe('focus overlay', () => {
      it('focus the overlay', () => {
        const focusSpy = jest.spyOn(HTMLElement.prototype, 'focus');

        mount(TModal, {
          props,
        });

        expect(focusSpy).toHaveBeenCalled();

        focusSpy.mockRestore();
      });

      it('doesnt focus the overlay if `focusOnOpen` is set to `false`', () => {
        const focusSpy = jest.spyOn(HTMLElement.prototype, 'focus');

        mount(TModal, {
          props: {
            ...props,
            focusOnOpen: false,
          },
        });

        expect(focusSpy).not.toHaveBeenCalled();

        focusSpy.mockRestore();
      });
    });

    it('emits the hide-related events in order', async () => {
      const wrapper = mount(TModal, {
        props,
      });

      expect(wrapper.emitted('before-hide')).toBeFalsy();
      expect(wrapper.emitted('hidden')).toBeFalsy();

      wrapper.vm.hide();

      // After press hidden it just change the modelValue, no events yet
      expect(wrapper.emitted('before-hide')).toBeFalsy();
      expect(wrapper.emitted('hidden')).toBeFalsy();

      // Model is about to hide
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-hide')).toBeTruthy();
      expect(Object.keys((wrapper.emitted('before-hide')![0] as any)[0])).toEqual([
        'cancel',
        'reason',
      ]);
      expect(wrapper.emitted('hidden')).toBeFalsy();

      // Modal is hidden, overlay is about to hide (no new events emitted)
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-hide')).toBeTruthy();
      expect(wrapper.emitted('hidden')).toBeFalsy();

      // Overlay is hidden, component is about to be removed from the DOM (no new events emitted)
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-hide')).toBeTruthy();
      expect(wrapper.emitted('hidden')).toBeFalsy();

      // component is removed from the DOM
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-hide')).toBeTruthy();
      expect(wrapper.emitted('hidden')).toBeTruthy();
    });

    describe('press esc key', () => {
      it('hides the modal when press esc', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        wrapper.vm.$refs.overlay.dispatchEvent(new KeyboardEvent('keydown', {
          key: 'Escape',
        }));

        await wrapper.vm.$nextTick();

        // Meaning the modal was hidden
        expect(wrapper.vm.$.setupState.showModal).toBe(false);
      });

      it('doesnt hide the modal when press esc if `escToClose` is set to `false`', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            escToClose: false,
          },
        });

        wrapper.vm.$refs.overlay.dispatchEvent(new KeyboardEvent('keydown', {
          key: 'Escape',
        }));

        await wrapper.vm.$nextTick();

        // Meaning the modal was hidden
        expect(wrapper.vm.$.setupState.showModal).toBe(true);
      });
    });

    describe('user clicks the overlay (outside)', () => {
      it('hides the modal when clicks outside', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        wrapper.vm.$refs.overlay.dispatchEvent(new MouseEvent('click'));

        await wrapper.vm.$nextTick();

        // Meaning the modal was hidden
        expect(wrapper.vm.$.setupState.showModal).toBe(false);
      });

      it('doesnt hides the modal when clicks the modal', async () => {
        const wrapper = mount(TModal, {
          props,
        });

        wrapper.vm.$refs.modal.dispatchEvent(new MouseEvent('click'));

        await wrapper.vm.$nextTick();

        // Meaning the modal was hidden
        expect(wrapper.vm.$.setupState.showModal).toBe(true);
      });

      it('doesnt hide the modal when click outside if `clickToClose` is set to `false`', async () => {
        const wrapper = mount(TModal, {
          props: {
            ...props,
            clickToClose: false,
          },
        });

        wrapper.vm.$refs.overlay.dispatchEvent(new MouseEvent('click'));

        await wrapper.vm.$nextTick();

        // Meaning the modal was hidden
        expect(wrapper.vm.$.setupState.showModal).toBe(true);
      });
    });
  });

  describe('modal is hidden initially', () => {
    const props = {
      teleport: false,
    };

    it('focus the overlay when shown', async () => {
      const focusSpy = jest.spyOn(HTMLElement.prototype, 'focus');

      const wrapper = mount(TModal, {
        props,
      });

      expect(focusSpy).not.toHaveBeenCalled();

      wrapper.vm.show();

      await waitUntilModalIsVisible(wrapper);

      expect(focusSpy).toHaveBeenCalled();

      focusSpy.mockRestore();
    });

    it('emits the open-related events in order', async () => {
      const wrapper = mount(TModal, {
        props,
      });

      expect(wrapper.emitted('before-show')).toBeFalsy();
      expect(wrapper.emitted('shown')).toBeFalsy();

      wrapper.vm.show();

      // After press shown it just adds the component into the DOM, no events emitted yet
      expect(wrapper.emitted('before-show')).toBeFalsy();
      expect(wrapper.emitted('shown')).toBeFalsy();

      // Component is added to the DOM, but not shown yet
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-show')).toBeTruthy();
      expect(Object.keys((wrapper.emitted('before-show')![0] as any)[0])).toEqual([
        'cancel',
        'params',
      ]);
      expect(wrapper.emitted('shown')).toBeFalsy();

      // Overlay is about to show (no new events emitted)
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-show')).toBeTruthy();
      expect(wrapper.emitted('shown')).toBeFalsy();

      // Overlay is shown, modal is about to show (no new events emitted)
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-show')).toBeTruthy();
      expect(wrapper.emitted('shown')).toBeFalsy();

      // Modal is shown
      await wrapper.vm.$nextTick();
      expect(wrapper.emitted('before-show')).toBeTruthy();
      expect(wrapper.emitted('shown')).toBeTruthy();
    });
  });

  describe('classes', () => {
    const props = {
      teleport: false,
    };

    it('creates the overlayTransitionClassesList computed property from default configuration', () => {
      const wrapper = mount(TModal, {
        props,
      });

      expect(wrapper.vm.overlayTransitionClassesList).toEqual({
        enterActiveClass: TModalConfig.classes.overlayEnterActiveClass,
        enterFromClass: TModalConfig.classes.overlayEnterFromClass,
        enterToClass: TModalConfig.classes.overlayEnterToClass,
        leaveActiveClass: TModalConfig.classes.overlayLeaveActiveClass,
        leaveFromClass: TModalConfig.classes.overlayLeaveFromClass,
        leaveToClass: TModalConfig.classes.overlayLeaveToClass,
      });
    });
  });
});


================================================
FILE: src/__tests/components/TRadio.integration.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { render, fireEvent } from '@testing-library/vue';
import TRadio from '@/components/TRadio.vue';

describe('TRadio.vue', () => {
  it('handles the v-model', async () => {
    const modelValue = 'A';
    const { container } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: 'A',
      },
    });
    const { container: container2 } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: 'B',
      },
    });

    const input = container.querySelector('input')!;
    const input2 = container2.querySelector('input')!;

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(false);

    await fireEvent.update(input2!, 'B');

    expect(input.checked).toBe(false);
    expect(input2.checked).toBe(true);
  });

  it('handles the v-model with not regular value types', async () => {
    const modelValue = [123, 'A'];
    const { container } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: modelValue,
      },
    });
    const { container: container2 } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: () => {},
      },
    });
    const { container: container3 } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: { A: 'B' },
      },
    });

    const input = container.querySelector('input')!;
    const input2 = container2.querySelector('input')!;
    const input3 = container3.querySelector('input')!;

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(false);
    expect(input3.checked).toBe(false);

    await fireEvent.update(input2!);

    expect(input.checked).toBe(false);
    expect(input2.checked).toBe(true);
    expect(input3.checked).toBe(false);

    await fireEvent.update(input3!);

    expect(input.checked).toBe(false);
    expect(input2.checked).toBe(false);
    expect(input3.checked).toBe(true);
  });

  it('handles the v-model independently if radio name is different', async () => {
    const modelValue = 'A';
    const { container } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input',
        value: 'A',
      },
    });
    const { container: container2 } = render(TRadio, {
      props: {
        modelValue,
      },
      attrs: {
        name: 'radio-input-b',
        value: 'B',
      },
    });

    const input = container.querySelector('input')!;
    const input2 = container2.querySelector('input')!;

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(false);

    await fireEvent.update(input2!, 'B');

    expect(input.checked).toBe(true);
    expect(input2.checked).toBe(true);
  });
});


================================================
FILE: src/__tests/components/TRadio.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */

import { shallowMount } from '@vue/test-utils';
import { TRadioConfig } from '@variantjs/core';
import TRadio from '@/components/TRadio.vue';

describe('TRadio.vue', () => {
  it('renders the input', () => {
    const wrapper = shallowMount(TRadio);
    expect(wrapper.get('input')).toBeTruthy();
  });

  it('renders the radio input with a default set of classes', () => {
    const wrapper = shallowMount(TRadio);

    expect(wrapper.html()).toBe(`<input type="radio" class="${TRadioConfig.classes}">`);
  });

  it('renders the input without attributes if no default theme', () => {
    const wrapper = shallowMount(TRadio, {
      global: {
        provide: {
          configuration: {
            TRadio: {
              classes: undefined,
            },
          },
        },
      },
    });

    expect(wrapper.html()).toBe('<input type="radio">');
  });

  it('adds the value attribute', () => {
    const value = 'input value';
    const wrapper = shallowMount(TRadio, {
      attrs: { value },
    });

    expect(wrapper.vm.$el.value).toBe(value);
  });

  it('doesnt add the modelValue as attribute', () => {
    const value = 'input value';
    const wrapper = shallowMount(TRadio, {
      props: { modelValue: value },
      attrs: { value },
    });

    expect(wrapper.vm.$el.attributes.modelValue).toBeUndefined();
  });

  it('set as checked if model value is same as value', async () => {
    const value = 'input value';
    const wrapper = shallowMount(TRadio, {
      props: { modelValue: 'something else' },
      attrs: { value },
    });

    const radio = wrapper.vm.$el as HTMLInputElement;
    expect(radio.value).toBe(value);
    expect(radio.checked).toBe(false);

    await wrapper.setProps({
      modelValue: value,
    });

    expect(radio.value).toBe(value);
    expect(radio.checked).toBe(true);
  });

  it('disables the input', async () => {
    const wrapper = shallowMount(TRadio, {
      props: { disabled: false },
    });
    expect(wrapper.vm.$el.disabled).toBe(false);

    await wrapper.setProps({ disabled: true });

    expect(wrapper.vm.$el.disabled).toBe(true);
  });

  it('accepts misc input attributes', async () => {
    const wrapper = shallowMount(TRadio);

    const values = {
      id: {
        default: '',
        new: 'new-id',
      },
      autocomplete: {
        default: '',
        new: 'on',
      },
      autofocus: {
        default: false,
        new: true,
      },
      disabled: {
        default: false,
        new: true,
      },
      max: {
        default: '',
        new: '10',
      },
      maxlength: {
        keyName: 'maxLength',
        default: 524288,
        new: 12,
      },
      minlength: {
        keyName: 'minLength',
        default: 0,
        new: 2,
      },
      min: {
        default: '',
        new: '3',
      },
      multiple: {
        default: false,
        new: true,
      },
      name: {
        default: '',
        new: 'new-name',
      },
      pattern: {
        default: '',
        new: '[A-Za-z]{3}',
      },
      placeholder: {
        default: '',
        new: 'new placeholder',
      },
      readonly: {
        keyName: 'readOnly',
        default: false,
        new: true,
      },
      required: {
        default: false,
        new: true,
      },
    };

    const netProps: any = {};
    // Check for the default values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.default);

      netProps[key as any] = elementValue.new;
    });

    await wrapper.setProps(netProps);

    // Check for the new values
    Object.keys(values).forEach((key) => {
      const elementValue = (values as any)[key];
      expect(wrapper.vm.$el[elementValue.keyName || key]).toBe(elementValue.new);
    });
  });

  it('emits an update event with the input value', () => {
    const modelValue = 'original value';

    const wrapper = shallowMount(TRadio, {
      props: {
        modelValue,
      },
    });

    const inputValue = 'new value';

    wrapper.setValue(inputValue);

    expect(wrapper.emitted('update:modelValue')).toBeTruthy();

    // assert event count
    expect(wrapper.emitted('update:modelValue')?.length).toBe(1);

    // assert event payload
    expect(wrapper.emitted('update:modelValue')![0]).toEqual([inputValue]);
  });

  it('emits native input events', () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();
    const onFocus = jest.fn();
    const onKeyup = jest.fn();
    const onInput = jest.fn();

    const wrapper = shallowMount(TRadio, {
      attrs: {
        onChange,
        onBlur,
        onFocus,
        onKeyup,
        onInput,
      },
    });

    const input = wrapper.vm.$el;

    input.dispatchEvent(new Event('change'));
    expect(onChange).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('focus'));
    expect(onFocus).toHaveBeenCalled();

    input.dispatchEvent(new FocusEvent('blur'));
    expect(onBlur).toHaveBeenCalled();

    input.dispatchEvent(new InputEvent('input'));
    expect(onInput).toHaveBeenCalled();

    input.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' }));
    expect(onKeyup).toHaveBeenCalled();
  });

  it('has native input methods', () => {
    const wrapper = shallowMount(TRadio);

    const input = wrapper.vm.$el;

    expect(typeof input.click).toBe('function');
    expect(typeof input.focus).toBe('function');
  });

  it('triggers custom events', async () => {
    const onCustom = jest.fn();

    const wrapper = shallowMount(TRadio, {
      attrs: {
        onCustom,
      },
    });
    const input = wrapper.vm.$el as HTMLInputElement;

    const evt = new CustomEvent('custom', { detail: 'my-custom-event' });
    input.dispatchEvent(evt);

    expect(onCustom).toHaveBeenCalled();
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectClearButton.spec.ts
================================================
import { shallowMount } from '@vue/test-utils';
import RichSelectClearButton from '../../../components/TRichSelect/RichSelectClearButton.vue';

describe('RichSelectClearButton', () => {
  it('renders the component', () => {
    const wrapper = shallowMount(RichSelectClearButton);
    expect(wrapper.vm.$el.tagName).toBe('BUTTON');
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectDropdown.spec.ts
================================================
import { NormalizedOptions } from '@variantjs/core';
import { shallowMount } from '@vue/test-utils';
import { computed } from 'vue';
import RichSelectDropdown from '../../../components/TRichSelect/RichSelectDropdown.vue';
import { getChildComponentNameByRef } from '../../testUtils';

describe('RichSelectDropdown', () => {
  const options: NormalizedOptions = [{
    value: 'a',
    text: 'a',
  }];

  const showSearchInput = computed(() => true);

  it('renders the component', () => {
    const wrapper = shallowMount(RichSelectDropdown, {
      global: {
        provide: {
          options,
          showSearchInput,
        },
      },
    });

    expect(wrapper.vm.$el.tagName).toBe('DIV');
  });

  it('has a RichSelectOptionsList component', () => {
    const wrapper = shallowMount(RichSelectDropdown, {
      global: {
        provide: {
          showSearchInput,
          options,
        },
      },
    });

    expect(getChildComponentNameByRef(wrapper, 'optionsList')).toEqual('RichSelectOptionsList');
  });

  it('has a RichSelectSearchInput  component', () => {
    const wrapper = shallowMount(RichSelectDropdown, {
      global: {
        provide: {
          showSearchInput,
          options,
        },
      },
    });

    expect(getChildComponentNameByRef(wrapper, 'searchInput')).toEqual('RichSelectSearchInput');
  });

  it('hides the RichSelectSearchInput component if `showSearchInput` is `false`', () => {
    const wrapper = shallowMount(RichSelectDropdown, {
      global: {
        provide: {
          options,
          showSearchInput: computed(() => false),
        },
      },
    });

    expect(getChildComponentNameByRef(wrapper, 'searchInput')).toBeUndefined();
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectOption.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { NormalizedOption } from '@variantjs/core';
import { mount, shallowMount } from '@vue/test-utils';
import { ref } from 'vue';
import RichSelectOption from '../../../components/TRichSelect/RichSelectOption.vue';

describe('RichSelectOption', () => {
  const scrollIntoViewMock = jest.fn();
  window.HTMLLIElement.prototype.scrollIntoView = scrollIntoViewMock;

  const toggleOption = jest.fn();
  const setActiveOption = jest.fn();
  const optionIsSelected = jest.fn();
  const optionIsActive = jest.fn();
  const shown = ref(true);
  const option: NormalizedOption = {
    value: 'a',
    text: 'Option A',
  };
  const deep = 0;

  const global = {
    provide: {
      toggleOption,
      setActiveOption,
      optionIsSelected,
      optionIsActive,
      shown,
    },
    stubs: {
      RichSelectOptionsList: {
        template: '<div />',
      },
    },
  };

  afterEach(() => {
    jest.clearAllMocks();

    optionIsActive.mockReturnValue(false);

    scrollIntoViewMock.mockReset();
  });

  it('renders the component', () => {
    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.$el.tagName).toBe('LI');
    expect(wrapper.vm.$el.textContent).toBe('Option A');
  });

  it('determines if option has children', () => {
    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.hasChildren).toBe(false);
  });

  it('determines if option has children when empty', () => {
    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option: {
          ...option,
          children: [],
        },
        deep,
      },
      global,
    });

    expect(wrapper.vm.hasChildren).toBe(false);
  });

  it('determines if option has children when not empty', () => {
    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option: {
          ...option,
          children: [
            { value: 1, text: 1 },
          ],
        },
        deep,
      },
      global,
    });

    expect(wrapper.vm.hasChildren).toBe(true);
  });

  it('determines if option is selected', () => {
    optionIsSelected.mockReturnValue(true);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.isSelected).toBe(true);
  });

  it('determines if option is selected when false', () => {
    optionIsSelected.mockReturnValue(false);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.isSelected).toBe(false);
  });

  it('determines if option is active', () => {
    optionIsActive.mockReturnValue(true);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeccesary').mockImplementation(() => {});

    expect(wrapper.vm.isActive).toBe(true);
  });

  it('determines if option is active when false', () => {
    optionIsActive.mockReturnValue(false);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.isActive).toBe(false);
  });

  it('shows the checkmark icon if option is selected', () => {
    optionIsSelected.mockReturnValue(true);

    const wrapper = mount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.$refs.checkIcon).toBeDefined();
  });

  it('hides the checkmark icon if option is not selected', () => {
    optionIsSelected.mockReturnValue(false);

    const wrapper = mount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    expect(wrapper.vm.$refs.checkIcon).toBeUndefined();
  });

  it('will scroll into the view if shown and is active', async () => {
    optionIsActive.mockReturnValue(true);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;

    await wrapper.vm.$nextTick();

    expect(scrollIntoViewMock).toHaveBeenCalled();
  });

  it('will scroll into the view if active changes state', async () => {
    optionIsActive.mockReturnValue(true);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    await wrapper.vm.$nextTick();
    // First time when created
    expect(scrollIntoViewMock).toHaveBeenCalledTimes(1);

    (wrapper.vm.$options.watch!.isActive as any).call(wrapper.vm);

    // Second time when state changed
    expect(scrollIntoViewMock).toHaveBeenCalledTimes(2);
  });

  it('will not scroll into the view if shown but is not active', async () => {
    optionIsActive.mockReturnValue(false);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global,
    });

    wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;

    await wrapper.vm.$nextTick();

    expect(scrollIntoViewMock).not.toHaveBeenCalled();
  });

  it('will not scroll into the view if is active but is not shown', async () => {
    optionIsActive.mockReturnValue(true);

    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option,
        deep,
      },
      global: {
        provide: {
          ...global.provide,
          shown: ref(false),
        },
        stubs: global.stubs,
      },
    });

    wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;

    await wrapper.vm.$nextTick();

    expect(scrollIntoViewMock).not.toHaveBeenCalled();
  });

  describe('event handlers', () => {
    it('calls the `mousemoveHandler` when option mousemove', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      const mousemoveHandlerSpy = jest.spyOn(wrapper.vm, 'mousemoveHandler');

      wrapper.vm.$el.dispatchEvent(new MouseEvent('mousemove'));

      expect(mousemoveHandlerSpy).toHaveBeenCalled();
    });

    it('calls setActiveOption method when `mousemoveHandler` called', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      wrapper.vm.mousemoveHandler();

      expect(setActiveOption).toHaveBeenCalledWith(option);
    });

    it('doesnt call setActiveOption method when `mousemoveHandler` called and option is disabled', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option: {
            ...option,
            disabled: true,
          },
          deep,
        },
        global,
      });

      wrapper.vm.mousemoveHandler();

      expect(setActiveOption).not.toHaveBeenCalled();
    });

    it('calls the `mousewheelHandler` when mousewheel event', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      const mousewheelHandlerSpy = jest.spyOn(wrapper.vm, 'mousewheelHandler');

      wrapper.vm.$el.dispatchEvent(new MouseEvent('mousewheel'));

      expect(mousewheelHandlerSpy).toHaveBeenCalled();
    });

    it('call setActiveOption method when `mousewheelHandler` is called ', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      wrapper.vm.mousewheelHandler();

      expect(setActiveOption).toHaveBeenCalledWith(option);
    });

    it('doesnt call setActiveOption method when `mousewheelHandler` is called and option is disabled', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option: {
            ...option,
            disabled: true,
          },
          deep,
        },
        global,
      });

      wrapper.vm.mousewheelHandler();

      expect(setActiveOption).not.toHaveBeenCalled();
    });

    it('calls the `clickHandler` when option clicked', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      const clickHandlerSpy = jest.spyOn(wrapper.vm, 'clickHandler');

      wrapper.vm.$el.dispatchEvent(new MouseEvent('click'));

      expect(clickHandlerSpy).toHaveBeenCalled();
    });

    it('the `clickHandler` toggles the option', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      wrapper.vm.clickHandler();

      expect(toggleOption).toHaveBeenCalledWith(option);
    });

    it('doesnt call option toggle method when `clickHandler` is called and option is disabled', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option: {
            ...option,
            disabled: true,
          },
          deep,
        },
        global,
      });

      wrapper.vm.clickHandler();

      expect(toggleOption).not.toHaveBeenCalled();
    });
  });

  describe('regular option attributes', () => {
    it('has the correct `aria-selected` attribute when is selected', () => {
      optionIsSelected.mockReturnValue(true);

      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('aria-selected')).toBe('true');
    });

    it('has the correct `aria-selected` attribute when is not selected', () => {
      optionIsSelected.mockReturnValue(false);

      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('aria-selected')).toBe('false');
    });

    it('has the role=option attribute', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('role')).toBe('option');
    });
    it('has the tabindex=-1 attribute', () => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('tabindex')).toBe('-1');
    });

    it.each([1, 'foo', undefined, NaN])('adds a value attribute for regular values with %s', (value) => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option: {
            value,
            text: 'Foo',
          },
          deep,
        },
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('value')).toBe(String(value));
    });

    it.each([{ foo: 'bar' }, [1, 2], null])('adds a value attribute for objects %s', (value) => {
      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option: {
            value,
            text: 'Foo',
          },
          deep,
        } as any,
        global,
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('value')).toBe(JSON.stringify(value));
    });
  });

  describe('option classes', () => {
    const classesList = {
      option: 'option',
      selectedHighlightedOption: 'selected-highlighted-option',
      selectedOption: 'selected-option',
      highlightedOption: 'highlighted-option',
    };

    const configuration = { classesList };

    it('adds the selectedHighlightedOption if option is selected an active', () => {
      optionIsSelected.mockReturnValue(true);
      optionIsActive.mockReturnValue(true);

      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global: {
          ...global,
          provide: {
            ...global.provide,
            configuration,
          },
        },
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option selected-highlighted-option');
    });

    it('adds the selectedOption if option is selected but is not active', () => {
      optionIsSelected.mockReturnValue(true);
      optionIsActive.mockReturnValue(false);

      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global: {
          ...global,
          provide: {
            ...global.provide,
            configuration,
          },
        },
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option selected-option');
    });

    it('adds the highlightedOption if option is active but is not selected', () => {
      optionIsSelected.mockReturnValue(false);
      optionIsActive.mockReturnValue(true);

      const wrapper = shallowMount(RichSelectOption, {
        props: {
          option,
          deep,
        },
        global: {
          ...global,
          provide: {
            ...global.provide,
            configuration,
          },
        },
      });

      expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option highlighted-option');
    });
  });

  describe('option has children', () => {
    const wrapper = shallowMount(RichSelectOption, {
      props: {
        option: {
          value: 'foo',
          text: 'Foo',
          children: [{ value: 1, text: 1 }],
        },
        deep: 0,
      },
      global,
    });

    it('has the role=optgroup attribute', () => {
      expect(wrapper.vm.$el.getAttribute('role')).toBe('optgroup');
    });

    it('has the option  text', () => {
      expect(wrapper.vm.$el.textContent).toBe('Foo');
    });

    it('shows the childrenOptions component', () => {
      expect(wrapper.vm.$refs.childrenOptions).toBeDefined();
    });
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectOptionsList.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { NormalizedOptions } from '@variantjs/core';
import { shallowMount } from '@vue/test-utils';
import { ref } from 'vue';
import RichSelectOptionsList from '../../../components/TRichSelect/RichSelectOptionsList.vue';
import { TSelectOptions } from '../../../types';

describe('RichSelectOptionsList', () => {
  const options: NormalizedOptions = [
    {
      value: 'A',
      text: 'Option A',
    },
    {
      value: 'B',
      text: 'Option B',
    },
  ];

  const props = {
    options,
    deep: 0,
  };

  const global = {
    provide: {
      configuration: {} as TSelectOptions,
      fetchingMoreOptions: ref(false),
      shown: ref(false),
      dropdownBottomReachedHandler: jest.fn(),
    },
  };

  it('should render without errors', () => {
    const wrapper = shallowMount(RichSelectOptionsList, { props, global });
    expect(wrapper.vm.$el.tagName).toBe('UL');
  });

  it('doesnt have `style` attribute by default', () => {
    const wrapper = shallowMount(RichSelectOptionsList, { props, global });
    expect(wrapper.vm.$el.getAttribute('style')).toBeNull();
  });

  it('adds the max-height style if defined on the settings', () => {
    const configuration: TSelectOptions = {
      maxHeight: 100,
    };
    const wrapper = shallowMount(RichSelectOptionsList, {
      props,
      global: {
        provide: {
          ...global.provide,
          configuration,
        },
      },
    });

    expect(wrapper.vm.$el.getAttribute('style')).toBe('max-height: 100px; overflow-x: auto;');
  });

  it('doesnt add the max-height style if deep > 0', () => {
    const configuration: TSelectOptions = {
      maxHeight: 100,
    };
    const wrapper = shallowMount(RichSelectOptionsList, {
      props: {
        options,
        deep: 1,
      },
      global: {
        provide: {
          ...global.provide,
          configuration,
        },
      },
    });

    expect(wrapper.vm.$el.getAttribute('style')).toBeNull();
  });

  it('adds every option', () => {
    const wrapper = shallowMount(RichSelectOptionsList, {
      props,
      global,
    });

    expect(wrapper.findAll('rich-select-option-stub').length).toBe(options.length);
  });

  it('scroll the list to the "fetchingMoreOptions" element after loaded more options ends', async () => {
    const fetchingMoreOptions = ref(false);

    const scrollElementMock = jest.fn();
    window.HTMLLIElement.prototype.scrollIntoView = scrollElementMock;

    const wrapper = shallowMount(RichSelectOptionsList, {
      props,
      global: {
        provide: {
          ...global.provide,
          fetchingMoreOptions,
        },
      },
    });

    expect(scrollElementMock).not.toHaveBeenCalled();

    fetchingMoreOptions.value = true;

    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    expect(scrollElementMock).toHaveBeenCalledTimes(1);

    // Should not call again when `fetchingMoreOptions` is false again
    fetchingMoreOptions.value = false;

    await wrapper.vm.$nextTick();
    await wrapper.vm.$nextTick();

    expect(scrollElementMock).toHaveBeenCalledTimes(1);
  });

  describe('dropdownBottomReachedHandler', () => {
    it('adds a scroll listener attached to the bottomReachedObserver when component is mounted', () => {
      const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener');

      const wrapper = shallowMount(RichSelectOptionsList, {
        props,
        global,
      });

      expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver);

      addEventListenerSpy.mockRestore();
    });

    it('doesnt adds a scroll listener if no options list', () => {
      const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener');

      shallowMount(RichSelectOptionsList, {
        props: {
          ...props,
          options: [],
        },
        global,
      });

      expect(addEventListenerSpy).not.toHaveBeenCalled();

      addEventListenerSpy.mockRestore();
    });

    it('adds a scroll listener once it have options list and is rendered', async () => {
      const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener');

      const wrapper = shallowMount(RichSelectOptionsList, {
        props: {
          ...props,
          options: [],
        },
        global,
      });

      expect(addEventListenerSpy).not.toHaveBeenCalled();

      await wrapper.setProps({
        options: [{ value: 'a', text: 'A' }],
      });

      // Not called yet since the element is not in the DOM yet
      expect(addEventListenerSpy).not.toHaveBeenCalled();

      await wrapper.vm.$nextTick();

      expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver);

      addEventListenerSpy.mockRestore();
    });

    it('removes the scroll listener when no longer have options list', async () => {
      const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener');

      const wrapper = shallowMount(RichSelectOptionsList, {
        props,
        global,
      });

      await wrapper.setProps({
        options: [],
      });

      expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver);

      removeEventListenerSpy.mockRestore();
    });

    it('removes the scroll listener when component is unmounted', () => {
      const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener');

      const wrapper = shallowMount(RichSelectOptionsList, {
        props,
        global,
      });

      wrapper.unmount();

      expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver);

      removeEventListenerSpy.mockRestore();
    });

    it('doesnt removes the scroll listener when component is unmounted if no options', () => {
      const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener');

      const wrapper = shallowMount(RichSelectOptionsList, {
        props: {
          ...props,
          options: [],
        },
        global,
      });

      wrapper.unmount();

      expect(removeEventListenerSpy).not.toHaveBeenCalled();

      removeEventListenerSpy.mockRestore();
    });

    it('calls the dropdownBottomReachedHandler when bottom reached', () => {
      const dropdownBottomReachedHandlerMock = jest.fn();

      jest.useFakeTimers();

      const wrapper = shallowMount(RichSelectOptionsList, {
        props,
        global: {
          provide: {
            ...global.provide,
            dropdownBottomReachedHandler: dropdownBottomReachedHandlerMock,
          },
        },
      });

      const container = wrapper.vm.$el;

      jest.spyOn(container, 'clientHeight', 'get').mockReturnValue(100);
      // 150 - 49 != 100 (which is the not height of the container) meaning it
      // not reached the bottom yet
      jest.spyOn(container, 'scrollHeight', 'get').mockReturnValue(150);
      jest.spyOn(container, 'scrollTop', 'get').mockReturnValue(49);

      container.dispatchEvent(new Event('scroll', {
        target: container,
      } as any));

      // (is debounced 200ms)
      jest.advanceTimersByTime(200);
      expect(dropdownBottomReachedHandlerMock).not.toHaveBeenCalled();

      // 150 - 50 === 100 (meaning reached the bottom)
      jest.spyOn(container, 'scrollTop', 'get').mockReturnValue(50);

      container.dispatchEvent(new Event('scroll', {
        target: container,
      } as any));

      // (is debounced 200ms)
      jest.advanceTimersByTime(199);
      expect(dropdownBottomReachedHandlerMock).not.toHaveBeenCalled();
      jest.advanceTimersByTime(1);
      expect(dropdownBottomReachedHandlerMock).toHaveBeenCalled();

      jest.useRealTimers();
    });
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectSearchInput.spec.ts
================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
import { shallowMount } from '@vue/test-utils';
import { ref } from 'vue';
import RichSelectSearchInput from '../../../components/TRichSelect/RichSelectSearchInput.vue';

describe('RichSelectSearchInput', () => {
  const keydownDownHandler = jest.fn();
  const keydownUpHandler = jest.fn();
  const keydownEnterHandler = jest.fn();
  const keydownEscHandler = jest.fn();

  const global = {
    provide: {
      shown: ref(true),
      searchQuery: ref(''),
      keydownDownHandler,
      keydownUpHandler,
      keydownEnterHandler,
      keydownEscHandler,
    },
  };

  let wrapper: any;
  let search: HTMLInputElement;

  beforeEach(() => {
    wrapper = shallowMount(RichSelectSearchInput, { global });

    search = wrapper.vm.$refs.search as HTMLInputElement;
  });

  it('renders the component without errors', () => {
    expect(wrapper.vm.$el.tagName).toBe('DIV');
  });

  it('calls the keydownDownHandler when down key pressed', () => {
    const event = new KeyboardEvent('keydown', { key: 'ArrowDown' });

    search.dispatchEvent(event);

    expect(keydownDownHandler).toHaveBeenCalled();
  });

  it('calls the keydownUpHandler when down key pressed', () => {
    const event = new KeyboardEvent('keydown', { key: 'ArrowUp' });

    search.dispatchEvent(event);

    expect(keydownUpHandler).toHaveBeenCalled();
  });

  it('calls the keydownEnterHandler when down key pressed', () => {
    const event = new KeyboardEvent('keydown', { key: 'enter' });

    search.dispatchEvent(event);

    expect(keydownEnterHandler).toHaveBeenCalled();
  });

  it('calls the keydownEscHandler when down key pressed', () => {
    const event = new KeyboardEvent('keydown', { key: 'esc' });

    search.dispatchEvent(event);

    expect(keydownEscHandler).toHaveBeenCalled();
  });

  it('focus the search field after is shown', async () => {
    const shown = ref(false);

    wrapper = shallowMount(RichSelectSearchInput, {
      global: {
        provide: {
          ...global.provide,
          shown,
        },
      },
    });

    const searchInput = wrapper.vm.$el.querySelector('input');

    const focusSpy = jest.spyOn(searchInput, 'focus');

    shown.value = true;

    await wrapper.vm.$nextTick();

    await wrapper.vm.$nextTick();

    expect(focusSpy).toHaveBeenCalled();

    focusSpy.mockRestore();
  });

  it('doesnt focus the search field after is hidden', async () => {
    const shown = ref(true);

    wrapper = shallowMount(RichSelectSearchInput, {
      global: {
        provide: {
          ...global.provide,
          shown,
        },
      },
    });

    const searchInput = wrapper.vm.$el.querySelector('input');

    const focusSpy = jest.spyOn(searchInput, 'focus');

    shown.value = false;

    await wrapper.vm.$nextTick();

    await wrapper.vm.$nextTick();

    expect(focusSpy).not.toHaveBeenCalled();

    focusSpy.mockRestore();
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectState.spec.ts
================================================
import { NormalizedOption } from '@variantjs/core';
import { shallowMount } from '@vue/test-utils';
import { computed, ref } from 'vue';
import RichSelectState from '../../../components/TRichSelect/RichSelectState.vue';

describe('RichSelectState', () => {
  const configuration = {
    noResultsText: 'No results',
    searchingText: 'Searching...',
  };
  const options = computed<NormalizedOption[]>(() => ([{ value: 'a', text: 'A' }]));
  const fetchingOptions = ref<boolean>(false);
  const needsMoreCharsToFetch = ref<boolean>(false);
  const needsMoreCharsMessage = ref<string>('Needs more chars');

  const global = {
    provide: {
      configuration,
      options,
      fetchingOptions,
      needsMoreCharsToFetch,
      needsMoreCharsMessage,
    },
  };

  beforeEach(() => {
    fetchingOptions.value = false;
    needsMoreCharsToFetch.value = false;
  });

  describe('have options', () => {
    const wrapper = shallowMount(RichSelectState, { global });

    it('doesnt render anything by default', () => {
      expect(wrapper.text()).toBe('');
    });

    it('renders the searching text if fetchingOptions', async () => {
      fetchingOptions.value = true;

      await wrapper.vm.$nextTick();

      expect(wrapper.text()).toBe('Searching...');
    });

    it('renders the searching text if fetchingOptions and need more chars', async () => {
      fetchingOptions.value = true;
      needsMoreCharsToFetch.value = true;

      await wrapper.vm.$nextTick();

      expect(wrapper.text()).toBe('Searching...');
    });

    it('renders the need more chars message if need more chars', async () => {
      needsMoreCharsToFetch.value = true;

      await wrapper.vm.$nextTick();

      expect(wrapper.text()).toBe('Needs more chars');
    });
  });

  describe('doesnt have options', () => {
    const wrapper = shallowMount(RichSelectState, {
      global: {
        provide: {
          ...global.provide,
          options: computed(() => []),
        },
      },
    });

    it('shows the noResultsText by default', async () => {
      expect(wrapper.text()).toBe('No results');
    });

    it('doesnt shows the noResultsText if it need more chars to fetch ', async () => {
      needsMoreCharsToFetch.value = true;

      await wrapper.vm.$nextTick();

      expect(wrapper.text()).toBe('Needs more chars');
    });

    it('renders the searching text if fetchingOptions and need more chars', async () => {
      fetchingOptions.value = true;
      needsMoreCharsToFetch.value = true;

      await wrapper.vm.$nextTick();

      expect(wrapper.text()).toBe('Searching...');
    });
  });
});


================================================
FILE: src/__tests/components/TRichSelect/RichSelectTrigger.spec.ts
================================================
import { NormalizedOption } from '@variantjs/core';
import { shallowMount } from '@vue/test-utils';
import { computed, ref } from 'vue';
import RichSelectTrigger from '../../../components/TRichSelect/RichSelectTrigger.vue';

describe('RichSelectTrigger', () => {
  const selectedOption = computed<NormalizedOption | undefined>(() => undefined);
  const configuration = {};
  const hasSelectedOption = ref(false);

  const global = {
    provide: {
      shown: ref(false),
      usesTags: ref(false),
      fetchingOptions: ref(false),
      selectedOption,
      hasSelectedOption,
      configuration,
    },
  };

  it('will show the placeholder if not option selected', () => {
    const wrapper = shallowMount(RichSelectTrigger, { global });

    expect(wrapper.find('text-placeholder-stub').exists()).toBe(true);
    expect(wrapper.find('span').exists()).toBe(false);
  });

  it('will show option selected text', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          selectedOption: computed<NormalizedOption | undefined>(() => ({
            value: 'foo',
            text: 'Foo Bar',
          })),
          hasSelectedOption: ref(true),
          configuration,
        },
      },
    });

    expect(wrapper.find('text-placeholder-stub').exists()).toBe(false);
    expect(wrapper.find('span').text()).toBe('Foo Bar');
  });

  it('will show the selector icon if is not clearable', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          configuration: {
            clearable: false,
          },
        },
      },
    });

    expect(wrapper.vm.showSelectorIcon).toBe(true);
    expect(wrapper.find('selector-icon-stub').exists()).toBe(true);
  });

  it('will show the selector icon if is disabled', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          configuration: {
            disabled: true,
          },
        },
      },
    });

    expect(wrapper.vm.showSelectorIcon).toBe(true);
    expect(wrapper.find('selector-icon-stub').exists()).toBe(true);
  });

  it('will show the selector icon if is disabled and have a selected option', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          configuration: {
            disabled: true,
          },
          selectedOption: computed<NormalizedOption | undefined>(() => ({
            value: 'foo',
            text: 'Foo Bar',
          })),
          hasSelectedOption: ref(true),
        },
      },
    });

    expect(wrapper.vm.showSelectorIcon).toBe(true);
    expect(wrapper.find('selector-icon-stub').exists()).toBe(true);
  });

  it('will  show the selector icon if clearable and doesnt have selected option', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          configuration: {
            clearable: true,
          },
        },
      },
    });

    expect(wrapper.vm.showSelectorIcon).toBe(true);
    expect(wrapper.find('selector-icon-stub').exists()).toBe(true);
  });

  it('will not show the selector icon if clearable and have selected option', () => {
    const wrapper = shallowMount(RichSelectTrigger, {
      global: {
        provide: {
          ...global.provide,
          selectedOption: computed<NormalizedOption | undefined>(() => ({
            value: 'foo',
            text: 'foo',
          })),
          hasSelectedOption: ref(true),
          configuration: {
            clearable: true,
          },
        },
      },
    });

    expect(wrapper.vm.showSelectorIcon).toBe(false);
    expect(wrapper.find('selector-icon-stub').exists()).toBe(false);
  });

  describe('label', () => {
    it('will use the selectedoption text for single values', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            selectedOption: computed<NormalizedOption | undefined>(() => ({
              value: 'foo',
              text: 'foo bar',
            })),
            hasSelectedOption: ref(true),
          },
        },
      });

      expect(wrapper.vm.label).toBe('foo bar');
    });

    it('will join all the the selectedoption text with a comma for multiple values', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            selectedOption: computed<NormalizedOption[]>(() => ([
              {
                value: 'foo',
                text: 'foo bar',
              },
              {
                value: 'bar',
                text: 'option 2',
              },
            ])),
            hasSelectedOption: ref(true),
          },
        },
      });

      expect(wrapper.vm.label).toBe('foo bar, option 2');
    });

    it('will return undefined if empty selected option', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            selectedOption: computed<undefined>(() => undefined),
            hasSelectedOption: ref(false),
          },
        },
      });

      expect(wrapper.vm.label).toBeUndefined();
    });
  });

  describe('isFetchingOptionsWhileClosed handle', () => {
    it('considers that isFetchingOptionsWhileClosed if is fetchingOptions is true and is not shown', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            fetchingOptions: ref(true),
            shown: ref(false),
          },
        },
      });

      expect(wrapper.vm.isFetchingOptionsWhileClosed).toBe(true);
    });

    it('doesnt considers that isFetchingOptionsWhileClosed if is not fetchingOptions', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            fetchingOptions: ref(false),
            shown: ref(false),
          },
        },
      });

      expect(wrapper.vm.isFetchingOptionsWhileClosed).toBe(false);
    });

    it('doesnt considers that isFetchingOptionsWhileClosed if is fetchingOptions is true but is shown', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            fetchingOptions: ref(true),
            shown: ref(true),
          },
        },
      });

      expect(wrapper.vm.isFetchingOptionsWhileClosed).toBe(false);
    });

    it('will show a loading... placeholder and loading icon when fetching options while closed', () => {
      const wrapper = shallowMount(RichSelectTrigger, {
        global: {
          provide: {
            ...global.provide,
            configuration: {
              loadingClosedPlaceholder: 'Loading...',
            },
            fetchingOptions: ref(true),
          },
        },
      });

      expect(wrapper.vm.$refs.label).not.toBeDefined();
      expect(wrapper.vm.$refs.placeholder).not.toBeDefined();
      expect(wrapper.vm.$refs.fetchingPlaceholder).toBeDefined();
      expect(wrapper.vm.$refs.loadingIcon).toBeDefined();
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      expect((wrapper.vm.$refs.fetchingPlaceholder as any).placeholder).toBe('Loading...');
      // eslint-
Download .txt
gitextract_omw15f4s/

├── .eslintrc.json
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── yarn.yml
├── .gitignore
├── README.md
├── index.html
├── jest.config.js
├── package.json
├── postcss.config.js
├── src/
│   ├── __tests/
│   │   ├── components/
│   │   │   ├── TAlert.spec.ts
│   │   │   ├── TButton.spec.ts
│   │   │   ├── TCard.spec.ts
│   │   │   ├── TCheckbox.integration.spec.ts
│   │   │   ├── TCheckbox.spec.ts
│   │   │   ├── TDialog.spec.ts
│   │   │   ├── TDropdown.spec.ts
│   │   │   ├── TInput.integration.spec.ts
│   │   │   ├── TInput.spec.ts
│   │   │   ├── TInputGroup.spec.ts
│   │   │   ├── TModal.spec.ts
│   │   │   ├── TRadio.integration.spec.ts
│   │   │   ├── TRadio.spec.ts
│   │   │   ├── TRichSelect/
│   │   │   │   ├── RichSelectClearButton.spec.ts
│   │   │   │   ├── RichSelectDropdown.spec.ts
│   │   │   │   ├── RichSelectOption.spec.ts
│   │   │   │   ├── RichSelectOptionsList.spec.ts
│   │   │   │   ├── RichSelectSearchInput.spec.ts
│   │   │   │   ├── RichSelectState.spec.ts
│   │   │   │   ├── RichSelectTrigger.spec.ts
│   │   │   │   ├── RichSelectTriggerTags.spec.ts
│   │   │   │   └── RichSelectTriggerTagsTag.spec.ts
│   │   │   ├── TRichSelect.spec.ts
│   │   │   ├── TSelect/
│   │   │   │   └── TSelectOption.spec.ts
│   │   │   ├── TSelect.integration.spec.ts
│   │   │   ├── TSelect.spec.ts
│   │   │   ├── TTag.spec.ts
│   │   │   ├── TTextarea.integration.spec.ts
│   │   │   ├── TTextarea.spec.ts
│   │   │   ├── TToggle.spec.ts
│   │   │   ├── icons/
│   │   │   │   └── CustomIcon.spec.ts
│   │   │   └── misc/
│   │   │       ├── TextPlaceholder.spec.ts
│   │   │       └── Transitionable.spec.ts
│   │   ├── index.spec.ts
│   │   ├── plugin.spec.ts
│   │   ├── testUtils.ts
│   │   ├── use/
│   │   │   ├── useActivableOption.spec.ts
│   │   │   ├── useConfiguration.spec.ts
│   │   │   ├── useConfigurationWithClassesList.spec.ts
│   │   │   ├── useFetchsOptions.spec.ts
│   │   │   ├── useInjectsClassesList.spec.ts
│   │   │   ├── useInjectsConfiguration.spec.ts
│   │   │   ├── useMulipleableVModel.spec.ts
│   │   │   ├── useMultioptions.spec.ts
│   │   │   ├── useSelectableOption.spec.ts
│   │   │   ├── useSetup.ts
│   │   │   └── useVModel.spec.ts
│   │   └── utils/
│   │       ├── createDialogProgramatically.spec.ts
│   │       ├── emitter.spec.ts
│   │       ├── getVariantProps.spec.ts
│   │       ├── popper.spec.ts
│   │       └── svgToVueComponent.spec.ts
│   ├── assets/
│   │   └── tailwind.css
│   ├── components/
│   │   ├── TAlert.vue
│   │   ├── TButton.vue
│   │   ├── TCard.vue
│   │   ├── TCheckbox.vue
│   │   ├── TDialog.vue
│   │   ├── TDropdown.vue
│   │   ├── TInput.vue
│   │   ├── TInputGroup.vue
│   │   ├── TModal.vue
│   │   ├── TRadio.vue
│   │   ├── TRichSelect/
│   │   │   ├── RichSelectClearButton.vue
│   │   │   ├── RichSelectDropdown.vue
│   │   │   ├── RichSelectOption.vue
│   │   │   ├── RichSelectOptionsList.vue
│   │   │   ├── RichSelectSearchInput.vue
│   │   │   ├── RichSelectState.vue
│   │   │   ├── RichSelectTrigger.vue
│   │   │   ├── RichSelectTriggerTags.vue
│   │   │   └── RichSelectTriggerTagsTag.vue
│   │   ├── TRichSelect.vue
│   │   ├── TSelect/
│   │   │   └── TSelectOption.vue
│   │   ├── TSelect.vue
│   │   ├── TTag.vue
│   │   ├── TTextarea.vue
│   │   ├── TToggle.vue
│   │   └── misc/
│   │       ├── TextPlaceholder.vue
│   │       └── Transitionable.vue
│   ├── development/
│   │   ├── About.vue
│   │   ├── Alert.vue
│   │   ├── App.vue
│   │   ├── AppMenu.vue
│   │   ├── Attributes.vue
│   │   ├── Check.vue
│   │   ├── Dialog.vue
│   │   ├── Dropdown.vue
│   │   ├── Home.vue
│   │   ├── Modal.vue
│   │   ├── Multioptions.vue
│   │   ├── Options.vue
│   │   ├── RichSelect.vue
│   │   ├── TSubmit.vue
│   │   ├── Theme.vue
│   │   └── router.ts
│   ├── icons/
│   │   ├── CheckCircleIcon.vue
│   │   ├── CheckmarkIcon.vue
│   │   ├── CloseIcon.vue
│   │   ├── CrossCircleIcon.vue
│   │   ├── CustomIcon.vue
│   │   ├── ExclamationIcon.vue
│   │   ├── InformationCircleIcon.vue
│   │   ├── LoadingIcon.vue
│   │   ├── QuestionMarkCircleIcon.vue
│   │   ├── SelectorIcon.vue
│   │   ├── SolidCheckCircleIcon.vue
│   │   ├── SolidCrossCircleIcon.vue
│   │   ├── SolidExclamationIcon.vue
│   │   ├── SolidInformationCircleIcon.vue
│   │   └── SolidQuestionMarkCircleIcon.vue
│   ├── index.ts
│   ├── main.ts
│   ├── plugin.ts
│   ├── shims-vue.d.ts
│   ├── types/
│   │   ├── components/
│   │   │   ├── t-alert.ts
│   │   │   ├── t-button.ts
│   │   │   ├── t-card.ts
│   │   │   ├── t-checkbox.ts
│   │   │   ├── t-dialog.ts
│   │   │   ├── t-dropdown.ts
│   │   │   ├── t-input-group.ts
│   │   │   ├── t-input.ts
│   │   │   ├── t-modal.ts
│   │   │   ├── t-radio.ts
│   │   │   ├── t-rich-select.ts
│   │   │   ├── t-select.ts
│   │   │   ├── t-tag.ts
│   │   │   ├── t-textarea.ts
│   │   │   └── t-toggle.ts
│   │   ├── helpers.ts
│   │   ├── index.ts
│   │   ├── misc.ts
│   │   ├── utils.ts
│   │   ├── variantCore.ts
│   │   └── vueRouter.ts
│   ├── use/
│   │   ├── useActivableOption.ts
│   │   ├── useConfiguration.ts
│   │   ├── useConfigurationWithClassesList.ts
│   │   ├── useFetchsOptions.ts
│   │   ├── useInjectsClassesList.ts
│   │   ├── useInjectsClassesListClass.ts
│   │   ├── useInjectsConfiguration.ts
│   │   ├── useMulipleableVModel.ts
│   │   ├── useMultioptions.ts
│   │   ├── useSelectableOption.ts
│   │   └── useVModel.ts
│   └── utils/
│       ├── createDialogProgramatically.ts
│       ├── emitter.ts
│       ├── getVariantProps.ts
│       ├── popper.ts
│       └── svgToVueComponent.ts
├── tailwind.config.js
├── tsconfig.json
├── vite.config.ts
└── vite.demo.config.ts
Download .txt
SYMBOL INDEX (87 symbols across 37 files)

FILE: src/__tests/components/TButton.spec.ts
  type RouterProps (line 254) | type RouterProps = typeof props;

FILE: src/__tests/use/useInjectsClassesList.spec.ts
  method setup (line 14) | setup() {

FILE: src/__tests/use/useInjectsConfiguration.spec.ts
  method setup (line 15) | setup() {

FILE: src/__tests/use/useSetup.ts
  type InstanceType (line 9) | type InstanceType<V> = V extends { new (...arg: any[]): infer X } ? X : ...
  type VM (line 11) | type VM<V> = InstanceType<V> & { unmount(): void };
  function mount (line 13) | function mount<V>(Comp: V, attributes?: Record<string, unknown>, configu...
  function useSetup (line 24) | function useSetup<V>(

FILE: src/plugin.ts
  method show (line 20) | show(name: string, params?: { [k: string]: string }) {
  method hide (line 23) | hide(name: string) {
  method show (line 36) | show(name: string): Promise<DialogResponse> {
  method hide (line 43) | hide(name: string) {
  type ComponentCustomProperties (line 65) | interface ComponentCustomProperties {

FILE: src/types/components/t-alert.ts
  type TAlertOptions (line 5) | type TAlertOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-button.ts
  type RouterLinkProps (line 5) | type RouterLinkProps = {
  type TButtonOptions (line 14) | type TButtonOptions = WithVariantProps<{

FILE: src/types/components/t-card.ts
  type TCardOptions (line 4) | type TCardOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-checkbox.ts
  type TCheckboxSimpleValue (line 6) | type TCheckboxSimpleValue = string | number | boolean | undefined | null...
  type TCheckboxValue (line 7) | type TCheckboxValue = TCheckboxSimpleValue | TCheckboxSimpleValue[] | Ob...
  type TCheckboxOptions (line 9) | type TCheckboxOptions = WithVariantProps<{

FILE: src/types/components/t-dialog.ts
  type TDialogOptions (line 7) | type TDialogOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-dropdown.ts
  type TDropdownOptions (line 5) | type TDropdownOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-input-group.ts
  type TInputGroupValidChilElementsKeys (line 4) | type TInputGroupValidChilElementsKeys = ('label' | 'default' | 'feedback...
  type TInputGroupOptions (line 6) | type TInputGroupOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-input.ts
  type TInputValue (line 4) | type TInputValue = string | number | string[] | undefined;
  type TInputOptions (line 6) | type TInputOptions = WithVariantProps<{

FILE: src/types/components/t-modal.ts
  type TModalOptions (line 5) | type TModalOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-radio.ts
  type TRadioSimpleValue (line 6) | type TRadioSimpleValue = string | number | boolean | undefined | null | ...
  type TRadioValue (line 7) | type TRadioValue = TRadioSimpleValue | TRadioSimpleValue[] | ObjectWithP...
  type TRadioOptions (line 9) | type TRadioOptions = WithVariantProps<{

FILE: src/types/components/t-rich-select.ts
  type MinimumInputLengthTextProp (line 9) | type MinimumInputLengthTextProp = ((minimumInputLength: number, query?: ...
  type TRichSelectOptions (line 11) | type TRichSelectOptions = WithVariantPropsAndClassesList<{

FILE: src/types/components/t-select.ts
  type TSelectValue (line 8) | type TSelectValue = string | number | boolean | undefined | null | Date ...
  type TSelectOptions (line 10) | type TSelectOptions = WithVariantProps<{

FILE: src/types/components/t-tag.ts
  type TTagOptions (line 4) | type TTagOptions = WithVariantProps<{

FILE: src/types/components/t-textarea.ts
  type TTextareaValue (line 4) | type TTextareaValue = string | number | string[] | undefined;
  type TTextareaOptions (line 6) | type TTextareaOptions = WithVariantProps<{

FILE: src/types/components/t-toggle.ts
  type TToggleValue (line 5) | type TToggleValue = TCheckboxValue;
  type TToggleOptions (line 7) | type TToggleOptions = WithVariantPropsAndClassesList<{

FILE: src/types/helpers.ts
  type ObjectWithProperties (line 1) | type ObjectWithProperties<P> = Record<string | number | symbol, P>;
  type KeysOfType (line 3) | type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp? P : ne...

FILE: src/types/misc.ts
  type Truthy (line 3) | type Truthy = boolean | string;
  type IconProp (line 6) | type IconProp = Element | string | (Data & { render?: Function });
  type FetchedOptions (line 8) | type FetchedOptions = Promise<{
  type FetchOptionsFn (line 13) | type FetchOptionsFn = (query?: string, nextPage?: number) => FetchedOpti...
  type PreFetchOptionsFn (line 16) | type PreFetchOptionsFn = (currentValue?: any) => Promise<InputOptions>;
  type PromiseRejectFn (line 19) | type PromiseRejectFn = ((reason?: any) => void);

FILE: src/types/utils.ts
  type EmitterFunction (line 2) | type EmitterFunction = (...args : any[]) => void;
  type EmitterEvents (line 4) | type EmitterEvents = {
  type EmitterInterface (line 8) | interface EmitterInterface {

FILE: src/types/variantCore.ts
  type VariantJSProps (line 17) | type VariantJSProps<ComponentOptions extends WithVariantProps<Data> = {
  type VariantJSWithClassesListProps (line 59) | type VariantJSWithClassesListProps<
  type VariantJSConfiguration (line 98) | type VariantJSConfiguration = {

FILE: src/types/vueRouter.ts
  type RouteParamValue (line 4) | type RouteParamValue = string;
  type RouteParamValueRaw (line 6) | type RouteParamValueRaw = RouteParamValue | number;
  type RouteParamsRaw (line 8) | type RouteParamsRaw = Record<string, RouteParamValueRaw | RouteParamValu...
  type RouteRecordName (line 10) | type RouteRecordName = string | symbol;
  type LocationAsRelativeRaw (line 12) | interface LocationAsRelativeRaw {
  type HistoryStateArray (line 17) | type HistoryStateArray = Array<HistoryStateValue>;
  type HistoryStateValue (line 19) | type HistoryStateValue = string | number | boolean | null | undefined | ...
  type HistoryState (line 24) | interface HistoryState {
  type RouteLocationOptions (line 29) | interface RouteLocationOptions {
  type LocationAsPath (line 46) | interface LocationAsPath {
  type LocationQueryValue (line 50) | type LocationQueryValue = string | null;
  type LocationQueryValueRaw (line 52) | type LocationQueryValueRaw = LocationQueryValue | number | undefined;
  type LocationQueryRaw (line 54) | type LocationQueryRaw = Record<string | number, LocationQueryValueRaw | ...
  type RouteQueryAndHash (line 56) | interface RouteQueryAndHash {
  type VueRouteRouteLocationRaw (line 61) | type VueRouteRouteLocationRaw = string | (RouteQueryAndHash & LocationAs...
  type VueRouteAriaCurrentValue (line 62) | type VueRouteAriaCurrentValue = 'page' | 'step' | 'location' | 'date' | ...

FILE: src/use/useActivableOption.ts
  function useActivableOption (line 6) | function useActivableOption(

FILE: src/use/useConfiguration.ts
  function useAttributes (line 19) | function useAttributes<ComponentOptions extends Data>(configuration: Com...
  function useConfigurationParts (line 41) | function useConfigurationParts<ComponentOptions extends Data>(): {
  function useConfiguration (line 68) | function useConfiguration<ComponentOptions extends Data>(defaultConfigur...

FILE: src/use/useConfigurationWithClassesList.ts
  function useConfigurationWithClassesList (line 7) | function useConfigurationWithClassesList<ComponentOptions extends Data>(...

FILE: src/use/useFetchsOptions.ts
  function useFetchsOptions (line 11) | function useFetchsOptions(

FILE: src/use/useInjectsClassesList.ts
  function useInjectsClassesList (line 5) | function useInjectsClassesList(): ComputedRef<CSSClassesList> {

FILE: src/use/useInjectsClassesListClass.ts
  function useInjectsClassesListClass (line 5) | function useInjectsClassesListClass(property: string): ComputedRef<CSSCl...

FILE: src/use/useInjectsConfiguration.ts
  function useInjectsConfiguration (line 4) | function useInjectsConfiguration<P extends WithVariantPropsAndClassesLis...

FILE: src/use/useMulipleableVModel.ts
  function useMulipleableVModel (line 6) | function useMulipleableVModel<P extends Data, K extends keyof P, C exten...

FILE: src/use/useMultioptions.ts
  function useMultioptions (line 10) | function useMultioptions(

FILE: src/use/useSelectableOption.ts
  type SelectedOption (line 9) | type SelectedOption = NormalizedOption | NormalizedOption[] | undefined;
  function useSelectableOption (line 11) | function useSelectableOption(

FILE: src/use/useVModel.ts
  function useVModel (line 6) | function useVModel<P extends Data, K extends keyof P>(

FILE: src/utils/emitter.ts
  class Emitter (line 4) | class Emitter implements EmitterInterface {
    method on (line 8) | on(name: keyof EmitterEvents, callback: EmitterFunction): void {
    method once (line 16) | once(name: keyof EmitterEvents, callback: EmitterFunction): void {
    method emit (line 25) | emit(name: keyof EmitterEvents, ...args: any[]): void {
    method off (line 37) | off(name: keyof EmitterEvents, callback: EmitterFunction): void {
Condensed preview — 165 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (654K chars).
[
  {
    "path": ".eslintrc.json",
    "chars": 995,
    "preview": "{\n  \"env\": {\n      \"browser\": true,\n      \"es6\": true\n  },\n  \"extends\": [\n    \"plugin:vue/vue3-recommended\",\n    \"plugin"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 114,
    "preview": "# These are supported funding model platforms\ngithub: variantjs\ncustom: https://www.buymeacoffee.com/alfonsobries\n"
  },
  {
    "path": ".github/workflows/yarn.yml",
    "chars": 200,
    "preview": "name: CI\non: push\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - name: Insta"
  },
  {
    "path": ".gitignore",
    "chars": 68,
    "preview": "node_modules\n.DS_Store\ndist\ndist-ssr\ncoverage\n*.local\nyarn-error.log"
  },
  {
    "path": "README.md",
    "chars": 10065,
    "preview": "# VariantJS/Vue (PREVIEW)\n\n\n![CI](https://github.com/variantjs/vue/workflows/CI/badge.svg) [![CI](https://github.com/var"
  },
  {
    "path": "index.html",
    "chars": 337,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <"
  },
  {
    "path": "jest.config.js",
    "chars": 458,
    "preview": "module.exports = {\n  testEnvironment: 'jsdom',\n  preset: '@vue/cli-plugin-unit-jest/presets/typescript',\n  collectCovera"
  },
  {
    "path": "package.json",
    "chars": 2641,
    "preview": "{\n  \"name\": \"@variantjs/vue\",\n  \"version\": \"0.0.22\",\n  \"description\": \"Vue VariantJS: Fully configurable Vue 3 component"
  },
  {
    "path": "postcss.config.js",
    "chars": 82,
    "preview": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "src/__tests/components/TAlert.spec.ts",
    "chars": 6901,
    "preview": "import { mount } from '@vue/test-utils';\nimport TAlert from '@/components/TAlert.vue';\nimport { scopedParamsAsString, pa"
  },
  {
    "path": "src/__tests/components/TButton.spec.ts",
    "chars": 9799,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-non-null-assertion */\n\ni"
  },
  {
    "path": "src/__tests/components/TCard.spec.ts",
    "chars": 5001,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport { TCardConfig } from '@variantjs/core';\nimport TCard from '@/comp"
  },
  {
    "path": "src/__tests/components/TCheckbox.integration.spec.ts",
    "chars": 2339,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { render, fireEvent } from '@testing-library/vue';\n"
  },
  {
    "path": "src/__tests/components/TCheckbox.spec.ts",
    "chars": 6085,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-non-null-assertion */\nim"
  },
  {
    "path": "src/__tests/components/TDialog.spec.ts",
    "chars": 31029,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { mount } from '@vue/test-utils';\nimport { h } from 'vue'"
  },
  {
    "path": "src/__tests/components/TDropdown.spec.ts",
    "chars": 37506,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ni"
  },
  {
    "path": "src/__tests/components/TInput.integration.spec.ts",
    "chars": 2660,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { render, fireEvent } from '@testing-library/vue';\n"
  },
  {
    "path": "src/__tests/components/TInput.spec.ts",
    "chars": 6349,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ni"
  },
  {
    "path": "src/__tests/components/TInputGroup.spec.ts",
    "chars": 8105,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport { TInputGroupConfig } from '@variantjs/core';\nimport TInputGroup "
  },
  {
    "path": "src/__tests/components/TModal.spec.ts",
    "chars": 18624,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { shallowMount, mount } from '@vue/test-utils';\nimport * "
  },
  {
    "path": "src/__tests/components/TRadio.integration.spec.ts",
    "chars": 2970,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { render, fireEvent } from '@testing-library/vue';\n"
  },
  {
    "path": "src/__tests/components/TRadio.spec.ts",
    "chars": 5999,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ni"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectClearButton.spec.ts",
    "chars": 342,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport RichSelectClearButton from '../../../components/TRichSelect/RichS"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectDropdown.spec.ts",
    "chars": 1724,
    "preview": "import { NormalizedOptions } from '@variantjs/core';\nimport { shallowMount } from '@vue/test-utils';\nimport { computed }"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectOption.spec.ts",
    "chars": 14279,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NormalizedOption } from '@variantjs/core';\nimport { mou"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectOptionsList.spec.ts",
    "chars": 8012,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NormalizedOptions } from '@variantjs/core';\nimport { sh"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectSearchInput.spec.ts",
    "chars": 2949,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { shallowMount } from '@vue/test-utils';\nimport { ref } f"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectState.spec.ts",
    "chars": 2622,
    "preview": "import { NormalizedOption } from '@variantjs/core';\nimport { shallowMount } from '@vue/test-utils';\nimport { computed, r"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectTrigger.spec.ts",
    "chars": 7738,
    "preview": "import { NormalizedOption } from '@variantjs/core';\nimport { shallowMount } from '@vue/test-utils';\nimport { computed, r"
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectTriggerTags.spec.ts",
    "chars": 867,
    "preview": "import { NormalizedOption } from '@variantjs/core';\nimport { shallowMount } from '@vue/test-utils';\nimport { computed } "
  },
  {
    "path": "src/__tests/components/TRichSelect/RichSelectTriggerTagsTag.spec.ts",
    "chars": 11222,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport RichSelectTriggerTagsTag from '../../../components/TRichSelect/Ri"
  },
  {
    "path": "src/__tests/components/TRichSelect.spec.ts",
    "chars": 60364,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NormalizedOption, normalizeOptions } from '@variantjs/c"
  },
  {
    "path": "src/__tests/components/TSelect/TSelectOption.spec.ts",
    "chars": 2301,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { mount, shallowMount } from '@vue/test-utils';\nimp"
  },
  {
    "path": "src/__tests/components/TSelect.integration.spec.ts",
    "chars": 1894,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { render, fireEvent } from '@testing-library/vue';\n"
  },
  {
    "path": "src/__tests/components/TSelect.spec.ts",
    "chars": 6295,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ni"
  },
  {
    "path": "src/__tests/components/TTag.spec.ts",
    "chars": 2056,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport TTag from '@/components/TTag.vue';\n\ndescribe('TTag.vue', () => {\n"
  },
  {
    "path": "src/__tests/components/TTextarea.integration.spec.ts",
    "chars": 2690,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { render, fireEvent } from '@testing-library/vue';\n"
  },
  {
    "path": "src/__tests/components/TTextarea.spec.ts",
    "chars": 6326,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ni"
  },
  {
    "path": "src/__tests/components/TToggle.spec.ts",
    "chars": 4985,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport TToggle from '../../components/TToggle.vue';\n\ndescribe('TToggle.v"
  },
  {
    "path": "src/__tests/components/icons/CustomIcon.spec.ts",
    "chars": 1623,
    "preview": "import { mount } from '@vue/test-utils';\nimport CustomIcon from '@/icons/CustomIcon.vue';\nimport CloseIcon from '@/icons"
  },
  {
    "path": "src/__tests/components/misc/TextPlaceholder.spec.ts",
    "chars": 2000,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport TextPlaceholder from '../../../components/misc/TextPlaceholder.vu"
  },
  {
    "path": "src/__tests/components/misc/Transitionable.spec.ts",
    "chars": 249,
    "preview": "import Transitionable from '../../../components/misc/Transitionable.vue';\n\ndescribe('Transitionable', () => {\n  it('defa"
  },
  {
    "path": "src/__tests/index.spec.ts",
    "chars": 1450,
    "preview": "import * as library from '../index';\n\ndescribe('main file', () => {\n  it('provides all the needed data', () => {\n    exp"
  },
  {
    "path": "src/__tests/plugin.spec.ts",
    "chars": 2729,
    "preview": "/* eslint-disable vue/one-component-per-file */\nimport { createApp } from 'vue';\nimport { variantJS } from '..';\nimport "
  },
  {
    "path": "src/__tests/testUtils.ts",
    "chars": 1163,
    "preview": "/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\n/* eslint-disable @typescript-eslint/no-explicit-"
  },
  {
    "path": "src/__tests/use/useActivableOption.spec.ts",
    "chars": 10129,
    "preview": "import { NormalizedOption } from '@variantjs/core';\nimport {\n  ComputedRef, computed, ref, Ref, nextTick,\n} from 'vue';\n"
  },
  {
    "path": "src/__tests/use/useConfiguration.spec.ts",
    "chars": 4600,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport useConfiguration from '../../use/useConfiguration';\nimport { useS"
  },
  {
    "path": "src/__tests/use/useConfigurationWithClassesList.spec.ts",
    "chars": 4544,
    "preview": "import useConfigurationWithClassesList from '../../use/useConfigurationWithClassesList';\nimport { useSetup } from './use"
  },
  {
    "path": "src/__tests/use/useFetchsOptions.spec.ts",
    "chars": 38054,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { nextTick, ref } from 'vue';\nimport { FetchOptionsFn, Mi"
  },
  {
    "path": "src/__tests/use/useInjectsClassesList.spec.ts",
    "chars": 954,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport { defineComponent } from 'vue';\nimport useInjectsClassesList from"
  },
  {
    "path": "src/__tests/use/useInjectsConfiguration.spec.ts",
    "chars": 970,
    "preview": "import { shallowMount } from '@vue/test-utils';\nimport { defineComponent } from 'vue';\nimport useInjectsConfiguration fr"
  },
  {
    "path": "src/__tests/use/useMulipleableVModel.spec.ts",
    "chars": 2671,
    "preview": "import useMulipleableVModel from '../../use/useMulipleableVModel';\nimport { useSetup } from './useSetup';\n\ndescribe('use"
  },
  {
    "path": "src/__tests/use/useMultioptions.spec.ts",
    "chars": 2898,
    "preview": "import { ref } from 'vue';\nimport useMultioptions from '../../use/useMultioptions';\nimport { useSetup } from './useSetup"
  },
  {
    "path": "src/__tests/use/useSelectableOption.spec.ts",
    "chars": 10566,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NormalizedOption } from '@variantjs/core';\nimport {\n  C"
  },
  {
    "path": "src/__tests/use/useSetup.ts",
    "chars": 1379,
    "preview": "/* eslint-disable vue/one-component-per-file */\nimport {\n  defineComponent, createApp, h, ComponentPropsOptions,\n} from "
  },
  {
    "path": "src/__tests/use/useVModel.spec.ts",
    "chars": 608,
    "preview": "import useVModel from '../../use/useVModel';\nimport { useSetup } from './useSetup';\n\ndescribe('useVModel', () => {\n  con"
  },
  {
    "path": "src/__tests/utils/createDialogProgramatically.spec.ts",
    "chars": 3174,
    "preview": "import { DialogIcon, DialogType } from '@variantjs/core';\nimport { VariantJSConfiguration } from '../..';\nimport createD"
  },
  {
    "path": "src/__tests/utils/emitter.spec.ts",
    "chars": 1831,
    "preview": "import { Emitter } from '../../utils/emitter';\n\ndescribe('Emitter', () => {\n  let emitter: Emitter;\n\n  beforeEach(() => "
  },
  {
    "path": "src/__tests/utils/getVariantProps.spec.ts",
    "chars": 624,
    "preview": "/* eslint-disable vue/one-component-per-file */\n\nimport { getVariantProps } from '../../utils/getVariantProps';\n\ndescrib"
  },
  {
    "path": "src/__tests/utils/popper.spec.ts",
    "chars": 1146,
    "preview": "/* eslint-disable @typescript-eslint/no-non-null-assertion */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nim"
  },
  {
    "path": "src/__tests/utils/svgToVueComponent.spec.ts",
    "chars": 961,
    "preview": "import { VNode } from 'vue';\nimport { svgToVueComponent } from '../../utils/svgToVueComponent';\n\ndescribe('svgToVueCompo"
  },
  {
    "path": "src/assets/tailwind.css",
    "chars": 58,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;"
  },
  {
    "path": "src/components/TAlert.vue",
    "chars": 3923,
    "preview": "<template>\n  <transitionable\n    :classes-list=\"configuration.classesList\"\n    :enabled=\"animate\"\n  >\n    <component\n   "
  },
  {
    "path": "src/components/TButton.vue",
    "chars": 2610,
    "preview": "<template>\n  <component\n    :is=\"routerLinkTag\"\n    v-if=\"useRouterLink\"\n    :to=\"configuration.to\"\n    :replace=\"config"
  },
  {
    "path": "src/components/TCard.vue",
    "chars": 1948,
    "preview": "<template>\n  <component\n    :is=\"configuration.tagName\"\n    :class=\"configuration.classesList?.wrapper\"\n    v-bind=\"attr"
  },
  {
    "path": "src/components/TCheckbox.vue",
    "chars": 1088,
    "preview": "<template>\n  <input\n    v-model=\"localValue\"\n    type=\"checkbox\"\n    v-bind=\"attributes\"\n  >\n</template>\n\n<script lang=\""
  },
  {
    "path": "src/components/TDialog.vue",
    "chars": 20892,
    "preview": "<template>\n  <t-modal\n    ref=\"modalRef\"\n    v-model=\"showModel\"\n    :modal-attributes=\"configuration.dialogAttributes\"\n"
  },
  {
    "path": "src/components/TDropdown.vue",
    "chars": 13907,
    "preview": "<template>\n  <component\n    :is=\"tagName\"\n    ref=\"trigger\"\n    :type=\"tagName === 'button' ? 'button' : undefined\"\n    "
  },
  {
    "path": "src/components/TInput.vue",
    "chars": 1194,
    "preview": "<template>\n  <input\n    v-if=\"usesVModel\"\n    v-model=\"localValue\"\n    v-bind=\"attributes\"\n  >\n  <input\n    v-else\n    v"
  },
  {
    "path": "src/components/TInputGroup.vue",
    "chars": 3099,
    "preview": "<template>\n  <component\n    :is=\"tagName\"\n    ref=\"wrapper\"\n    :class=\"configuration.classesList?.wrapper\"\n    v-bind=\""
  },
  {
    "path": "src/components/TModal.vue",
    "chars": 10536,
    "preview": "<template>\n  <teleport\n    v-if=\"showComponent\"\n    :to=\"configuration.teleportTo\"\n    :disabled=\"! configuration.telepo"
  },
  {
    "path": "src/components/TRadio.vue",
    "chars": 1004,
    "preview": "<template>\n  <input\n    v-model=\"localValue\"\n    type=\"radio\"\n    v-bind=\"attributes\"\n  >\n</template>\n\n<script lang=\"ts\""
  },
  {
    "path": "src/components/TRichSelect/RichSelectClearButton.vue",
    "chars": 651,
    "preview": "<template>\n  <button\n    type=\"button\"\n    data-rich-select-focusable\n    :class=\"className\"\n  >\n    <slot name=\"clearBu"
  },
  {
    "path": "src/components/TRichSelect/RichSelectDropdown.vue",
    "chars": 1850,
    "preview": "<template>\n  <div :class=\"className\">\n    <rich-select-search-input\n      v-if=\"showSearchInput\"\n      ref=\"searchInput\""
  },
  {
    "path": "src/components/TRichSelect/RichSelectOption.vue",
    "chars": 5269,
    "preview": "<template>\n  <li\n    v-if=\"hasChildren\"\n    role=\"optgroup\"\n    :class=\"classesList.optgroup\"\n  >\n    <div :class=\"class"
  },
  {
    "path": "src/components/TRichSelect/RichSelectOptionsList.vue",
    "chars": 3609,
    "preview": "<template>\n  <ul\n    v-if=\"showOptions\"\n    :class=\"classesList.optionsList\"\n    :style=\"usesMaxHeight? `max-height: ${m"
  },
  {
    "path": "src/components/TRichSelect/RichSelectSearchInput.vue",
    "chars": 1870,
    "preview": "<template>\n  <div :class=\"classesList.searchWrapper\">\n    <input\n      ref=\"search\"\n      v-model=\"searchQuery\"\n      da"
  },
  {
    "path": "src/components/TRichSelect/RichSelectState.vue",
    "chars": 1714,
    "preview": "<template>\n  <slot\n    name=\"stateFeedback\"\n    :fetching-options=\"fetchingOptions\"\n    :needs-more-chars-to-fetch=\"need"
  },
  {
    "path": "src/components/TRichSelect/RichSelectTrigger.vue",
    "chars": 3743,
    "preview": "<template>\n  <slot\n    v-if=\"isFetchingOptionsWhileClosed || !hasSelectedOption\"\n    name=\"placeholder\"\n    :is-fetching"
  },
  {
    "path": "src/components/TRichSelect/RichSelectTriggerTags.vue",
    "chars": 1202,
    "preview": "<template>\n  <div\n    :class=\"className\"\n  >\n    <rich-select-trigger-tags-tag\n      v-for=\"(option, index) in selectedO"
  },
  {
    "path": "src/components/TRichSelect/RichSelectTriggerTagsTag.vue",
    "chars": 3493,
    "preview": "<template>\n  <button\n    type=\"button\"\n    :class=\"classesList.tag\"\n    :disabled=\"isDisabled\"\n    data-rich-select-focu"
  },
  {
    "path": "src/components/TRichSelect.vue",
    "chars": 20716,
    "preview": "<template>\n  <div\n    :class=\"configuration.classesList?.wrapper\"\n    v-bind=\"attributes\"\n  >\n    <t-select\n      v-mode"
  },
  {
    "path": "src/components/TSelect/TSelectOption.vue",
    "chars": 1097,
    "preview": "<template>\n  <optgroup\n    v-if=\"hasChildren\"\n    :data-value=\"option.value !== undefined ? String(option.value) : undef"
  },
  {
    "path": "src/components/TSelect.vue",
    "chars": 2274,
    "preview": "<template>\n  <select\n    v-model=\"localValue\"\n    :multiple=\"configuration.multiple\"\n    v-bind=\"attributes\"\n  >\n    <t-"
  },
  {
    "path": "src/components/TTag.vue",
    "chars": 905,
    "preview": "<template>\n  <component\n    :is=\"configuration.tagName\"\n    v-bind=\"attributes\"\n  >\n    <slot :configuration=\"configurat"
  },
  {
    "path": "src/components/TTextarea.vue",
    "chars": 1206,
    "preview": "<template>\n  <textarea\n    v-if=\"usesVModel\"\n    v-model=\"localValue\"\n    v-bind=\"attributes\"\n  />\n  <textarea\n    v-els"
  },
  {
    "path": "src/components/TToggle.vue",
    "chars": 5945,
    "preview": "<template>\n  <button\n    type=\"button\"\n    role=\"checkbox\"\n    :aria-checked=\"isChecked ? 'true' : 'false'\"\n    :class=\""
  },
  {
    "path": "src/components/misc/TextPlaceholder.vue",
    "chars": 738,
    "preview": "<template>\n  <span :class=\"className\">\n    <slot>\n      <template v-if=\"placeholder !== undefined\">{{ placeholder }}</te"
  },
  {
    "path": "src/components/misc/Transitionable.vue",
    "chars": 741,
    "preview": "<template>\n  <transition\n    :enter-active-class=\"classesList?.enterActiveClass\"\n    :enter-from-class=\"classesList?.ent"
  },
  {
    "path": "src/development/About.vue",
    "chars": 471,
    "preview": "<template>\n  <t-card>\n    <template #header>\n      <h1>VariantJS</h1>\n    </template>\n\n    <p>Development playground use"
  },
  {
    "path": "src/development/Alert.vue",
    "chars": 1723,
    "preview": "<template>\n  <t-card>\n    <template #header>\n      <h1>Alert</h1>\n    </template>\n\n    <TInputGroup\n      label=\"Regular"
  },
  {
    "path": "src/development/App.vue",
    "chars": 938,
    "preview": "<template>\n  <div class=\"pb-36\">\n    <div class=\"relative z-10 p-4 sm:hidden\">\n      <t-dropdown ref=\"dropdown\">\n       "
  },
  {
    "path": "src/development/AppMenu.vue",
    "chars": 989,
    "preview": "<template>\n  <div class=\"flex flex-col space-y-3\">\n    <t-button to=\"/\">\n      Home\n    </t-button>\n    <t-button to=\"/t"
  },
  {
    "path": "src/development/Attributes.vue",
    "chars": 1325,
    "preview": "<template>\n  <t-card\n    class=\"w-full\"\n    header=\"Attributes\"\n  >\n    <div class=\"grid grid-cols-1 gap-6\">\n      <p>Ge"
  },
  {
    "path": "src/development/Check.vue",
    "chars": 3375,
    "preview": "<template>\n  <t-card header=\"Options\">\n    <TInputGroup\n      label=\"Toggle/Checkbox model sync \"\n      class=\"mb-4\"\n   "
  },
  {
    "path": "src/development/Dialog.vue",
    "chars": 9480,
    "preview": "<template>\n  <t-card class=\"w-full\">\n    <template #header>\n      <h1>Dialog</h1>\n    </template>\n\n    <TInputGroup\n    "
  },
  {
    "path": "src/development/Dropdown.vue",
    "chars": 8051,
    "preview": "<template>\n  <t-card>\n    <template #header>\n      <h1>Dropdown</h1>\n    </template>\n\n    <TInputGroup\n      label=\"Togg"
  },
  {
    "path": "src/development/Home.vue",
    "chars": 3312,
    "preview": "<template>\n  <t-card>\n    <template #header>\n      <h1>List of components</h1>\n    </template>\n\n    <t-alert>\n      Lore"
  },
  {
    "path": "src/development/Modal.vue",
    "chars": 4373,
    "preview": "<template>\n  <t-card>\n    <template #header>\n      <h1>Modal</h1>\n    </template>\n\n    <TInputGroup\n      label=\"Open wi"
  },
  {
    "path": "src/development/Multioptions.vue",
    "chars": 1508,
    "preview": "<template>\n  <t-card\n    header=\"Multioptions\"\n  >\n    <div class=\"grid grid-cols-1 gap-6\">\n      <p>Multioptions compon"
  },
  {
    "path": "src/development/Options.vue",
    "chars": 3693,
    "preview": "<template>\n  <t-card header=\"Options\">\n    <div class=\"grid grid-cols-1 gap-6\">\n      <p>Options components v-model sync"
  },
  {
    "path": "src/development/RichSelect.vue",
    "chars": 4548,
    "preview": "<template>\n  <t-card header=\"RichSelect\">\n    <t-input-group\n      label=\"Disabled rich select\"\n      class=\"mb-4\"\n    >"
  },
  {
    "path": "src/development/TSubmit.vue",
    "chars": 218,
    "preview": "<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport TButton from '../components/TButton.vue';\n\nexport defau"
  },
  {
    "path": "src/development/Theme.vue",
    "chars": 5978,
    "preview": "<template>\n  <t-card class=\"w-full\">\n    <template #header>\n      Simple component\n    </template>\n\n    <div class=\"grid"
  },
  {
    "path": "src/development/router.ts",
    "chars": 1161,
    "preview": "import { createRouter, createWebHashHistory } from 'vue-router';\n\nimport Home from './Home.vue';\nimport About from './Ab"
  },
  {
    "path": "src/icons/CheckCircleIcon.vue",
    "chars": 303,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke=\"currentColo"
  },
  {
    "path": "src/icons/CheckmarkIcon.vue",
    "chars": 324,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/icons/CloseIcon.vue",
    "chars": 260,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke=\"currentColo"
  },
  {
    "path": "src/icons/CrossCircleIcon.vue",
    "chars": 326,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke=\"currentColo"
  },
  {
    "path": "src/icons/CustomIcon.vue",
    "chars": 609,
    "preview": "<template>\n  <component :is=\"child\" />\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\n"
  },
  {
    "path": "src/icons/ExclamationIcon.vue",
    "chars": 390,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke=\"currentColo"
  },
  {
    "path": "src/icons/InformationCircleIcon.vue",
    "chars": 335,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    class=\"w-6 h-6\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n   "
  },
  {
    "path": "src/icons/LoadingIcon.vue",
    "chars": 735,
    "preview": "<template>\n  <svg\n    viewBox=\"0 0 20 20\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n  >\n    <circle\n      cx=\"10\"\n      cy="
  },
  {
    "path": "src/icons/QuestionMarkCircleIcon.vue",
    "chars": 411,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    fill=\"none\"\n    viewBox=\"0 0 24 24\"\n    stroke=\"currentColo"
  },
  {
    "path": "src/icons/SelectorIcon.vue",
    "chars": 416,
    "preview": "<template>\n  <svg\n    fill=\"currentColor\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n  ><path\n    cl"
  },
  {
    "path": "src/icons/SolidCheckCircleIcon.vue",
    "chars": 343,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/icons/SolidCrossCircleIcon.vue",
    "chars": 425,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/icons/SolidExclamationIcon.vue",
    "chars": 419,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/icons/SolidInformationCircleIcon.vue",
    "chars": 338,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/icons/SolidQuestionMarkCircleIcon.vue",
    "chars": 392,
    "preview": "<template>\n  <svg\n    xmlns=\"http://www.w3.org/2000/svg\"\n    viewBox=\"0 0 20 20\"\n    fill=\"currentColor\"\n  >\n    <path\n "
  },
  {
    "path": "src/index.ts",
    "chars": 2642,
    "preview": "// Import Utils\nimport { Emitter } from './utils/emitter';\nimport { getVariantProps, getVariantPropsWithClassesList } fr"
  },
  {
    "path": "src/main.ts",
    "chars": 509,
    "preview": "import './assets/tailwind.css';\nimport { createApp } from 'vue';\nimport plugin from './plugin';\nimport { VariantJSConfig"
  },
  {
    "path": "src/plugin.ts",
    "chars": 3183,
    "preview": "import {\n  DialogHideFn, DialogProgramaticallyShowFn, DialogResponse, DialogShowFn, DialogType, ModalHideFn, ModalShowFn"
  },
  {
    "path": "src/shims-vue.d.ts",
    "chars": 261,
    "preview": "/* eslint-disable @typescript-eslint/ban-types */\ndeclare module '*.vue' {\n  import { DefineComponent } from 'vue';\n\n  /"
  },
  {
    "path": "src/types/components/t-alert.ts",
    "chars": 453,
    "preview": "import { WithVariantPropsAndClassesList, TAlertClassesValidKeys, Data } from '@variantjs/core';\nimport { HTMLAttributes "
  },
  {
    "path": "src/types/components/t-button.ts",
    "chars": 524,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { ButtonHTMLAttributes } from 'vue';\nimport { VueRouteA"
  },
  {
    "path": "src/types/components/t-card.ts",
    "chars": 316,
    "preview": "import { WithVariantPropsAndClassesList, TCardClassesValidKeys, Data } from '@variantjs/core';\nimport { HTMLAttributes }"
  },
  {
    "path": "src/types/components/t-checkbox.ts",
    "chars": 572,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { InputHTMLAttributes } from 'vue';\nimport { ObjectWith"
  },
  {
    "path": "src/types/components/t-dialog.ts",
    "chars": 1565,
    "preview": "import {\n  WithVariantPropsAndClassesList, Data, TDialogClassesValidKeys, DialogPreconfirmFn, DialogInputValidatorFn,\n} "
  },
  {
    "path": "src/types/components/t-dropdown.ts",
    "chars": 673,
    "preview": "import { Placement, Options } from '@popperjs/core';\nimport { WithVariantPropsAndClassesList, TDropdownClassesValidKeys,"
  },
  {
    "path": "src/types/components/t-input-group.ts",
    "chars": 622,
    "preview": "import { WithVariantPropsAndClassesList, TInputGroupClassesValidKeys, Data } from '@variantjs/core';\nimport { HTMLAttrib"
  },
  {
    "path": "src/types/components/t-input.ts",
    "chars": 277,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { InputHTMLAttributes } from 'vue';\n\nexport type TInput"
  },
  {
    "path": "src/types/components/t-modal.ts",
    "chars": 716,
    "preview": "import { WithVariantPropsAndClassesList, Data, TModalClassesValidKeys } from '@variantjs/core';\nimport { BodyScrollOptio"
  },
  {
    "path": "src/types/components/t-radio.ts",
    "chars": 548,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { InputHTMLAttributes } from 'vue';\nimport { ObjectWith"
  },
  {
    "path": "src/types/components/t-rich-select.ts",
    "chars": 1566,
    "preview": "import {\n  Data, InputOptions, Measure, NormalizedOption, NormalizedOptions, TRichSelectClassesValidKeys, WithVariantPro"
  },
  {
    "path": "src/types/components/t-select.ts",
    "chars": 646,
    "preview": "import {\n  Data, InputOptions, NormalizedOption, NormalizedOptions, WithVariantProps,\n} from '@variantjs/core';\nimport {"
  },
  {
    "path": "src/types/components/t-tag.ts",
    "chars": 205,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { HTMLAttributes } from 'vue';\n\nexport type TTagOptions"
  },
  {
    "path": "src/types/components/t-textarea.ts",
    "chars": 286,
    "preview": "import { Data, WithVariantProps } from '@variantjs/core';\nimport { InputHTMLAttributes } from 'vue';\n\nexport type TTexta"
  },
  {
    "path": "src/types/components/t-toggle.ts",
    "chars": 552,
    "preview": "import { WithVariantPropsAndClassesList, TToggleClassesValidKeys, Data } from '@variantjs/core';\nimport { HTMLAttributes"
  },
  {
    "path": "src/types/helpers.ts",
    "chars": 203,
    "preview": "type ObjectWithProperties<P> = Record<string | number | symbol, P>;\n\ntype KeysOfType<T, TProp> = { [P in keyof T]: T[P] "
  },
  {
    "path": "src/types/index.ts",
    "chars": 1437,
    "preview": "export { VariantJSConfiguration, VariantJSProps, VariantJSWithClassesListProps } from './variantCore';\nexport { TInputVa"
  },
  {
    "path": "src/types/misc.ts",
    "chars": 728,
    "preview": "import { Data, InputOptions } from '@variantjs/core';\n\ntype Truthy = boolean | string;\n\n// eslint-disable-next-line @typ"
  },
  {
    "path": "src/types/utils.ts",
    "chars": 577,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\ntype EmitterFunction = (...args : any[]) => void;\n\ntype EmitterE"
  },
  {
    "path": "src/types/variantCore.ts",
    "chars": 3224,
    "preview": "import {\n  CSSRawClassesList,\n  CSSClass, Variants, VariantsWithClassesList, WithVariantProps, WithVariantPropsAndClasse"
  },
  {
    "path": "src/types/vueRouter.ts",
    "chars": 1900,
    "preview": "// Copied from the types on vue/node_modules/vue-router/dist/vue-router.d.ts\n// so we dont need to add any extra depende"
  },
  {
    "path": "src/use/useActivableOption.ts",
    "chars": 3192,
    "preview": "import { isEqual, NormalizedOption, normalizedOptionIsDisabled } from '@variantjs/core';\nimport {\n  computed, ComputedRe"
  },
  {
    "path": "src/use/useConfiguration.ts",
    "chars": 3084,
    "preview": "import {\n  computed, inject, camelize, getCurrentInstance, ComponentInternalInstance, ComputedRef, watch, reactive,\n} fr"
  },
  {
    "path": "src/use/useConfigurationWithClassesList.ts",
    "chars": 1153,
    "preview": "import {\n  computed, getCurrentInstance, reactive, watch,\n} from 'vue';\nimport { Data, parseVariantWithClassesList } fro"
  },
  {
    "path": "src/use/useFetchsOptions.ts",
    "chars": 6832,
    "preview": "import {\n  debounce, filterOptions, flattenOptions, InputOptions, NormalizedOption, NormalizedOptions, normalizeOptions,"
  },
  {
    "path": "src/use/useInjectsClassesList.ts",
    "chars": 384,
    "preview": "import { CSSClassesList } from '@variantjs/core';\nimport { ComputedRef, computed } from 'vue';\nimport useInjectsConfigur"
  },
  {
    "path": "src/use/useInjectsClassesListClass.ts",
    "chars": 399,
    "preview": "import { CSSClass, get } from '@variantjs/core';\nimport { ComputedRef, computed } from 'vue';\nimport useInjectsConfigura"
  },
  {
    "path": "src/use/useInjectsConfiguration.ts",
    "chars": 262,
    "preview": "import { Data, WithVariantPropsAndClassesList } from '@variantjs/core';\nimport { inject } from 'vue';\n\nexport default fu"
  },
  {
    "path": "src/use/useMulipleableVModel.ts",
    "chars": 1175,
    "preview": "import { Data } from '@variantjs/core';\nimport {\n  computed, ref, getCurrentInstance, Ref, watch,\n} from 'vue';\n\nexport "
  },
  {
    "path": "src/use/useMultioptions.ts",
    "chars": 976,
    "preview": "import {\n  flattenOptions,\n  InputOptions,\n  NormalizedOption,\n  NormalizedOptions,\n  normalizeOptions,\n} from '@variant"
  },
  {
    "path": "src/use/useSelectableOption.ts",
    "chars": 4010,
    "preview": "/* eslint-disable no-param-reassign */\nimport {\n  addToArray, isEqual, NormalizedOption, substractFromArray,\n} from '@va"
  },
  {
    "path": "src/use/useVModel.ts",
    "chars": 475,
    "preview": "import { Data } from '@variantjs/core';\nimport {\n  getCurrentInstance, Ref, watch, ref,\n} from 'vue';\n\nexport default fu"
  },
  {
    "path": "src/utils/createDialogProgramatically.ts",
    "chars": 1476,
    "preview": "import { DialogResponse, DialogType } from '@variantjs/core';\nimport { createApp } from 'vue';\nimport { TDialogOptions }"
  },
  {
    "path": "src/utils/emitter.ts",
    "chars": 1276,
    "preview": "/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { EmitterEvents, EmitterFunction, EmitterInterface } from"
  },
  {
    "path": "src/utils/getVariantProps.ts",
    "chars": 1382,
    "preview": "import {\n  CSSClass, CSSRawClassesList, Data, Variants, VariantsWithClassesList,\n} from '@variantjs/core';\n\nimport { Pro"
  },
  {
    "path": "src/utils/popper.ts",
    "chars": 668,
    "preview": "import { Modifier, ModifierArguments } from '@popperjs/core';\nimport { Data } from '@variantjs/core';\n\nconst sameWidthMo"
  },
  {
    "path": "src/utils/svgToVueComponent.ts",
    "chars": 1232,
    "preview": "import { Data } from '@variantjs/core';\nimport { h, VNode, VNodeProps } from 'vue';\n\nconst icons: {\n  [key: string]: VNo"
  },
  {
    "path": "tailwind.config.js",
    "chars": 195,
    "preview": "module.exports = {\n  content: [\n    './src/development/**/*.{html,js,vue,ts}',\n    './node_modules/@variantjs/core/src/c"
  },
  {
    "path": "tsconfig.json",
    "chars": 464,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"declaration\": true,\n    \"outDir\": \"./dist\""
  },
  {
    "path": "vite.config.ts",
    "chars": 684,
    "preview": "import path from 'path'\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport typescript from "
  },
  {
    "path": "vite.demo.config.ts",
    "chars": 157,
    "preview": "import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\n\n// https://vitejs.dev/config/\nexport default d"
  }
]

About this extraction

This page contains the full source code of the variantjs/vue GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 165 files (603.0 KB), approximately 148.9k tokens, and a symbol index with 87 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!