@pequity/squirrel 8.3.5 → 8.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/cjs/chunks/p-dropdown-select.js +1 -4
  2. package/dist/cjs/chunks/p-tabs-pills.js +44 -0
  3. package/dist/cjs/index.js +2 -0
  4. package/dist/cjs/p-tabs-pills.js +3 -0
  5. package/dist/es/chunks/p-dropdown-select.js +1 -4
  6. package/dist/es/chunks/p-tabs-pills.js +45 -0
  7. package/dist/es/index.js +4 -2
  8. package/dist/es/p-tabs-pills.js +4 -0
  9. package/dist/squirrel/components/index.d.ts +2 -1
  10. package/dist/squirrel/components/p-tabs-pills/p-tabs-pills.vue.d.ts +21 -0
  11. package/package.json +26 -32
  12. package/squirrel/components/index.ts +2 -0
  13. package/squirrel/components/p-action-bar/p-action-bar.stories.js +2 -2
  14. package/squirrel/components/p-alert/p-alert.stories.js +1 -1
  15. package/squirrel/components/p-btn/p-btn.stories.js +2 -2
  16. package/squirrel/components/p-checkbox/p-checkbox.stories.js +1 -1
  17. package/squirrel/components/p-close-btn/p-close-btn.stories.js +2 -2
  18. package/squirrel/components/p-dropdown-select/p-dropdown-select.spec.js +12 -25
  19. package/squirrel/components/p-dropdown-select/p-dropdown-select.vue +1 -5
  20. package/squirrel/components/p-file-upload/p-file-upload.spec.js +1 -1
  21. package/squirrel/components/p-input-number/p-input-number.spec.js +1 -1
  22. package/squirrel/components/p-ring-loader/p-ring-loader.spec.js +1 -1
  23. package/squirrel/components/p-select-btn/p-select-btn.stories.js +1 -1
  24. package/squirrel/components/p-select-pill/p-select-pill.stories.js +1 -1
  25. package/squirrel/components/p-table-header-cell/p-table-header-cell.stories.js +1 -1
  26. package/squirrel/components/p-tabs/p-tabs.stories.js +1 -1
  27. package/squirrel/components/p-tabs-pills/p-tabs-pills.spec.js +117 -0
  28. package/squirrel/components/p-tabs-pills/p-tabs-pills.stories.js +139 -0
  29. package/squirrel/components/p-tabs-pills/p-tabs-pills.vue +60 -0
  30. package/squirrel/components/p-toggle/p-toggle.spec.js +1 -1
  31. package/squirrel/composables/useInputClasses.spec.js +1 -1
@@ -260,9 +260,6 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
260
260
  return rest;
261
261
  });
262
262
  const style = vue.computed(() => $attrs.style);
263
- const selectableItemsCount = vue.computed(
264
- () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
265
- );
266
263
  vue.watch(
267
264
  dropdownShow,
268
265
  (nV) => {
@@ -502,7 +499,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
502
499
  ], 8, _hoisted_5)
503
500
  ], 2);
504
501
  }), 128))
505
- ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_6, vue.toDisplayString(__props.multiple ? vue.unref(selectedItems).length === selectableItemsCount.value ? "All options selected" : `${vue.unref(selectedItems).length} option${vue.unref(selectedItems).length > 1 ? "s" : ""} selected` : vue.unref(selectedItems)[0][__props.itemText]), 1))
502
+ ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_6, vue.toDisplayString(__props.multiple ? vue.unref(selectedItems).length === vue.unref(computedItems).length ? "All options selected" : `${vue.unref(selectedItems).length} option${vue.unref(selectedItems).length > 1 ? "s" : ""} selected` : vue.unref(selectedItems)[0][__props.itemText]), 1))
506
503
  ]),
507
504
  __props.clearable && vue.unref(selectedItems).length ? (vue.openBlock(), vue.createElementBlock("button", {
508
505
  key: 2,
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ const vue = require("vue");
3
+ const _hoisted_1 = {
4
+ class: "flex h-6 w-fit flex-row space-x-1 overflow-x-auto rounded bg-p-gray-10 p-1 text-sm font-medium text-p-gray-50",
5
+ "aria-label": "Tabs Pills",
6
+ role: "tablist",
7
+ "aria-orientation": "horizontal"
8
+ };
9
+ const _hoisted_2 = ["disabled", "aria-selected", "data-tab", "aria-controls", "onClick"];
10
+ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
11
+ ...{
12
+ name: "PTabsPills"
13
+ },
14
+ __name: "p-tabs-pills",
15
+ props: {
16
+ modelValue: { type: [String, Number, Boolean], default: "" },
17
+ items: { default: () => [] },
18
+ itemText: { default: "text" },
19
+ itemValue: { default: "value" }
20
+ },
21
+ emits: ["update:modelValue"],
22
+ setup(__props) {
23
+ return (_ctx, _cache) => {
24
+ return vue.openBlock(), vue.createElementBlock("nav", _hoisted_1, [
25
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(_ctx.items, (tab) => {
26
+ return vue.openBlock(), vue.createElementBlock("button", {
27
+ key: String(tab[_ctx.itemValue]),
28
+ type: "button",
29
+ class: vue.normalizeClass(["rounded px-2 pb-px leading-none transition-all duration-300", [
30
+ tab.disabled ? "text-p-gray-30" : tab[_ctx.itemValue] === _ctx.modelValue ? "bg-night text-surface hover:bg-night hover:text-surface" : "hover:text-p-gray-70"
31
+ ]]),
32
+ disabled: !!tab.disabled,
33
+ role: "tab",
34
+ "aria-selected": tab[_ctx.itemValue] === _ctx.modelValue,
35
+ "data-tab": `#${tab[_ctx.itemValue]}`,
36
+ "aria-controls": String(tab[_ctx.itemValue]),
37
+ onClick: ($event) => _ctx.$emit("update:modelValue", tab[_ctx.itemValue])
38
+ }, vue.toDisplayString(tab[_ctx.itemText]), 11, _hoisted_2);
39
+ }), 128))
40
+ ]);
41
+ };
42
+ }
43
+ });
44
+ exports._sfc_main = _sfc_main;
package/dist/cjs/index.js CHANGED
@@ -52,6 +52,7 @@ const pTableFilterIcon = require("./p-table-filter-icon.js");
52
52
  const pTableLoader_vue_vue_type_script_setup_true_lang = require("./chunks/p-table-loader.js");
53
53
  const pTableSort = require("./p-table-sort.js");
54
54
  const pTabs_vue_vue_type_script_setup_true_lang = require("./chunks/p-tabs.js");
55
+ const pTabsPills_vue_vue_type_script_setup_true_lang = require("./chunks/p-tabs-pills.js");
55
56
  const pTextarea_vue_vue_type_script_setup_true_lang = require("./chunks/p-textarea.js");
56
57
  const pToggle = require("./p-toggle.js");
57
58
  const config = require("./config.js");
@@ -1086,6 +1087,7 @@ exports.PFilterIcon = pTableFilterIcon;
1086
1087
  exports.PTableLoader = pTableLoader_vue_vue_type_script_setup_true_lang._sfc_main;
1087
1088
  exports.SORTING_TYPES = pTableSort.SORTING_TYPES;
1088
1089
  exports.PTabs = pTabs_vue_vue_type_script_setup_true_lang._sfc_main;
1090
+ exports.PTabsPills = pTabsPills_vue_vue_type_script_setup_true_lang._sfc_main;
1089
1091
  exports.PTextarea = pTextarea_vue_vue_type_script_setup_true_lang._sfc_main;
1090
1092
  exports.PToggle = pToggle;
1091
1093
  exports.squirrelTailwindConfig = config.squirrelTailwindConfig;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ const pTabsPills_vue_vue_type_script_setup_true_lang = require("./chunks/p-tabs-pills.js");
3
+ module.exports = pTabsPills_vue_vue_type_script_setup_true_lang._sfc_main;
@@ -259,9 +259,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
259
259
  return rest;
260
260
  });
261
261
  const style = computed(() => $attrs.style);
262
- const selectableItemsCount = computed(
263
- () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
264
- );
265
262
  watch(
266
263
  dropdownShow,
267
264
  (nV) => {
@@ -501,7 +498,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
501
498
  ], 8, _hoisted_5)
502
499
  ], 2);
503
500
  }), 128))
504
- ])) : (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(__props.multiple ? unref(selectedItems).length === selectableItemsCount.value ? "All options selected" : `${unref(selectedItems).length} option${unref(selectedItems).length > 1 ? "s" : ""} selected` : unref(selectedItems)[0][__props.itemText]), 1))
501
+ ])) : (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(__props.multiple ? unref(selectedItems).length === unref(computedItems).length ? "All options selected" : `${unref(selectedItems).length} option${unref(selectedItems).length > 1 ? "s" : ""} selected` : unref(selectedItems)[0][__props.itemText]), 1))
505
502
  ]),
506
503
  __props.clearable && unref(selectedItems).length ? (openBlock(), createElementBlock("button", {
507
504
  key: 2,
@@ -0,0 +1,45 @@
1
+ import { defineComponent, createElementBlock, openBlock, Fragment, renderList, normalizeClass, toDisplayString } from "vue";
2
+ const _hoisted_1 = {
3
+ class: "flex h-6 w-fit flex-row space-x-1 overflow-x-auto rounded bg-p-gray-10 p-1 text-sm font-medium text-p-gray-50",
4
+ "aria-label": "Tabs Pills",
5
+ role: "tablist",
6
+ "aria-orientation": "horizontal"
7
+ };
8
+ const _hoisted_2 = ["disabled", "aria-selected", "data-tab", "aria-controls", "onClick"];
9
+ const _sfc_main = /* @__PURE__ */ defineComponent({
10
+ ...{
11
+ name: "PTabsPills"
12
+ },
13
+ __name: "p-tabs-pills",
14
+ props: {
15
+ modelValue: { type: [String, Number, Boolean], default: "" },
16
+ items: { default: () => [] },
17
+ itemText: { default: "text" },
18
+ itemValue: { default: "value" }
19
+ },
20
+ emits: ["update:modelValue"],
21
+ setup(__props) {
22
+ return (_ctx, _cache) => {
23
+ return openBlock(), createElementBlock("nav", _hoisted_1, [
24
+ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.items, (tab) => {
25
+ return openBlock(), createElementBlock("button", {
26
+ key: String(tab[_ctx.itemValue]),
27
+ type: "button",
28
+ class: normalizeClass(["rounded px-2 pb-px leading-none transition-all duration-300", [
29
+ tab.disabled ? "text-p-gray-30" : tab[_ctx.itemValue] === _ctx.modelValue ? "bg-night text-surface hover:bg-night hover:text-surface" : "hover:text-p-gray-70"
30
+ ]]),
31
+ disabled: !!tab.disabled,
32
+ role: "tab",
33
+ "aria-selected": tab[_ctx.itemValue] === _ctx.modelValue,
34
+ "data-tab": `#${tab[_ctx.itemValue]}`,
35
+ "aria-controls": String(tab[_ctx.itemValue]),
36
+ onClick: ($event) => _ctx.$emit("update:modelValue", tab[_ctx.itemValue])
37
+ }, toDisplayString(tab[_ctx.itemText]), 11, _hoisted_2);
38
+ }), 128))
39
+ ]);
40
+ };
41
+ }
42
+ });
43
+ export {
44
+ _sfc_main as _
45
+ };
package/dist/es/index.js CHANGED
@@ -52,7 +52,8 @@ import { default as default11 } from "./p-table-filter-icon.js";
52
52
  import { _ as _20 } from "./chunks/p-table-loader.js";
53
53
  import { SORTING_TYPES } from "./p-table-sort.js";
54
54
  import { _ as _21 } from "./chunks/p-tabs.js";
55
- import { _ as _22 } from "./chunks/p-textarea.js";
55
+ import { _ as _22 } from "./chunks/p-tabs-pills.js";
56
+ import { _ as _23 } from "./chunks/p-textarea.js";
56
57
  import { default as default12 } from "./p-toggle.js";
57
58
  import { squirrelTailwindConfig } from "./config.js";
58
59
  import { S } from "./chunks/p-btn.types.js";
@@ -1079,7 +1080,8 @@ export {
1079
1080
  _sfc_main as PTableSort,
1080
1081
  PTableTd,
1081
1082
  _21 as PTabs,
1082
- _22 as PTextarea,
1083
+ _22 as PTabsPills,
1084
+ _23 as PTextarea,
1083
1085
  default12 as PToggle,
1084
1086
  P_ICON_ALIASES,
1085
1087
  S as SIZES,
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "./chunks/p-tabs-pills.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -50,6 +50,7 @@ import { SORTING_TYPES, type SortingType, type SortingTypeWithoutNoSorting } fro
50
50
  import PTableSort from './p-table-sort/p-table-sort.vue';
51
51
  import PTableTd from './p-table-td/p-table-td.vue';
52
52
  import PTabs from './p-tabs/p-tabs.vue';
53
+ import PTabsPills from './p-tabs-pills/p-tabs-pills.vue';
53
54
  import PTextarea from './p-textarea/p-textarea.vue';
54
55
  import PToggle from './p-toggle/p-toggle.vue';
55
- export { colsInjectionKey, FileUploadFile, HeaderCellAttrs, isColsResizableInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, MIN_WIDTH_COL_RESIZE, P_ICON_ALIASES, PActionBar, PActionBarAction, PAlert, PAvatar, PBtn, PCard, PCheckbox, PChips, PCloseBtn, PDatePicker, PDrawer, PDropdown, PDropdownSelect, PFileUpload, PFilterIcon, PIcon, PIconAlias, PInfoIcon, PInlineDatePicker, PInput, PInputNumber, PInputPercent, PInputSearch, PLink, PLoading, PModal, PPagination, PPaginationInfo, PProgressBar, PRingLoader, PSelect, PSelectBtn, PSelectList, PSelectPill, PSkeletonLoader, PSteps, PTable, PTableHeaderCell, PTableLoader, PTableSort, PTableTd, PTabs, PTextarea, PToggle, Size, SORTING_TYPES, SortingType, SortingTypeWithoutNoSorting, StepItem, TableCol, ThAttrs, usePLoading, usePModal, usePTableColResize, usePTableRowVirtualizer, useSelectList, };
56
+ export { colsInjectionKey, FileUploadFile, HeaderCellAttrs, isColsResizableInjectionKey, isFirstColFixedInjectionKey, isLastColFixedInjectionKey, MIN_WIDTH_COL_RESIZE, P_ICON_ALIASES, PActionBar, PActionBarAction, PAlert, PAvatar, PBtn, PCard, PCheckbox, PChips, PCloseBtn, PDatePicker, PDrawer, PDropdown, PDropdownSelect, PFileUpload, PFilterIcon, PIcon, PIconAlias, PInfoIcon, PInlineDatePicker, PInput, PInputNumber, PInputPercent, PInputSearch, PLink, PLoading, PModal, PPagination, PPaginationInfo, PProgressBar, PRingLoader, PSelect, PSelectBtn, PSelectList, PSelectPill, PSkeletonLoader, PSteps, PTable, PTableHeaderCell, PTableLoader, PTableSort, PTableTd, PTabs, PTabsPills, PTextarea, PToggle, Size, SORTING_TYPES, SortingType, SortingTypeWithoutNoSorting, StepItem, TableCol, ThAttrs, usePLoading, usePModal, usePTableColResize, usePTableRowVirtualizer, useSelectList, };
@@ -0,0 +1,21 @@
1
+ type Value = string | number | boolean;
2
+ type Option = {
3
+ [key: string]: Value;
4
+ };
5
+ type Props = {
6
+ modelValue?: Value;
7
+ items?: readonly Option[];
8
+ itemText?: string;
9
+ itemValue?: string;
10
+ };
11
+ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
12
+ "update:modelValue": (val: Value) => any;
13
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
14
+ "onUpdate:modelValue"?: ((val: Value) => any) | undefined;
15
+ }>, {
16
+ modelValue: Value;
17
+ items: readonly Option[];
18
+ itemText: string;
19
+ itemValue: string;
20
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pequity/squirrel",
3
3
  "description": "Squirrel component library",
4
- "version": "8.3.5",
4
+ "version": "8.4.1",
5
5
  "packageManager": "pnpm@10.6.4",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -49,56 +49,50 @@
49
49
  "devDependencies": {
50
50
  "@commitlint/cli": "^19.8.1",
51
51
  "@commitlint/config-conventional": "^19.8.1",
52
- "@pequity/eslint-config": "^2.0.1",
53
- "@playwright/test": "^1.52.0",
52
+ "@pequity/eslint-config": "^2.0.2",
53
+ "@playwright/test": "^1.53.1",
54
54
  "@semantic-release/changelog": "^6.0.3",
55
55
  "@semantic-release/git": "^10.0.1",
56
- "@storybook/addon-a11y": "^8.6.14",
57
- "@storybook/addon-actions": "^8.6.14",
58
- "@storybook/addon-essentials": "^8.6.14",
59
- "@storybook/addon-interactions": "^8.6.14",
60
- "@storybook/addon-links": "^8.6.14",
61
- "@storybook/blocks": "^8.6.14",
62
- "@storybook/manager-api": "^8.6.14",
63
- "@storybook/test": "^8.6.14",
64
- "@storybook/test-runner": "^0.22.0",
65
- "@storybook/theming": "^8.6.14",
66
- "@storybook/vue3": "^8.6.14",
67
- "@storybook/vue3-vite": "^8.6.14",
68
- "@tanstack/vue-virtual": "3.13.9",
56
+ "@storybook/addon-a11y": "^9.0.14",
57
+ "@storybook/addon-docs": "^9.0.14",
58
+ "@storybook/addon-links": "^9.0.14",
59
+ "@storybook/test-runner": "^0.23.0",
60
+ "@storybook/vue3": "^9.0.14",
61
+ "@storybook/vue3-vite": "^9.0.14",
62
+ "@tanstack/vue-virtual": "3.13.12",
69
63
  "@types/jsdom": "^21.1.7",
70
64
  "@types/lodash-es": "^4.17.12",
71
- "@types/node": "^22.15.21",
65
+ "@types/node": "^24.0.7",
72
66
  "@vitejs/plugin-vue": "^5.2.4",
73
- "@vitest/coverage-v8": "^3.1.4",
74
- "@vue/compiler-sfc": "3.5.15",
67
+ "@vitest/coverage-v8": "^3.2.4",
68
+ "@vue/compiler-sfc": "3.5.17",
75
69
  "@vue/test-utils": "^2.4.6",
76
70
  "@vuepic/vue-datepicker": "11.0.2",
77
71
  "autoprefixer": "^10.4.21",
78
- "eslint": "^9.27.0",
79
- "eslint-plugin-storybook": "^0.12.0",
72
+ "eslint": "^9.30.0",
73
+ "eslint-plugin-storybook": "^9.0.14",
80
74
  "floating-vue": "5.2.2",
81
- "glob": "^11.0.2",
75
+ "glob": "^11.0.3",
82
76
  "husky": "^9.1.7",
83
77
  "iconify-icon": "^3.0.0",
84
78
  "jsdom": "^26.1.0",
85
- "lint-staged": "^16.0.0",
79
+ "lint-staged": "^16.1.2",
86
80
  "lodash-es": "4.17.21",
87
81
  "make-coverage-badge": "^1.2.0",
88
- "postcss": "^8.5.3",
89
- "prettier": "^3.5.3",
90
- "prettier-plugin-tailwindcss": "^0.6.11",
82
+ "postcss": "^8.5.6",
83
+ "prettier": "^3.6.2",
84
+ "prettier-plugin-tailwindcss": "^0.6.13",
91
85
  "resolve-tspaths": "^0.8.23",
92
86
  "rimraf": "^6.0.1",
93
- "sass": "^1.89.0",
94
- "semantic-release": "^24.2.5",
95
- "storybook": "^8.6.14",
96
- "svgo": "^3.3.2",
87
+ "sass": "^1.89.2",
88
+ "semantic-release": "^24.2.6",
89
+ "storybook": "^9.0.14",
90
+ "svgo": "^4.0.0",
97
91
  "tailwindcss": "^3.4.17",
98
92
  "typescript": "5.8.3",
99
93
  "vite": "^6.3.5",
100
- "vitest": "^3.1.4",
101
- "vue": "3.5.15",
94
+ "vitest": "^3.2.4",
95
+ "vue": "3.5.17",
102
96
  "vue-currency-input": "3.2.1",
103
97
  "vue-router": "4.5.1",
104
98
  "vue-toastification": "2.0.0-rc.5",
@@ -63,6 +63,7 @@ import {
63
63
  import PTableSort from '@squirrel/components/p-table-sort/p-table-sort.vue';
64
64
  import PTableTd from '@squirrel/components/p-table-td/p-table-td.vue';
65
65
  import PTabs from '@squirrel/components/p-tabs/p-tabs.vue';
66
+ import PTabsPills from '@squirrel/components/p-tabs-pills/p-tabs-pills.vue';
66
67
  import PTextarea from '@squirrel/components/p-textarea/p-textarea.vue';
67
68
  import PToggle from '@squirrel/components/p-toggle/p-toggle.vue';
68
69
 
@@ -117,6 +118,7 @@ export {
117
118
  PTableSort,
118
119
  PTableTd,
119
120
  PTabs,
121
+ PTabsPills,
120
122
  PTextarea,
121
123
  PToggle,
122
124
  Size,
@@ -2,8 +2,8 @@ import PaginateLeftIcon from '@squirrel/assets/pagination-left-icon.svg?inline';
2
2
  import PaginateRightIcon from '@squirrel/assets/pagination-right-icon.svg?inline';
3
3
  import PActionBar from '@squirrel/components/p-action-bar/p-action-bar.vue';
4
4
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
5
- import { action } from '@storybook/addon-actions';
6
- import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
5
+ import { action } from 'storybook/actions';
6
+ import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
7
7
 
8
8
  const actionBarActions = [
9
9
  {
@@ -1,5 +1,5 @@
1
1
  import PAlert from '@squirrel/components/p-alert/p-alert.vue';
2
- import { expect, within } from '@storybook/test';
2
+ import { expect, within } from 'storybook/test';
3
3
 
4
4
  export default {
5
5
  title: 'Components/PAlert',
@@ -1,6 +1,6 @@
1
1
  import PBtn from '@squirrel/components/p-btn/p-btn.vue';
2
- import { action } from '@storybook/addon-actions';
3
- import { expect, within } from '@storybook/test';
2
+ import { action } from 'storybook/actions';
3
+ import { expect, within } from 'storybook/test';
4
4
 
5
5
  // You can also import an `md` file and use it as a story's description
6
6
  // import PBtnReadme from './PBtn.readme.md';
@@ -1,5 +1,5 @@
1
1
  import PCheckbox from '@squirrel/components/p-checkbox/p-checkbox.vue';
2
- import { action } from '@storybook/addon-actions';
2
+ import { action } from 'storybook/actions';
3
3
 
4
4
  export default {
5
5
  title: 'Components/PCheckbox',
@@ -1,6 +1,6 @@
1
1
  import PCloseBtn from '@squirrel/components/p-close-btn/p-close-btn.vue';
2
- import { action } from '@storybook/addon-actions';
3
- import { expect, within } from '@storybook/test';
2
+ import { action } from 'storybook/actions';
3
+ import { expect, within } from 'storybook/test';
4
4
 
5
5
  export default {
6
6
  title: 'Components/PCloseBtn',
@@ -198,58 +198,45 @@ describe('PDropdownSelect.vue', () => {
198
198
  cleanup(wrapper);
199
199
  });
200
200
 
201
- it('shows "All options selected" when all selectable options and disabled-selected options are selected', async () => {
201
+ it('shows "All options selected" when all options are selected', async () => {
202
202
  useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
203
203
 
204
204
  const items = cloneDeep(filterListItems).slice(0, 10);
205
205
  items[0].disabled = true;
206
206
  items[1].disabled = true;
207
- // Pre-select one disabled item
208
- const wrapper = createWrapper({ selected: [1], items }, { multiple: true });
207
+ const wrapper = createWrapper({ selected: filterListItems.map((item) => item.value), items }, { multiple: true });
209
208
 
210
209
  await wrapper.find('button').trigger('click');
211
- await wrapper.findByText('Select all').trigger('click');
212
-
213
- const selectedItemsOptions = wrapper.findAll('[p-select-list-option-item]');
214
- const selectedItems = wrapper.findAll('[p-select-list-option-item].selected');
215
210
 
216
- // Should have all non-disabled items (8) plus the pre-selected disabled item (1)
217
- expect(selectedItems.length).toBe(selectedItemsOptions.length - 1);
218
- expect(wrapper.vm.$data.selected.length).toBe(items.length - 1);
219
211
  expect(wrapper.find('button').text()).toBe('All options selected');
220
212
 
221
213
  cleanup(wrapper);
222
214
  });
223
215
 
224
- it('shows number of selected options when not all non-disabled options are selected', async () => {
216
+ it('does not select disabled options when "Select All" is clicked', async () => {
225
217
  useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
226
218
 
227
219
  const items = cloneDeep(filterListItems).slice(0, 10);
228
220
  items[0].disabled = true;
229
- const wrapper = createWrapper({ selected: [2, 3], items }, { multiple: true });
221
+ items[1].disabled = true;
222
+ const wrapper = createWrapper({ selected: [], items }, { multiple: true });
230
223
 
231
- expect(wrapper.find('button').text()).toBe('2 options selected');
224
+ await wrapper.find('button').trigger('click');
225
+ await wrapper.findByText('Select all').trigger('click');
226
+
227
+ expect(wrapper.find('button').text()).toBe('8 options selected');
232
228
 
233
229
  cleanup(wrapper);
234
230
  });
235
231
 
236
- it('does not select disabled items when "Select All" is clicked', async () => {
232
+ it('shows number of selected options when not all non-disabled options are selected', async () => {
237
233
  useVirtualizer.mockImplementation(() => createMockedVirtualizer(10));
238
234
 
239
235
  const items = cloneDeep(filterListItems).slice(0, 10);
240
236
  items[0].disabled = true;
241
- items[1].disabled = true;
242
- const wrapper = createWrapper({ selected: [], items }, { multiple: true });
243
-
244
- await wrapper.find('button').trigger('click');
245
-
246
- await wrapper.findByText('Select all').trigger('click');
247
-
248
- const selectedItemsOptions = wrapper.findAll('[p-select-list-option-item]');
249
- const selectedItems = wrapper.findAll('[p-select-list-option-item].selected');
237
+ const wrapper = createWrapper({ selected: [2, 3], items }, { multiple: true });
250
238
 
251
- expect(selectedItems.length).toBe(selectedItemsOptions.length - 2);
252
- expect(wrapper.vm.$data.selected.length).toBe(items.length - 2);
239
+ expect(wrapper.find('button').text()).toBe('2 options selected');
253
240
 
254
241
  cleanup(wrapper);
255
242
  });
@@ -59,7 +59,7 @@
59
59
  <div v-else class="truncate text-left">
60
60
  {{
61
61
  multiple
62
- ? selectedItems.length === selectableItemsCount
62
+ ? selectedItems.length === computedItems.length
63
63
  ? 'All options selected'
64
64
  : `${selectedItems.length} option${selectedItems.length > 1 ? 's' : ''} selected`
65
65
  : selectedItems[0][itemText]
@@ -467,10 +467,6 @@ const attrs = computed(() => {
467
467
 
468
468
  const style = computed(() => $attrs.style as StyleValue);
469
469
 
470
- const selectableItemsCount = computed(
471
- () => internalItems.value.filter((item) => !isDisabled(item) || isSelected(item[props.itemValue])).length
472
- );
473
-
474
470
  // Watch
475
471
  // Sorts internalItems putting the selected ones first
476
472
  watch(
@@ -1,6 +1,6 @@
1
1
  import PFileUpload from '@squirrel/components/p-file-upload/p-file-upload.vue';
2
- import { expect } from '@storybook/test';
3
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
+ import { expect } from 'storybook/test';
4
4
  import { vi } from 'vitest';
5
5
 
6
6
  // Mocking the toastification library
@@ -1,6 +1,6 @@
1
1
  import PInputNumber from '@squirrel/components/p-input-number/p-input-number.vue';
2
- import { expect } from '@storybook/test';
3
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
+ import { expect } from 'storybook/test';
4
4
 
5
5
  const baseClasses = () => [
6
6
  'text-night',
@@ -1,6 +1,6 @@
1
1
  import PRingLoader from '@squirrel/components/p-ring-loader/p-ring-loader.vue';
2
- import { expect } from '@storybook/test';
3
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
+ import { expect } from 'storybook/test';
4
4
 
5
5
  describe('PRingLoader.vue', () => {
6
6
  it('renders correctly with custom props', async () => {
@@ -1,6 +1,6 @@
1
1
  import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import PSelectBtn from '@squirrel/components/p-select-btn/p-select-btn.vue';
3
- import { expect, userEvent, within } from '@storybook/test';
3
+ import { expect, userEvent, within } from 'storybook/test';
4
4
  import { ref } from 'vue';
5
5
 
6
6
  const selectItems = [
@@ -1,7 +1,7 @@
1
1
  import { fieldArgTypes } from '@root/stories/common/field';
2
2
  import { getCSSTransitionDuration, sleep } from '@root/stories/common/helpers';
3
3
  import PSelectPill from '@squirrel/components/p-select-pill/p-select-pill.vue';
4
- import { expect, userEvent, within } from '@storybook/test';
4
+ import { expect, userEvent, within } from 'storybook/test';
5
5
  import { ref } from 'vue';
6
6
 
7
7
  const ACTIVE_CLASS = 'text-p-purple-60';
@@ -1,5 +1,5 @@
1
1
  import PTableHeaderCell from '@squirrel/components/p-table-header-cell/p-table-header-cell.vue';
2
- import { action } from '@storybook/addon-actions';
2
+ import { action } from 'storybook/actions';
3
3
 
4
4
  export default {
5
5
  title: 'Components/PTableHeaderCell',
@@ -1,6 +1,6 @@
1
1
  import PaginateRightIcon from '@squirrel/assets/pagination-right-icon.svg?inline';
2
2
  import PTabs from '@squirrel/components/p-tabs/p-tabs.vue';
3
- import { action } from '@storybook/addon-actions';
3
+ import { action } from 'storybook/actions';
4
4
 
5
5
  export default {
6
6
  title: 'Components/PTabs',
@@ -0,0 +1,117 @@
1
+ import PTabsPills from '@squirrel/components/p-tabs-pills/p-tabs-pills.vue';
2
+ import { createWrapperFor } from '@tests/vitest.helpers';
3
+
4
+ const ACTIVE_CLASSES = ['bg-night', 'text-surface', 'hover:bg-night', 'hover:text-surface'];
5
+
6
+ const createTabItems = () => [
7
+ { value: 'tab1', text: 'Home' },
8
+ { value: 'tab2', text: 'Profile' },
9
+ { value: 'tab3', text: 'Messages' },
10
+ { value: 'tab4', text: 'About' },
11
+ { value: 'tab5', text: 'Settings' },
12
+ ];
13
+
14
+ const createWrapper = (props = {}) => {
15
+ return createWrapperFor(PTabsPills, {
16
+ props: {
17
+ items: createTabItems(),
18
+ ...props,
19
+ },
20
+ });
21
+ };
22
+
23
+ describe('PTabsPills.vue', () => {
24
+ it('renders correctly', async () => {
25
+ const wrapper = createWrapper();
26
+ const buttons = await wrapper.findAll('button');
27
+
28
+ buttons.forEach((button) => {
29
+ ACTIVE_CLASSES.forEach((className) => {
30
+ expect(button.classes()).not.toContain(className);
31
+ });
32
+ });
33
+ expect(buttons.length).toBe(5);
34
+ expect(buttons[1].text()).toBe(createTabItems()[1].text);
35
+ });
36
+
37
+ it(`updates the value bound with v-model`, async () => {
38
+ const wrapper = createWrapperFor({
39
+ data() {
40
+ return {
41
+ selected: 'tab1',
42
+ items: createTabItems(),
43
+ };
44
+ },
45
+ template: '<PTabsPills :items="items" v-model="selected" />',
46
+ components: { PTabsPills },
47
+ });
48
+
49
+ await wrapper.setData({ selected: 'tab4' });
50
+
51
+ const selected = wrapper.findAll('button')[3];
52
+
53
+ ACTIVE_CLASSES.forEach((className) => {
54
+ expect(selected.classes()).toContain(className);
55
+ });
56
+ expect(selected.attributes('aria-selected')).toBe('true');
57
+ });
58
+
59
+ it(`updates the value bound with v-model when an option is clicked`, async () => {
60
+ const wrapper = createWrapperFor({
61
+ data() {
62
+ return {
63
+ selected: 'tab1',
64
+ items: createTabItems(),
65
+ };
66
+ },
67
+ template: '<PTabsPills :items="items" v-model="selected" />',
68
+ components: { PTabsPills },
69
+ });
70
+
71
+ await wrapper.findAll('button')[3].trigger('click');
72
+
73
+ const selected = wrapper.findAll('button')[3];
74
+
75
+ ACTIVE_CLASSES.forEach((className) => {
76
+ expect(selected.classes()).toContain(className);
77
+ });
78
+ expect(selected.attributes('aria-selected')).toBe('true');
79
+ });
80
+
81
+ it('works with custom itemValue and itemText', async () => {
82
+ const tabItems = createTabItems();
83
+
84
+ const wrapper = createWrapper({
85
+ items: tabItems.map((item) => ({ customValue: item.value, customText: item.text })),
86
+ itemValue: 'customValue',
87
+ itemText: 'customText',
88
+ });
89
+
90
+ const buttons = await wrapper.findAll('button');
91
+
92
+ expect(buttons.length).toBe(5);
93
+
94
+ buttons.forEach((button, i) => {
95
+ expect(button.text()).toBe(tabItems[i].text);
96
+ });
97
+ });
98
+
99
+ it(`disables an option based on the disabled attribute`, async () => {
100
+ const wrapper = createWrapper({
101
+ items: createTabItems().map((item, i) => ({ ...item, disabled: i === 2 })),
102
+ modelValue: 'tab1',
103
+ });
104
+
105
+ const buttons = await wrapper.findAll('button');
106
+
107
+ buttons.forEach((button, i) => {
108
+ if (i === 2) {
109
+ expect(button.element.disabled).toBe(true);
110
+ expect(button.classes()).toContain('text-p-gray-30');
111
+ } else {
112
+ expect(button.element.disabled).toBe(false);
113
+ expect(button.classes()).not.toContain('text-p-gray-30');
114
+ }
115
+ });
116
+ });
117
+ });
@@ -0,0 +1,139 @@
1
+ import PTabsPills from '@squirrel/components/p-tabs-pills/p-tabs-pills.vue';
2
+ import { expect, userEvent, within } from 'storybook/test';
3
+ import { ref } from 'vue';
4
+
5
+ const ACTIVE_CLASSES = ['bg-night', 'text-surface', 'hover:bg-night', 'hover:text-surface'];
6
+
7
+ const DISABLED_CLASSES = ['text-p-gray-30'];
8
+
9
+ export default {
10
+ title: 'Components/PTabsPills',
11
+ component: PTabsPills,
12
+ tags: ['autodocs'],
13
+ render: (args) => ({
14
+ components: { PTabsPills },
15
+ setup() {
16
+ const selected = ref('tab1');
17
+
18
+ return { args, selected };
19
+ },
20
+ template: `
21
+ <PTabsPills v-model="selected" v-bind="args" />
22
+ <div class="mt-2">Selected: {{ selected }}</div>
23
+ `,
24
+ }),
25
+ parameters: {
26
+ docs: {
27
+ description: {
28
+ component: `PTabsPills can be used as a tab or button group that allows the user to select an option from a list of options.`,
29
+ },
30
+ },
31
+ },
32
+ };
33
+
34
+ export const Default = {
35
+ args: {
36
+ items: [
37
+ { value: 'tab1', text: 'Home' },
38
+ { value: 'tab2', text: 'Profile' },
39
+ { value: 'tab3', text: 'Messages' },
40
+ ],
41
+ },
42
+ play: async ({ canvasElement }) => {
43
+ const canvas = within(canvasElement);
44
+
45
+ await userEvent.click(canvas.getByText(/Profile/i));
46
+
47
+ const button = canvas.getByText(/Profile/i).closest('button');
48
+ ACTIVE_CLASSES.forEach((className) => {
49
+ expect(button.classList).toContain(className);
50
+ });
51
+ },
52
+ };
53
+
54
+ export const CustomValueAndText = {
55
+ args: {
56
+ items: [
57
+ { myValue: 'tab1', myText: 'Home' },
58
+ { myValue: 'tab2', myText: 'Profile' },
59
+ { myValue: 'tab3', myText: 'Messages' },
60
+ ],
61
+ itemValue: 'myValue',
62
+ itemText: 'myText',
63
+ },
64
+ parameters: {
65
+ docs: {
66
+ description: {
67
+ story: 'You can customize the property names used for value and text using `itemValue` and `itemText` props.',
68
+ },
69
+ },
70
+ },
71
+ play: async ({ canvasElement }) => {
72
+ const canvas = within(canvasElement);
73
+
74
+ await userEvent.click(canvas.getByText(/Profile/i));
75
+
76
+ const button = canvas.getByText(/Profile/i).closest('button');
77
+ ACTIVE_CLASSES.forEach((className) => {
78
+ expect(button.classList).toContain(className);
79
+ });
80
+ },
81
+ };
82
+
83
+ export const DisabledItems = {
84
+ args: {
85
+ items: [
86
+ { myValue: 'tab1', myText: 'Home', disabled: false },
87
+ { myValue: 'tab2', myText: 'Profile', disabled: true },
88
+ { myValue: 'tab3', myText: 'About', disabled: true },
89
+ { myValue: 'tab4', myText: 'Messages', disabled: false },
90
+ ],
91
+ itemValue: 'myValue',
92
+ itemText: 'myText',
93
+ },
94
+ parameters: {
95
+ docs: {
96
+ description: {
97
+ story: 'You can disable specific tabs by adding a `disabled` property to the items.',
98
+ },
99
+ },
100
+ },
101
+ play: async ({ canvasElement }) => {
102
+ const canvas = within(canvasElement);
103
+
104
+ // Check that disabled buttons have disabled classes
105
+ const disabledButtons = [
106
+ canvas.getByText(/Profile/i).closest('button'),
107
+ canvas.getByText(/About/i).closest('button'),
108
+ ];
109
+ disabledButtons.forEach((button) => {
110
+ DISABLED_CLASSES.forEach((className) => {
111
+ expect(button.classList).toContain(className);
112
+ });
113
+ ACTIVE_CLASSES.forEach((className) => {
114
+ expect(button.classList).not.toContain(className);
115
+ });
116
+ });
117
+
118
+ // Try to click a disabled button
119
+ await userEvent.click(canvas.getByText(/Profile/i));
120
+
121
+ // Check that disabled button still has disabled classes and not active classes
122
+ const disabledButton = canvas.getByText(/Profile/i).closest('button');
123
+ DISABLED_CLASSES.forEach((className) => {
124
+ expect(disabledButton.classList).toContain(className);
125
+ });
126
+ ACTIVE_CLASSES.forEach((className) => {
127
+ expect(disabledButton.classList).not.toContain(className);
128
+ });
129
+
130
+ // Check that Home button still has active classes
131
+ const homeButton = canvas.getByText(/Home/i).closest('button');
132
+ ACTIVE_CLASSES.forEach((className) => {
133
+ expect(homeButton.classList).toContain(className);
134
+ });
135
+ DISABLED_CLASSES.forEach((className) => {
136
+ expect(homeButton.classList).not.toContain(className);
137
+ });
138
+ },
139
+ };
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <nav
3
+ class="flex h-6 w-fit flex-row space-x-1 overflow-x-auto rounded bg-p-gray-10 p-1 text-sm font-medium text-p-gray-50"
4
+ aria-label="Tabs Pills"
5
+ role="tablist"
6
+ aria-orientation="horizontal"
7
+ >
8
+ <button
9
+ v-for="tab in items"
10
+ :key="String(tab[itemValue])"
11
+ type="button"
12
+ class="rounded px-2 pb-px leading-none transition-all duration-300"
13
+ :class="[
14
+ tab.disabled
15
+ ? 'text-p-gray-30'
16
+ : tab[itemValue] === modelValue
17
+ ? 'bg-night text-surface hover:bg-night hover:text-surface'
18
+ : 'hover:text-p-gray-70',
19
+ ]"
20
+ :disabled="!!tab.disabled"
21
+ role="tab"
22
+ :aria-selected="tab[itemValue] === modelValue"
23
+ :data-tab="`#${tab[itemValue]}`"
24
+ :aria-controls="String(tab[itemValue])"
25
+ @click="$emit('update:modelValue', tab[itemValue])"
26
+ >
27
+ {{ tab[itemText] }}
28
+ </button>
29
+ </nav>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ defineOptions({
34
+ name: 'PTabsPills',
35
+ });
36
+
37
+ type Value = string | number | boolean;
38
+
39
+ type Option = {
40
+ [key: string]: Value;
41
+ };
42
+
43
+ type Props = {
44
+ modelValue?: Value;
45
+ items?: readonly Option[];
46
+ itemText?: string;
47
+ itemValue?: string;
48
+ };
49
+
50
+ withDefaults(defineProps<Props>(), {
51
+ modelValue: '',
52
+ items: () => [],
53
+ itemValue: 'value',
54
+ itemText: 'text',
55
+ });
56
+
57
+ defineEmits<{
58
+ 'update:modelValue': [val: Value];
59
+ }>();
60
+ </script>
@@ -1,6 +1,6 @@
1
1
  import PToggle from '@squirrel/components/p-toggle/p-toggle.vue';
2
- import { expect } from '@storybook/test';
3
2
  import { createWrapperFor } from '@tests/vitest.helpers';
3
+ import { expect } from 'storybook/test';
4
4
 
5
5
  describe('PToggle.vue', () => {
6
6
  it('renders correctly', async () => {
@@ -1,5 +1,5 @@
1
1
  import { useInputClasses } from '@squirrel/composables/useInputClasses';
2
- import { expect } from '@storybook/test';
2
+ import { expect } from 'storybook/test';
3
3
  import { reactive, ref } from 'vue';
4
4
 
5
5
  const baseInputClassesMd = () => [