@code-coaching/vuetiful 0.14.1 → 0.15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-coaching/vuetiful",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "onchange 'src/**/*.vue' 'src/**/*.ts' 'src/**/*.css' -- npm run build",
@@ -0,0 +1,105 @@
1
+ import { mount } from "@vue/test-utils";
2
+ import { describe, expect, test } from "vitest";
3
+ import { VAvatar } from ".";
4
+
5
+ test("VAvatar", () => {
6
+ expect(VAvatar).toBeTruthy();
7
+ });
8
+
9
+ describe("VAvatar props", () => {
10
+ test("VAvatar defaults", () => {
11
+ const wrapper = mount(VAvatar);
12
+
13
+ expect(wrapper.classes()).toContain("avatar");
14
+ expect(wrapper.classes()).toContain("w-16");
15
+ expect(wrapper.classes()).toContain("rounded-token");
16
+
17
+ const avatarText = wrapper.find(".avatar-text");
18
+ expect(avatarText.exists()).toBe(true);
19
+ expect(avatarText.text()).toBe("");
20
+ expect(avatarText.classes()).toContain("dark:fill-on-surface-token");
21
+ expect(avatarText.classes()).toContain("fill-base-token");
22
+ });
23
+
24
+ test("VAvatar initials", () => {
25
+ const wrapper = mount(VAvatar, {
26
+ props: {
27
+ initials: "JD",
28
+ class: "variant-filled",
29
+ },
30
+ });
31
+
32
+ expect(wrapper.classes()).toContain("avatar");
33
+ expect(wrapper.classes()).toContain("w-16");
34
+ expect(wrapper.classes()).toContain("rounded-token");
35
+
36
+ const avatarText = wrapper.find(".avatar-text");
37
+ expect(avatarText.exists()).toBe(true);
38
+ expect(avatarText.text()).toBe("JD");
39
+ expect(avatarText.classes()).toContain("dark:fill-base-token");
40
+ expect(avatarText.classes()).toContain("fill-on-surface-token");
41
+
42
+ const avatarImage = wrapper.find(".avatar-image");
43
+ expect(avatarImage.exists()).toBe(false);
44
+ });
45
+
46
+ test("VAvatar initials fill", () => {
47
+ const wrapper = mount(VAvatar, {
48
+ props: {
49
+ initials: "JD",
50
+ fill: "custom-fill-class",
51
+ },
52
+ });
53
+
54
+ expect(wrapper.classes()).toContain("avatar");
55
+ expect(wrapper.classes()).toContain("w-16");
56
+ expect(wrapper.classes()).toContain("rounded-token");
57
+
58
+ const avatarText = wrapper.find(".avatar-text");
59
+ expect(avatarText.exists()).toBe(true);
60
+ expect(avatarText.text()).toBe("JD");
61
+ expect(avatarText.classes()).toContain("custom-fill-class");
62
+ });
63
+
64
+ test("VAvatar image", () => {
65
+ const wrapper = mount(VAvatar, {
66
+ props: {
67
+ src: "https://via.placeholder.com/150",
68
+ },
69
+ });
70
+
71
+ expect(wrapper.classes()).toContain("avatar");
72
+ expect(wrapper.classes()).toContain("w-16");
73
+ expect(wrapper.classes()).toContain("rounded-token");
74
+
75
+ const avatarText = wrapper.find(".avatar-text");
76
+ expect(avatarText.exists()).toBe(false);
77
+
78
+ const avatarImage = wrapper.find(".avatar-image");
79
+ expect(avatarImage.exists()).toBe(true);
80
+ expect(avatarImage.attributes("src")).toBe("https://via.placeholder.com/150");
81
+ });
82
+
83
+ test("VAvatar image fallback", async () => {
84
+ const wrapper = mount(VAvatar, {
85
+ props: {
86
+ src: "https://via.placeholder.com/150",
87
+ fallback: "/image/john-duck.png",
88
+ },
89
+ });
90
+
91
+ expect(wrapper.classes()).toContain("avatar");
92
+ expect(wrapper.classes()).toContain("w-16");
93
+ expect(wrapper.classes()).toContain("rounded-token");
94
+
95
+ const avatarText = wrapper.find(".avatar-text");
96
+ expect(avatarText.exists()).toBe(false);
97
+
98
+ const avatarImage = wrapper.find(".avatar-image");
99
+ avatarImage.trigger("error");
100
+ await wrapper.vm.$nextTick();
101
+
102
+ expect(avatarImage.exists()).toBe(true);
103
+ expect(avatarImage.attributes("src")).toBe("/image/john-duck.png");
104
+ });
105
+ });
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ import { CssClasses } from "@/index";
3
+ import { computed, ref, useAttrs } from "vue";
4
+
5
+ const props = defineProps({
6
+ // Initials
7
+ initials: { type: String, default: "" },
8
+ fill: { type: String as () => CssClasses, default: "" },
9
+
10
+ // Image
11
+ src: { type: String, default: "" },
12
+ alt: { type: String, default: "" },
13
+ fallback: { type: String, default: "" },
14
+
15
+ width: { type: String as () => CssClasses, default: "w-16" },
16
+ rounded: { type: String as () => CssClasses, default: "rounded-token" },
17
+ });
18
+
19
+ const imgSrc = ref(props.src);
20
+ const fillInitials = computed(() => {
21
+ if (props.fill) return props.fill;
22
+
23
+ const attrs = useAttrs();
24
+ const classString = attrs.class as string | undefined;
25
+
26
+ if (classString?.includes("variant-filled")) {
27
+ return "fill-on-surface-token dark:fill-base-token";
28
+ }
29
+ return "dark:fill-on-surface-token fill-base-token";
30
+ });
31
+ </script>
32
+ <template>
33
+ <figure
34
+ :class="`avatar isolate flex aspect-square items-center justify-center overflow-hidden font-semibold ${rounded} ${width}`"
35
+ >
36
+ <img class="avatar-image" v-if="src" :src="imgSrc" :alt="alt" @error="() => (imgSrc = fallback)" />
37
+ <svg v-else class="avatar-initials h-full w-full" viewBox="0 0 512 512">
38
+ <text
39
+ x="50%"
40
+ y="50%"
41
+ dominant-baseline="central"
42
+ text-anchor="middle"
43
+ font-weight="bold"
44
+ :font-size="150"
45
+ :class="`avatar-text ${fillInitials}`"
46
+ >
47
+ {{ String(initials).substring(0, 2).toUpperCase() }}
48
+ </text>
49
+ </svg>
50
+ </figure>
51
+ </template>
@@ -71,21 +71,27 @@ const trackSize = computed(() => {
71
71
  <template>
72
72
  <!-- There is some odd behavior with test coverge, v-model must be the last property in this component -->
73
73
  <Switch
74
- :class="`slide-toggle-track flex transition-all duration-[150ms] border-token rounded-container-token ${trackSize} ${
74
+ :class="`slide-toggle rounded-container-token ${
75
75
  disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
76
- } ${switchClass}`"
76
+ }`"
77
77
  :name="name"
78
78
  :as="as"
79
79
  v-slot="{ checked }"
80
80
  v-model="parentModelValue"
81
81
  >
82
- <template v-if="$slots.default">
83
- <span class="sr-only"><slot /></span>
84
- </template>
85
82
  <div
86
- :class="`slide-toggle-thumb h-full w-[50%] scale-[0.8] shadow transition-all duration-[150ms] rounded-token ${
87
- checked ? 'translate-x-full' : 'opacity-90'
88
- } ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} ${thumbClass} bg-opacity-90`"
89
- ></div>
83
+ :class="`slide-toggle-track flex transition-all duration-[150ms] border-token rounded-token ${trackSize} ${
84
+ disabled ? 'cursor-not-allowed' : 'cursor-pointer'
85
+ } ${switchClass}`"
86
+ >
87
+ <template v-if="$slots.default">
88
+ <span class="sr-only"><slot /></span>
89
+ </template>
90
+ <div
91
+ :class="`slide-toggle-thumb h-full w-[50%] scale-[0.8] shadow transition-all duration-[150ms] rounded-token ${
92
+ checked ? 'translate-x-full' : 'opacity-90'
93
+ } ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} ${thumbClass} bg-opacity-90`"
94
+ ></div>
95
+ </div>
90
96
  </Switch>
91
97
  </template>
@@ -1,3 +1,4 @@
1
+ import VAvatar from "./VAvatar.vue";
1
2
  import VBadge from "./VBadge.vue";
2
3
  import VButton from "./VButton.vue";
3
4
  import VChip from "./VChip.vue";
@@ -13,6 +14,7 @@ import VSwitchGroup from "./VSwitch/VSwitchGroup.vue";
13
14
  import VSwitchLabel from "./VSwitch/VSwitchLabel.vue";
14
15
 
15
16
  export {
17
+ VAvatar,
16
18
  VButton,
17
19
  VBadge,
18
20
  VChip,