@code-coaching/vuetiful 0.22.0 → 0.23.0

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 (105) hide show
  1. package/README.md +1 -1
  2. package/dist/style.css +2 -2
  3. package/dist/types/components/VBootstrap.vue.d.ts +15 -0
  4. package/dist/types/components/atoms/VAvatar.vue.d.ts +37 -10
  5. package/dist/types/components/atoms/VBadge.vue.d.ts +22 -1
  6. package/dist/types/components/atoms/VButton.vue.d.ts +27 -0
  7. package/dist/types/components/atoms/VChip.vue.d.ts +22 -1
  8. package/dist/types/components/atoms/VRadio/VRadioGroup.vue.d.ts +10 -1
  9. package/dist/types/components/atoms/VRadio/VRadioItem.vue.d.ts +2 -2
  10. package/dist/types/components/atoms/VSwitch/VSwitch.vue.d.ts +24 -15
  11. package/dist/types/components/atoms/index.d.ts +1 -2
  12. package/dist/types/components/molecules/VAccordion/VAccordion.vue.d.ts +15 -6
  13. package/dist/types/components/molecules/VAccordion/VAccordionItem.vue.d.ts +11 -1
  14. package/dist/types/components/molecules/VAlert.vue.d.ts +39 -3
  15. package/dist/types/components/molecules/VCard/VCard.vue.d.ts +5 -5
  16. package/dist/types/components/molecules/VCard/VCardBody.vue.d.ts +13 -1
  17. package/dist/types/components/molecules/VCard/VCardFooter.vue.d.ts +22 -1
  18. package/dist/types/components/molecules/VCard/VCardHeader.vue.d.ts +22 -1
  19. package/dist/types/components/{atoms → molecules}/VCodeBlock.vue.d.ts +36 -9
  20. package/dist/types/components/molecules/VListbox/VListbox.vue.d.ts +51 -14
  21. package/dist/types/components/molecules/VListbox/VListboxItem.vue.d.ts +13 -3
  22. package/dist/types/components/molecules/VListbox/VListboxItems.vue.d.ts +9 -0
  23. package/dist/types/components/molecules/VTabs/VTab.vue.d.ts +9 -0
  24. package/dist/types/components/molecules/VTabs/VTabs.vue.d.ts +18 -0
  25. package/dist/types/components/molecules/index.d.ts +4 -3
  26. package/dist/types/props/index.d.ts +1 -0
  27. package/dist/types/props/props.d.ts +14 -0
  28. package/dist/types/services/index.d.ts +5 -3
  29. package/dist/types/services/settings.service.d.ts +132 -0
  30. package/dist/types/services/settings.service.test.d.ts +1 -0
  31. package/dist/types/types/index.d.ts +53 -0
  32. package/dist/vuetiful.es.mjs +920 -557
  33. package/dist/vuetiful.umd.js +23 -10
  34. package/package.json +1 -1
  35. package/src/components/VBootstrap.vue +62 -0
  36. package/src/components/atoms/VAvatar.test.ts +98 -28
  37. package/src/components/atoms/VAvatar.vue +46 -13
  38. package/src/components/atoms/VBadge.test.ts +10 -0
  39. package/src/components/atoms/VBadge.vue +13 -1
  40. package/src/components/atoms/VButton.test.ts +58 -0
  41. package/src/components/atoms/VButton.vue +31 -2
  42. package/src/components/atoms/VChip.test.ts +26 -11
  43. package/src/components/atoms/VChip.vue +13 -1
  44. package/src/components/atoms/VRadio/VRadioDescription.vue +1 -1
  45. package/src/components/atoms/VRadio/VRadioGroup.test.ts +7 -7
  46. package/src/components/atoms/VRadio/VRadioGroup.vue +16 -5
  47. package/src/components/atoms/VRadio/VRadioItem.vue +12 -8
  48. package/src/components/atoms/VRadio/VRadioLabel.vue +1 -1
  49. package/src/components/atoms/VSwitch/VSwitch.test.ts +20 -18
  50. package/src/components/atoms/VSwitch/VSwitch.vue +29 -17
  51. package/src/components/atoms/VSwitch/VSwitchDescription.vue +1 -1
  52. package/src/components/atoms/VSwitch/VSwitchGroup.vue +2 -2
  53. package/src/components/atoms/VSwitch/VSwitchLabel.vue +1 -1
  54. package/src/components/atoms/index.ts +0 -2
  55. package/src/components/molecules/VAccordion/VAccordion.test.ts +11 -0
  56. package/src/components/molecules/VAccordion/VAccordion.vue +16 -7
  57. package/src/components/molecules/VAccordion/VAccordionItem.test.ts +65 -16
  58. package/src/components/molecules/VAccordion/VAccordionItem.vue +53 -32
  59. package/src/components/molecules/VAlert.test.ts +11 -1
  60. package/src/components/molecules/VAlert.vue +33 -7
  61. package/src/components/molecules/VCard/VCard.test.ts +1 -1
  62. package/src/components/molecules/VCard/VCard.vue +12 -7
  63. package/src/components/molecules/VCard/VCardBody.test.ts +18 -0
  64. package/src/components/molecules/VCard/VCardBody.vue +16 -1
  65. package/src/components/molecules/VCard/VCardFooter.test.ts +18 -0
  66. package/src/components/molecules/VCard/VCardFooter.vue +21 -3
  67. package/src/components/molecules/VCard/VCardHeader.test.ts +18 -0
  68. package/src/components/molecules/VCard/VCardHeader.vue +26 -5
  69. package/src/components/molecules/VCodeBlock.test.ts +133 -0
  70. package/src/components/molecules/VCodeBlock.vue +120 -0
  71. package/src/components/molecules/VListbox/VListbox.test.ts +42 -15
  72. package/src/components/molecules/VListbox/VListbox.vue +44 -15
  73. package/src/components/molecules/VListbox/VListboxButton.test.ts +15 -6
  74. package/src/components/molecules/VListbox/VListboxButton.vue +10 -1
  75. package/src/components/molecules/VListbox/VListboxItem.test.ts +2 -2
  76. package/src/components/molecules/VListbox/VListboxItem.vue +18 -7
  77. package/src/components/molecules/VListbox/VListboxItems.test.ts +2 -2
  78. package/src/components/molecules/VListbox/VListboxItems.vue +18 -5
  79. package/src/components/molecules/VListbox/VListboxLabel.test.ts +1 -2
  80. package/src/components/molecules/VListbox/VListboxLabel.vue +1 -1
  81. package/src/components/molecules/VPreview.vue +9 -5
  82. package/src/components/molecules/{VRail.test.ts → VRail/VRail.test.ts} +1 -1
  83. package/src/components/molecules/{VRail.vue → VRail/VRail.vue} +6 -6
  84. package/src/components/molecules/VRail/VRailTile.test.ts +99 -0
  85. package/src/components/molecules/{VRailTile.vue → VRail/VRailTile.vue} +4 -6
  86. package/src/components/molecules/VTabs/VTab.test.ts +7 -3
  87. package/src/components/molecules/VTabs/VTab.vue +20 -5
  88. package/src/components/molecules/VTabs/VTabPanel.vue +2 -2
  89. package/src/components/molecules/VTabs/VTabs.test.ts +4 -2
  90. package/src/components/molecules/VTabs/VTabs.vue +32 -8
  91. package/src/components/molecules/index.ts +5 -2
  92. package/src/props/index.ts +1 -0
  93. package/src/props/props.ts +62 -0
  94. package/src/services/index.ts +5 -3
  95. package/src/services/settings.service.test.ts +17 -0
  96. package/src/services/settings.service.ts +136 -0
  97. package/src/types/index.ts +58 -0
  98. package/src/components/atoms/VCodeBlock.test.ts +0 -14
  99. package/src/components/atoms/VCodeBlock.vue +0 -92
  100. package/src/components/molecules/VRailTile.test.ts +0 -14
  101. /package/dist/types/components/{atoms → molecules}/VCodeBlock.test.d.ts +0 -0
  102. /package/dist/types/components/molecules/{VRail.test.d.ts → VRail/VRail.test.d.ts} +0 -0
  103. /package/dist/types/components/molecules/{VRail.vue.d.ts → VRail/VRail.vue.d.ts} +0 -0
  104. /package/dist/types/components/molecules/{VRailTile.test.d.ts → VRail/VRailTile.test.d.ts} +0 -0
  105. /package/dist/types/components/molecules/{VRailTile.vue.d.ts → VRail/VRailTile.vue.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-coaching/vuetiful",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "onchange 'src/**/*.vue' 'src/**/*.ts' 'src/**/*.css' -- npm run build",
@@ -0,0 +1,62 @@
1
+ <script setup lang="ts">
2
+ import { Variant } from "@/types";
3
+ import { PropType } from "vue";
4
+
5
+ /**
6
+ * VBoostrap is a component that should not be used directly.
7
+ * It is used to make sure that the variant-classes are bundled with the application.
8
+ * This allows us to use `variant-${variant}` in the template.
9
+ *
10
+ * In the future it might be a good idea to just add .filled, .ringed, etc. as alternatives for .variant-filled, .variant-ringed, etc. and remove this component.
11
+ */
12
+
13
+ defineProps({
14
+ variant: {
15
+ // explicit string union because TypeScript type won't throw error if invalid value is passed
16
+ type: String as PropType<
17
+ | ""
18
+ | "variant-filled"
19
+ | "variant-filled-primary"
20
+ | "variant-filled-secondary"
21
+ | "variant-filled-tertiary"
22
+ | "variant-filled-success"
23
+ | "variant-filled-warning"
24
+ | "variant-filled-error"
25
+ | "variant-filled-surface"
26
+ | "variant-ringed"
27
+ | "variant-ringed-primary"
28
+ | "variant-ringed-secondary"
29
+ | "variant-ringed-tertiary"
30
+ | "variant-ringed-success"
31
+ | "variant-ringed-warning"
32
+ | "variant-ringed-error"
33
+ | "variant-ringed-surface"
34
+ | "variant-ghost"
35
+ | "variant-ghost-primary"
36
+ | "variant-ghost-secondary"
37
+ | "variant-ghost-tertiary"
38
+ | "variant-ghost-success"
39
+ | "variant-ghost-warning"
40
+ | "variant-ghost-error"
41
+ | "variant-ghost-surface"
42
+ | "variant-soft"
43
+ | "variant-soft-primary"
44
+ | "variant-soft-secondary"
45
+ | "variant-soft-tertiary"
46
+ | "variant-soft-success"
47
+ | "variant-soft-warning"
48
+ | "variant-soft-error"
49
+ | "variant-soft-surface"
50
+ | "variant-glass"
51
+ | "variant-glass-primary"
52
+ | "variant-glass-secondary"
53
+ | "variant-glass-tertiary"
54
+ | "variant-glass-success"
55
+ | "variant-glass-warning"
56
+ | "variant-glass-error"
57
+ | "variant-glass-surface"
58
+ >,
59
+ default: Variant.Filled,
60
+ },
61
+ });
62
+ </script>
@@ -2,48 +2,63 @@ import { mount } from "@vue/test-utils";
2
2
  import { describe, expect, test } from "vitest";
3
3
  import { VAvatar } from ".";
4
4
 
5
- test("VAvatar", () => {
6
- expect(VAvatar).toBeTruthy();
7
- });
8
-
9
- describe("VAvatar props", () => {
10
- test("VAvatar defaults", () => {
5
+ describe("VAvatar", () => {
6
+ test("defaults", () => {
11
7
  const wrapper = mount(VAvatar);
12
8
 
13
- expect(wrapper.classes()).toContain("avatar");
9
+ expect(wrapper.classes()).toContain("vuetiful-avatar");
14
10
  expect(wrapper.classes()).toContain("w-16");
15
11
  expect(wrapper.classes()).toContain("rounded-token");
16
12
 
17
- const avatarText = wrapper.find(".avatar-text");
13
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
18
14
  expect(avatarText.exists()).toBe(true);
19
15
  expect(avatarText.text()).toBe("");
20
- expect(avatarText.classes()).toContain("dark:fill-on-surface-token");
21
- expect(avatarText.classes()).toContain("fill-base-token");
16
+ expect(avatarText.classes()).toEqual(["vuetiful-avatar-text", "fill-white", "dark:fill-black"]);
22
17
  });
23
18
 
24
- test("VAvatar initials", () => {
19
+ test("custom variant", () => {
20
+ const wrapper = mount(VAvatar, {
21
+ props: {
22
+ variant: "ghost",
23
+ },
24
+ });
25
+
26
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
27
+ expect(avatarText.classes()).toEqual(["vuetiful-avatar-text", "fill-black", "dark:fill-white"]);
28
+ });
29
+
30
+ test("variant with hyphen", () => {
31
+ const wrapper = mount(VAvatar, {
32
+ props: {
33
+ variant: "filled-primary",
34
+ },
35
+ });
36
+
37
+ expect(wrapper.classes()).toContain("variant-filled-primary");
38
+ })
39
+
40
+ test("initials", () => {
25
41
  const wrapper = mount(VAvatar, {
26
42
  props: {
27
43
  initials: "JD",
28
- class: "variant-filled",
44
+ variant: "filled",
29
45
  },
30
46
  });
31
47
 
32
- expect(wrapper.classes()).toContain("avatar");
48
+ expect(wrapper.classes()).toContain("vuetiful-avatar");
33
49
  expect(wrapper.classes()).toContain("w-16");
34
50
  expect(wrapper.classes()).toContain("rounded-token");
35
51
 
36
- const avatarText = wrapper.find(".avatar-text");
52
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
37
53
  expect(avatarText.exists()).toBe(true);
38
54
  expect(avatarText.text()).toBe("JD");
39
- expect(avatarText.classes()).toContain("dark:fill-base-token");
40
- expect(avatarText.classes()).toContain("fill-on-surface-token");
55
+ expect(avatarText.classes()).toEqual(["vuetiful-avatar-text", "fill-white", "dark:fill-black"]);
41
56
 
42
- const avatarImage = wrapper.find(".avatar-image");
57
+ const avatarImage = wrapper.find(".vuetiful-avatar-image");
43
58
  expect(avatarImage.exists()).toBe(false);
44
59
  });
45
60
 
46
- test("VAvatar initials fill", () => {
61
+ test("initials fill", () => {
47
62
  const wrapper = mount(VAvatar, {
48
63
  props: {
49
64
  initials: "JD",
@@ -51,36 +66,36 @@ describe("VAvatar props", () => {
51
66
  },
52
67
  });
53
68
 
54
- expect(wrapper.classes()).toContain("avatar");
69
+ expect(wrapper.classes()).toContain("vuetiful-avatar");
55
70
  expect(wrapper.classes()).toContain("w-16");
56
71
  expect(wrapper.classes()).toContain("rounded-token");
57
72
 
58
- const avatarText = wrapper.find(".avatar-text");
73
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
59
74
  expect(avatarText.exists()).toBe(true);
60
75
  expect(avatarText.text()).toBe("JD");
61
76
  expect(avatarText.classes()).toContain("custom-fill-class");
62
77
  });
63
78
 
64
- test("VAvatar image", () => {
79
+ test("image", () => {
65
80
  const wrapper = mount(VAvatar, {
66
81
  props: {
67
82
  src: "https://via.placeholder.com/150",
68
83
  },
69
84
  });
70
85
 
71
- expect(wrapper.classes()).toContain("avatar");
86
+ expect(wrapper.classes()).toContain("vuetiful-avatar");
72
87
  expect(wrapper.classes()).toContain("w-16");
73
88
  expect(wrapper.classes()).toContain("rounded-token");
74
89
 
75
- const avatarText = wrapper.find(".avatar-text");
90
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
76
91
  expect(avatarText.exists()).toBe(false);
77
92
 
78
- const avatarImage = wrapper.find(".avatar-image");
93
+ const avatarImage = wrapper.find(".vuetiful-avatar-image");
79
94
  expect(avatarImage.exists()).toBe(true);
80
95
  expect(avatarImage.attributes("src")).toBe("https://via.placeholder.com/150");
81
96
  });
82
97
 
83
- test("VAvatar image fallback", async () => {
98
+ test("image fallback", async () => {
84
99
  const wrapper = mount(VAvatar, {
85
100
  props: {
86
101
  src: "https://via.placeholder.com/150",
@@ -88,18 +103,73 @@ describe("VAvatar props", () => {
88
103
  },
89
104
  });
90
105
 
91
- expect(wrapper.classes()).toContain("avatar");
106
+ expect(wrapper.classes()).toContain("vuetiful-avatar");
92
107
  expect(wrapper.classes()).toContain("w-16");
93
108
  expect(wrapper.classes()).toContain("rounded-token");
94
109
 
95
- const avatarText = wrapper.find(".avatar-text");
110
+ const avatarText = wrapper.find(".vuetiful-avatar-text");
96
111
  expect(avatarText.exists()).toBe(false);
97
112
 
98
- const avatarImage = wrapper.find(".avatar-image");
113
+ const avatarImage = wrapper.find(".vuetiful-avatar-image");
99
114
  avatarImage.trigger("error");
100
115
  await wrapper.vm.$nextTick();
101
116
 
102
117
  expect(avatarImage.exists()).toBe(true);
103
118
  expect(avatarImage.attributes("src")).toBe("/image/john-duck.png");
104
119
  });
120
+
121
+ test("size xs", () => {
122
+ const wrapper = mount(VAvatar, {
123
+ props: {
124
+ size: "xs",
125
+ },
126
+ });
127
+
128
+ const avatar = wrapper.find("[data-test='avatar']");
129
+ expect(avatar.attributes("class")).toContain("w-8");
130
+ });
131
+
132
+ test("size sm", () => {
133
+ const wrapper = mount(VAvatar, {
134
+ props: {
135
+ size: "sm",
136
+ },
137
+ });
138
+
139
+ const avatar = wrapper.find("[data-test='avatar']");
140
+ expect(avatar.attributes("class")).toContain("w-12");
141
+ });
142
+
143
+ test("size md", () => {
144
+ const wrapper = mount(VAvatar, {
145
+ props: {
146
+ size: "md",
147
+ },
148
+ });
149
+
150
+ const avatar = wrapper.find("[data-test='avatar']");
151
+ expect(avatar.attributes("class")).toContain("w-16");
152
+ });
153
+
154
+ test("size lg", () => {
155
+ const wrapper = mount(VAvatar, {
156
+ props: {
157
+ size: "lg",
158
+ },
159
+ });
160
+
161
+ const avatar = wrapper.find("[data-test='avatar']");
162
+ expect(avatar.attributes("class")).toContain("w-20");
163
+ });
164
+
165
+ test("size xl", () => {
166
+ const wrapper = mount(VAvatar, {
167
+ props: {
168
+ size: "xl",
169
+ },
170
+ });
171
+
172
+ const avatar = wrapper.find("[data-test='avatar']");
173
+ expect(avatar.attributes("class")).toContain("w-24");
174
+ });
105
175
  });
@@ -1,46 +1,79 @@
1
1
  <script setup lang="ts">
2
- import { CssClasses } from "@/index";
3
- import { computed, ref, useAttrs } from "vue";
2
+ import { CssClasses, useSettings } from "@/index";
3
+ import { sizeProp, unstyledProp, variantProp } from "@/props";
4
+ import { Size } from "@/types";
5
+ import { computed, ref } from "vue";
4
6
 
5
7
  const props = defineProps({
6
8
  // Initials
7
9
  initials: { type: String, default: "" },
8
10
  fill: { type: String as () => CssClasses, default: "" },
11
+ classInitials: { type: String, default: "" },
9
12
 
10
13
  // Image
11
14
  src: { type: String, default: "" },
12
15
  alt: { type: String, default: "" },
13
16
  fallback: { type: String, default: "" },
17
+ classImage: { type: String, default: "" },
14
18
 
15
- width: { type: String as () => CssClasses, default: "w-16" },
16
- rounded: { type: String as () => CssClasses, default: "rounded-token" },
19
+ size: sizeProp,
20
+ variant: variantProp,
21
+ unstyled: unstyledProp,
17
22
  });
18
23
 
19
24
  const imgSrc = ref(props.src);
20
25
  const fillInitials = computed(() => {
21
26
  if (props.fill) return props.fill;
22
27
 
23
- const attrs = useAttrs();
24
- const classString = attrs.class as string | undefined;
28
+ const variantString = props.variant as string | undefined;
29
+ const type = variantString?.split("-")[1];
30
+ if (variantString?.includes("filled")) {
31
+ if (!type) return "fill-white dark:fill-black";
32
+ return "fill-white";
33
+ }
34
+ return "fill-black dark:fill-white";
35
+ });
25
36
 
26
- if (classString?.includes("variant-filled")) {
27
- return "fill-on-surface-token dark:fill-base-token";
37
+ const avatarSize = computed(() => {
38
+ switch (props.size) {
39
+ case Size.XS:
40
+ return "w-8";
41
+ case Size.SM:
42
+ return "w-12";
43
+ case Size.MD:
44
+ return "w-16";
45
+ case Size.LG:
46
+ return "w-20";
47
+ case Size.XL:
48
+ return "w-24";
28
49
  }
29
- return "dark:fill-on-surface-token fill-base-token";
30
50
  });
51
+
52
+ const { settings } = useSettings();
53
+ const isUnstyled =
54
+ settings.global.unstyled || settings.components.avatar.unstyled || props.unstyled;
31
55
  </script>
32
56
  <template>
33
57
  <figure
34
- :class="`avatar isolate flex aspect-square items-center justify-center overflow-hidden font-semibold ${rounded} ${width}`"
58
+ data-test="avatar"
59
+ :class="`vuetiful-avatar ${
60
+ isUnstyled
61
+ ? ''
62
+ : 'isolate flex aspect-square items-center justify-center overflow-hidden font-semibold rounded-token'
63
+ } ${avatarSize} variant-${variant}`"
35
64
  >
36
65
  <img
37
- class="avatar-image"
66
+ :class="`vuetiful-avatar-image ${classImage}`"
38
67
  v-if="src"
39
68
  :src="imgSrc"
40
69
  :alt="alt"
41
70
  @error="() => (imgSrc = fallback)"
42
71
  />
43
- <svg v-else class="avatar-initials h-full w-full" viewBox="0 0 512 512">
72
+ <svg
73
+ v-else
74
+ :class="`vuetiful-avatar-initials ${isUnstyled ? '' : 'h-full w-full'} ${classInitials}`"
75
+ viewBox="0 0 512 512"
76
+ >
44
77
  <text
45
78
  x="50%"
46
79
  y="50%"
@@ -48,7 +81,7 @@ const fillInitials = computed(() => {
48
81
  text-anchor="middle"
49
82
  font-weight="bold"
50
83
  :font-size="150"
51
- :class="`avatar-text ${fillInitials}`"
84
+ :class="`vuetiful-avatar-text ${fillInitials}`"
52
85
  >
53
86
  {{ String(initials).substring(0, 2).toUpperCase() }}
54
87
  </text>
@@ -16,3 +16,13 @@ test("VBadge using slot", () => {
16
16
  expect(wrapper.text()).toContain("John Duck");
17
17
  expect(wrapper.classes()).toContain("badge");
18
18
  });
19
+
20
+ test("VBadge unstyled", () => {
21
+ const wrapper = mount(VBadge, {
22
+ props: {
23
+ unstyled: true,
24
+ },
25
+ });
26
+
27
+ expect(wrapper.classes()).not.toContain("badge");
28
+ })
@@ -1,5 +1,17 @@
1
+ <script setup lang="ts">
2
+ import { unstyledProp, variantProp } from "@/props";
3
+ import { useSettings } from "@/services";
4
+
5
+ const props = defineProps({
6
+ variant: variantProp,
7
+ unstyled: unstyledProp,
8
+ });
9
+
10
+ const { settings } = useSettings();
11
+ const isUnstyled = settings.global.unstyled || settings.components.badge.unstyled || props.unstyled;
12
+ </script>
1
13
  <template>
2
- <div :class="`vuetiful-badge badge`">
14
+ <div :class="`vuetiful-badge ${isUnstyled ? '' : 'badge'} variant-${variant}`">
3
15
  <slot />
4
16
  </div>
5
17
  </template>
@@ -17,6 +17,64 @@ test("VButton using slot", () => {
17
17
  });
18
18
 
19
19
  describe("VButton props", () => {
20
+ describe("size", () => {
21
+ test("xs", () => {
22
+ const wrapper = mount(VButton, {
23
+ props: {
24
+ size: "xs",
25
+ },
26
+ });
27
+ expect(wrapper.classes()).toContain("px-2.5");
28
+ expect(wrapper.classes()).toContain("py-1.5");
29
+ expect(wrapper.classes()).toContain("text-xs");
30
+ });
31
+
32
+ test("sm", () => {
33
+ const wrapper = mount(VButton, {
34
+ props: {
35
+ size: "sm",
36
+ },
37
+ });
38
+ expect(wrapper.classes()).toContain("px-3");
39
+ expect(wrapper.classes()).toContain("py-2");
40
+ expect(wrapper.classes()).toContain("text-sm");
41
+ expect(wrapper.classes()).toContain("leading-4");
42
+ });
43
+
44
+ test("md", () => {
45
+ const wrapper = mount(VButton, {
46
+ props: {
47
+ size: "md",
48
+ },
49
+ });
50
+ expect(wrapper.classes()).toContain("px-4");
51
+ expect(wrapper.classes()).toContain("py-2");
52
+ expect(wrapper.classes()).toContain("text-sm");
53
+ });
54
+
55
+ test("lg", () => {
56
+ const wrapper = mount(VButton, {
57
+ props: {
58
+ size: "lg",
59
+ },
60
+ });
61
+ expect(wrapper.classes()).toContain("px-4");
62
+ expect(wrapper.classes()).toContain("py-2");
63
+ expect(wrapper.classes()).toContain("text-base");
64
+ });
65
+
66
+ test("xl", () => {
67
+ const wrapper = mount(VButton, {
68
+ props: {
69
+ size: "xl",
70
+ },
71
+ });
72
+ expect(wrapper.classes()).toContain("px-6");
73
+ expect(wrapper.classes()).toContain("py-3");
74
+ expect(wrapper.classes()).toContain("text-base");
75
+ });
76
+ });
77
+
20
78
  describe("given icon is true", () => {
21
79
  test("should have btn-icon class, not have btn class", () => {
22
80
  const wrapper = mount(VButton, {
@@ -1,5 +1,9 @@
1
1
  <script setup lang="ts">
2
- defineProps({
2
+ import { sizeProp, unstyledProp, variantProp } from "@/props";
3
+ import { useSettings } from "@/services";
4
+ import { computed } from "vue";
5
+
6
+ const props = defineProps({
3
7
  icon: {
4
8
  type: Boolean as () => boolean,
5
9
  default: false,
@@ -8,6 +12,10 @@ defineProps({
8
12
  type: String as () => string,
9
13
  default: "button",
10
14
  },
15
+
16
+ size: sizeProp,
17
+ variant: variantProp,
18
+ unstyled: unstyledProp,
11
19
  });
12
20
  const emit = defineEmits<{ (event: "click"): void }>();
13
21
 
@@ -31,6 +39,25 @@ const keyupHandler = (event: KeyboardEvent) => {
31
39
  activate();
32
40
  }
33
41
  };
42
+
43
+ const btnSize = computed(() => {
44
+ switch (props.size) {
45
+ case "xs":
46
+ return "px-2.5 py-1.5 text-xs";
47
+ case "sm":
48
+ return "px-3 py-2 text-sm leading-4";
49
+ case "md":
50
+ return "px-4 py-2 text-sm";
51
+ case "lg":
52
+ return "px-4 py-2 text-base";
53
+ case "xl":
54
+ return "px-6 py-3 text-base";
55
+ }
56
+ });
57
+
58
+ const { settings } = useSettings();
59
+ const isUnstyled =
60
+ settings.global.unstyled || settings.components.button.unstyled || props.unstyled;
34
61
  </script>
35
62
 
36
63
  <template>
@@ -38,7 +65,9 @@ const keyupHandler = (event: KeyboardEvent) => {
38
65
  tabindex="0"
39
66
  role="button"
40
67
  :is="tag"
41
- :class="`vuetiful-button ${icon ? 'btn-icon' : 'btn'} border-token hover:cursor-pointer`"
68
+ :class="`vuetiful-button ${
69
+ isUnstyled ? '' : `${icon ? 'btn-icon' : 'btn'} border-token hover:cursor-pointer`
70
+ } ${btnSize} variant-${variant}`"
42
71
  @click="clickHandler"
43
72
  @keydown="keydownHandler"
44
73
  @keyup="keyupHandler"
@@ -1,18 +1,33 @@
1
1
  import { mount } from "@vue/test-utils";
2
- import { expect, test } from "vitest";
2
+ import { describe, expect, test } from "vitest";
3
3
  import { VChip } from ".";
4
4
 
5
- test("VChip", () => {
6
- expect(VChip).toBeTruthy();
7
- });
5
+ describe("VChip", () => {
6
+ test("default slot", () => {
7
+ const wrapper = mount({
8
+ template: `<v-chip>John Duck</v-chip>`,
9
+ components: { VChip },
10
+ });
11
+
12
+ expect(wrapper.text()).toContain("John Duck");
13
+ expect(wrapper.classes()).toContain("chip");
14
+ });
8
15
 
9
- test("VChip using slot", () => {
10
- const wrapper = mount(VChip, {
11
- slots: {
12
- default: "John Duck",
13
- },
16
+ test("unstyled", () => {
17
+ const wrapper = mount({
18
+ template: `<v-chip unstyled>John Duck</v-chip>`,
19
+ components: { VChip },
20
+ });
21
+
22
+ expect(wrapper.classes()).not.toContain("chip");
14
23
  });
15
24
 
16
- expect(wrapper.text()).toContain("John Duck");
17
- expect(wrapper.classes()).toContain("chip");
25
+ test("variant", () => {
26
+ const wrapper = mount({
27
+ template: `<v-chip variant="primary">John Duck</v-chip>`,
28
+ components: { VChip },
29
+ });
30
+
31
+ expect(wrapper.classes()).toContain("variant-primary");
32
+ });
18
33
  });
@@ -1,5 +1,17 @@
1
+ <script setup lang="ts">
2
+ import { unstyledProp, variantProp } from "@/props";
3
+ import { useSettings } from "@/services";
4
+
5
+ const props = defineProps({
6
+ unstyled: unstyledProp,
7
+ variant: variantProp,
8
+ });
9
+
10
+ const { settings } = useSettings();
11
+ const isUnstyled = settings.global.unstyled || settings.components.chip.unstyled || props.unstyled;
12
+ </script>
1
13
  <template>
2
- <div :class="`vuetiful-chip chip`">
14
+ <div :class="`vuetiful-chip ${isUnstyled ? '' : 'chip'} variant-${variant}`">
3
15
  <slot />
4
16
  </div>
5
17
  </template>
@@ -10,5 +10,5 @@ defineProps({
10
10
  </script>
11
11
 
12
12
  <template>
13
- <RadioGroupDescription :as="as"><slot /></RadioGroupDescription>
13
+ <RadioGroupDescription :as="as" class="vuetiful-radio-group-description"><slot /></RadioGroupDescription>
14
14
  </template>
@@ -16,16 +16,16 @@ test("VRadioGroup default props", () => {
16
16
  const wrapper = mount(VRadioGroup);
17
17
  const radioGroupEl = wrapper.find("div");
18
18
  expect(radioGroupEl.classes()).toEqual([
19
- "radio-group",
19
+ "vuetiful-radio-group",
20
20
  "inline-flex",
21
21
  "gap-1",
22
22
  "p-1",
23
- "bg-surface-200-700-token",
24
- "text-surface-900",
25
- "dark:text-surface-50",
26
23
  "border-token",
27
24
  "border-surface-400-500-token",
28
25
  "rounded-container-token",
26
+ "bg-surface-200-700-token",
27
+ "text-surface-900",
28
+ "dark:text-surface-50",
29
29
  ]);
30
30
 
31
31
  const radioGroup = wrapper.find("[data-test='radio-group']");
@@ -41,15 +41,15 @@ test("VRadioGroup custom class props", () => {
41
41
  });
42
42
  const radioGroupEl = wrapper.find("div");
43
43
  expect(radioGroupEl.classes()).toEqual([
44
- "radio-group",
44
+ "vuetiful-radio-group",
45
45
  "inline-flex",
46
46
  "gap-1",
47
47
  "p-1",
48
- "custom-background-class",
49
- "custom-text-class",
50
48
  "border-token",
51
49
  "border-surface-400-500-token",
52
50
  "rounded-container-token",
51
+ "custom-background-class",
52
+ "custom-text-class",
53
53
  ]);
54
54
  });
55
55