@code-coaching/vuetiful 0.23.1 → 0.24.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 (151) hide show
  1. package/dist/style.css +1 -1
  2. package/dist/types/components/VBootstrap.vue.d.ts +1 -1
  3. package/dist/types/components/atoms/VAvatar.vue.d.ts +1 -1
  4. package/dist/types/components/atoms/VLightSwitch.vue.d.ts +7 -3
  5. package/dist/types/components/atoms/index.d.ts +13 -13
  6. package/dist/types/components/index.d.ts +2 -2
  7. package/dist/types/components/molecules/VAlert.vue.d.ts +1 -1
  8. package/dist/types/components/molecules/VCodeBlock.vue.d.ts +2 -2
  9. package/dist/types/components/molecules/VDrawer.vue.d.ts +1 -1
  10. package/dist/types/components/molecules/VListbox/VListbox.vue.d.ts +1 -1
  11. package/dist/types/components/molecules/VRail/VRail.vue.d.ts +1 -1
  12. package/dist/types/components/molecules/VRail/VRailTile.vue.d.ts +1 -1
  13. package/dist/types/components/molecules/index.d.ts +21 -21
  14. package/dist/types/directives/clipboard.d.ts +1 -1
  15. package/dist/types/directives/index.d.ts +1 -1
  16. package/dist/types/index.d.ts +6 -6
  17. package/dist/types/props/props.d.ts +1 -1
  18. package/dist/types/services/dark-mode.service.d.ts +13 -13
  19. package/dist/types/services/drawer.service.d.ts +1 -1
  20. package/dist/types/services/index.d.ts +6 -6
  21. package/dist/types/types/index.d.ts +1 -1
  22. package/dist/types/utils/colors/colors.service.d.ts +69 -0
  23. package/dist/types/utils/index.d.ts +7 -3
  24. package/dist/types/utils/theme/theme-switcher.vue.d.ts +1 -1
  25. package/dist/types/utils/theme/theme.service.d.ts +9 -24
  26. package/dist/types/utils/theme/themes.d.ts +35 -0
  27. package/dist/vuetiful.es.mjs +456 -161
  28. package/dist/vuetiful.umd.js +71 -16
  29. package/package.json +1 -1
  30. package/src/assets/main.css +6 -6
  31. package/src/components/VBootstrap.vue +43 -43
  32. package/src/components/atoms/VAvatar.test.ts +71 -71
  33. package/src/components/atoms/VAvatar.vue +22 -23
  34. package/src/components/atoms/VBadge.test.ts +11 -11
  35. package/src/components/atoms/VBadge.vue +2 -2
  36. package/src/components/atoms/VButton.test.ts +82 -82
  37. package/src/components/atoms/VButton.vue +20 -21
  38. package/src/components/atoms/VChip.test.ts +11 -11
  39. package/src/components/atoms/VChip.vue +3 -3
  40. package/src/components/atoms/VLightSwitch.test.ts +63 -14
  41. package/src/components/atoms/VLightSwitch.vue +35 -46
  42. package/src/components/atoms/VRadio/VRadioDescription.test.ts +13 -13
  43. package/src/components/atoms/VRadio/VRadioDescription.vue +2 -2
  44. package/src/components/atoms/VRadio/VRadioGroup.test.ts +40 -40
  45. package/src/components/atoms/VRadio/VRadioGroup.vue +19 -22
  46. package/src/components/atoms/VRadio/VRadioItem.test.ts +67 -67
  47. package/src/components/atoms/VRadio/VRadioItem.vue +10 -13
  48. package/src/components/atoms/VRadio/VRadioLabel.test.ts +13 -13
  49. package/src/components/atoms/VRadio/VRadioLabel.vue +5 -3
  50. package/src/components/atoms/VSwitch/VSwitch.test.ts +33 -33
  51. package/src/components/atoms/VSwitch/VSwitch.vue +24 -27
  52. package/src/components/atoms/VSwitch/VSwitchDescription.test.ts +13 -13
  53. package/src/components/atoms/VSwitch/VSwitchDescription.vue +2 -2
  54. package/src/components/atoms/VSwitch/VSwitchGroup.test.ts +9 -9
  55. package/src/components/atoms/VSwitch/VSwitchGroup.vue +2 -2
  56. package/src/components/atoms/VSwitch/VSwitchLabel.test.ts +19 -19
  57. package/src/components/atoms/VSwitch/VSwitchLabel.vue +2 -2
  58. package/src/components/atoms/index.ts +13 -13
  59. package/src/components/index.ts +2 -2
  60. package/src/components/molecules/VAccordion/VAccordion.test.ts +11 -17
  61. package/src/components/molecules/VAccordion/VAccordion.vue +3 -3
  62. package/src/components/molecules/VAccordion/VAccordionItem.test.ts +55 -55
  63. package/src/components/molecules/VAccordion/VAccordionItem.vue +9 -22
  64. package/src/components/molecules/VAlert.test.ts +38 -38
  65. package/src/components/molecules/VAlert.vue +25 -47
  66. package/src/components/molecules/VCard/VCard.test.ts +25 -25
  67. package/src/components/molecules/VCard/VCard.vue +13 -15
  68. package/src/components/molecules/VCard/VCardBody.test.ts +14 -14
  69. package/src/components/molecules/VCard/VCardBody.vue +4 -8
  70. package/src/components/molecules/VCard/VCardFooter.test.ts +22 -22
  71. package/src/components/molecules/VCard/VCardFooter.vue +10 -8
  72. package/src/components/molecules/VCard/VCardHeader.test.ts +25 -25
  73. package/src/components/molecules/VCard/VCardHeader.vue +7 -8
  74. package/src/components/molecules/VCodeBlock.test.ts +63 -63
  75. package/src/components/molecules/VCodeBlock.vue +27 -34
  76. package/src/components/molecules/VDrawer.test.ts +5 -5
  77. package/src/components/molecules/VDrawer.vue +10 -10
  78. package/src/components/molecules/VListbox/VListbox.test.ts +53 -53
  79. package/src/components/molecules/VListbox/VListbox.vue +31 -32
  80. package/src/components/molecules/VListbox/VListboxButton.test.ts +13 -13
  81. package/src/components/molecules/VListbox/VListboxButton.vue +5 -5
  82. package/src/components/molecules/VListbox/VListboxItem.test.ts +25 -25
  83. package/src/components/molecules/VListbox/VListboxItem.vue +7 -8
  84. package/src/components/molecules/VListbox/VListboxItems.test.ts +14 -14
  85. package/src/components/molecules/VListbox/VListboxItems.vue +9 -11
  86. package/src/components/molecules/VListbox/VListboxLabel.test.ts +10 -10
  87. package/src/components/molecules/VListbox/VListboxLabel.vue +2 -2
  88. package/src/components/molecules/VPreview.test.ts +26 -26
  89. package/src/components/molecules/VPreview.vue +22 -27
  90. package/src/components/molecules/VRail/VRail.test.ts +5 -5
  91. package/src/components/molecules/VRail/VRail.vue +7 -7
  92. package/src/components/molecules/VRail/VRailTile.test.ts +26 -28
  93. package/src/components/molecules/VRail/VRailTile.vue +13 -11
  94. package/src/components/molecules/VShell.test.ts +5 -5
  95. package/src/components/molecules/VShell.vue +11 -20
  96. package/src/components/molecules/VTabs/VTab.test.ts +69 -52
  97. package/src/components/molecules/VTabs/VTab.vue +13 -17
  98. package/src/components/molecules/VTabs/VTabPanel.test.ts +8 -8
  99. package/src/components/molecules/VTabs/VTabPanel.vue +1 -1
  100. package/src/components/molecules/VTabs/VTabs.test.ts +36 -36
  101. package/src/components/molecules/VTabs/VTabs.vue +18 -22
  102. package/src/components/molecules/index.ts +21 -21
  103. package/src/directives/clipboard.test.ts +9 -9
  104. package/src/directives/clipboard.ts +2 -2
  105. package/src/directives/index.ts +1 -1
  106. package/src/env.d.ts +2 -2
  107. package/src/index.ts +7 -7
  108. package/src/props/index.ts +1 -1
  109. package/src/props/props.ts +44 -44
  110. package/src/services/dark-mode.service.test.ts +64 -194
  111. package/src/services/dark-mode.service.ts +35 -54
  112. package/src/services/drawer.service.test.ts +21 -21
  113. package/src/services/drawer.service.ts +10 -10
  114. package/src/services/highlight.service.test.ts +12 -12
  115. package/src/services/highlight.service.ts +1 -1
  116. package/src/services/index.ts +6 -6
  117. package/src/services/rail.service.test.ts +7 -7
  118. package/src/services/rail.service.ts +2 -2
  119. package/src/services/settings.service.test.ts +5 -5
  120. package/src/services/settings.service.ts +1 -1
  121. package/src/styles/all.css +7 -7
  122. package/src/styles/elements/buttons.css +1 -1
  123. package/src/styles/elements/forms.css +7 -7
  124. package/src/styles/elements.css +13 -13
  125. package/src/styles/transitions.css +2 -2
  126. package/src/styles/typography.css +5 -5
  127. package/src/themes/theme-rocket.css +10 -10
  128. package/src/themes/theme-sahara.css +13 -13
  129. package/src/themes/theme-seafoam.css +13 -13
  130. package/src/themes/theme-seasonal.css +4 -4
  131. package/src/themes/theme-skeleton.css +7 -7
  132. package/src/themes/theme-vintage.css +12 -12
  133. package/src/themes/theme-vuetiful-0.0.1.css +13 -13
  134. package/src/types/index.ts +46 -46
  135. package/src/types/tailwind.ts +2 -21
  136. package/src/utils/colors/colors.service.ts +293 -0
  137. package/src/utils/index.ts +7 -3
  138. package/src/utils/platform/platform.service.test.ts +6 -6
  139. package/src/utils/platform/platform.service.ts +1 -1
  140. package/src/utils/theme/callback.test.ts +11 -7
  141. package/src/utils/theme/remove.test.ts +11 -9
  142. package/src/utils/theme/theme-switcher.vue +43 -49
  143. package/src/utils/theme/theme.service.test.ts +194 -84
  144. package/src/utils/theme/theme.service.ts +141 -81
  145. package/src/utils/theme/themes.ts +122 -0
  146. package/dist/types/components/index.test.d.ts +0 -1
  147. package/dist/types/index.test.d.ts +0 -1
  148. package/dist/types/utils/index.test.d.ts +0 -1
  149. package/src/components/index.test.ts +0 -10
  150. package/src/index.test.ts +0 -26
  151. package/src/utils/index.test.ts +0 -11
@@ -1,16 +1,16 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { usePlatform } from "./platform.service";
1
+ import { describe, expect, vi, test } from 'vitest';
2
+ import { usePlatform } from './platform.service';
3
3
 
4
4
  const matchMediaMock = (matches: boolean) => vi.fn(() => ({ matches, onchange: vi.fn() }));
5
5
 
6
- describe("usePlatform", () => {
7
- describe("isBrowser", () => {
8
- it("should return true when window is defined", () => {
6
+ describe('usePlatform', () => {
7
+ describe('isBrowser', () => {
8
+ test('should return true when window is defined', () => {
9
9
  const { isBrowser } = usePlatform();
10
10
  expect(isBrowser).toBe(true);
11
11
  });
12
12
 
13
- it("should return false when window is not defined", () => {
13
+ test('should return false when window is not defined', () => {
14
14
  window = undefined as any;
15
15
  const { isBrowser } = usePlatform();
16
16
  expect(isBrowser).toBe(false);
@@ -1,5 +1,5 @@
1
1
  const usePlatform = () => {
2
- const isBrowser = typeof window !== "undefined";
2
+ const isBrowser = typeof window !== 'undefined';
3
3
  return {
4
4
  isBrowser,
5
5
  };
@@ -1,21 +1,25 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, vi, test } from 'vitest';
2
2
 
3
3
  /**
4
4
  * No clue why, but when this test is added to theme.service.test.ts, it fails.
5
5
  * Running it in isolation works fine.
6
6
  */
7
7
 
8
- describe("given there is a callback", () => {
9
- it("should call the callback", async () => {
10
- const { useTheme } = await import("./theme.service");
11
- const { loadTheme } = useTheme();
8
+ describe('given there is a callback', () => {
9
+ test('should call the callback', async () => {
10
+ const { useTheme } = await import('./theme.service');
11
+ const { applyTheme, themes } = useTheme();
12
12
 
13
13
  const callbackSpy = vi.fn();
14
14
  const callbackFunction = () => {
15
15
  callbackSpy();
16
16
  };
17
- loadTheme("vuetiful", callbackFunction);
18
- const link = document.querySelector("#theme") as HTMLLinkElement;
17
+
18
+ const newTheme = JSON.parse(JSON.stringify(themes[0]));
19
+ newTheme.name = 'new-theme';
20
+ applyTheme(newTheme, callbackFunction);
21
+
22
+ const link = document.querySelector('#vuetiful-theme') as HTMLLinkElement;
19
23
  // @ts-ignore
20
24
  link.onload();
21
25
 
@@ -1,22 +1,24 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, vi, test } from 'vitest';
2
2
 
3
3
  /**
4
4
  * No clue why, but when this test is added to theme.service.test.ts, it fails.
5
5
  * Running it in isolation works fine.
6
6
  */
7
7
 
8
- describe("given there is no existing theme style tag", () => {
9
- it("should create a new theme style tag", async () => {
10
- const { useTheme } = await import("./theme.service");
11
- const { loadTheme } = useTheme();
8
+ describe('given there is no existing theme style tag', () => {
9
+ test('should create a new theme style tag', async () => {
10
+ const { useTheme } = await import('./theme.service');
11
+ const { applyTheme, themes } = useTheme();
12
12
 
13
13
  const removeObject = { remove: () => {} };
14
- const removeSpy = vi.spyOn(removeObject, "remove");
15
- vi.spyOn(window.document, "getElementById").mockReturnValueOnce(removeObject as any);
14
+ const removeSpy = vi.spyOn(removeObject, 'remove');
15
+ vi.spyOn(window.document, 'getElementById').mockReturnValueOnce(removeObject as any);
16
16
 
17
- loadTheme("vuetiful");
17
+ const newTheme = JSON.parse(JSON.stringify(themes[0]));
18
+ newTheme.name = 'new-theme';
19
+ applyTheme(newTheme);
18
20
 
19
- const link = document.querySelector("#theme") as HTMLLinkElement;
21
+ const link = document.querySelector('#vuetiful-theme') as HTMLLinkElement;
20
22
  // @ts-ignore
21
23
  link.onload();
22
24
 
@@ -1,80 +1,74 @@
1
- <template>
2
- <div class="vuetiful-theme-switcher">
3
- <v-button
4
- :class="`vuetiful-theme-switcher__button ${classButton}`"
5
- @click="showPopup = !showPopup"
6
- >
7
- Theme
8
- </v-button>
9
-
10
- <div
11
- v-if="showPopup"
12
- class="vuetiful-theme-switcher__popup absolute z-10 mt-1 space-y-4 p-4 shadow-xl rounded-container-token"
13
- :class="`${background} ${text} ${widthPopup} ${classList}`"
14
- >
15
- <section class="flex items-center justify-between">
16
- <div class="text-lg">Mode</div>
17
- <v-light-switch />
18
- </section>
19
- <nav
20
- class="vuetiful-theme-switcher__popup-list -m-4 flex flex-col gap-4 overflow-y-auto p-4"
21
- :class="`${heightList} ${classList}`"
22
- >
23
- <v-button
24
- class="vuetiful-theme-switcher__popup-list-item h-full w-full p-2 text-center capitalize hover:cursor-pointer"
25
- v-for="(theme, index) in themes"
26
- :class="`${classListItem} ${chosenTheme === theme.name ? 'variant-filled-surface' : ''}`"
27
- :key="index"
28
- @click="loadTheme(theme.name)"
29
- >
30
- {{ theme.name }}
31
- </v-button>
32
- </nav>
33
- </div>
34
- </div>
35
- </template>
36
-
37
1
  <script setup lang="ts">
38
- import { CssClasses, useTheme, VButton, VLightSwitch } from "@/index";
39
- import { onMounted, ref } from "vue";
2
+ import { CssClasses, useTheme, VButton, VLightSwitch } from '@/index';
3
+ import { onMounted, ref } from 'vue';
40
4
 
41
5
  defineProps({
42
6
  background: {
43
7
  type: String as () => CssClasses,
44
- default: "bg-surface-50-900-token",
8
+ default: 'bg-surface-50-900-token',
45
9
  },
46
10
  text: {
47
11
  type: String as () => CssClasses,
48
- default: "text-surface-900-50-token",
12
+ default: 'text-surface-900-50-token',
49
13
  },
50
14
 
51
15
  widthPopup: {
52
16
  type: String as () => CssClasses,
53
- default: "w-60",
17
+ default: 'w-60',
54
18
  },
55
19
  heightList: {
56
20
  type: String as () => CssClasses,
57
- default: "max-h-64 lg:max-h-[500px]",
21
+ default: 'max-h-64 lg:max-h-[500px]',
58
22
  },
59
23
 
60
24
  classButton: {
61
25
  type: String as () => CssClasses,
62
- default: "",
26
+ default: '',
63
27
  },
64
28
  classList: {
65
29
  type: String as () => CssClasses,
66
- default: "",
30
+ default: '',
67
31
  },
68
32
  classListItem: {
69
33
  type: String as () => CssClasses,
70
- default: "",
34
+ default: '',
71
35
  },
72
36
  });
73
37
 
74
- const { initializeTheme, loadTheme, themes, chosenTheme } = useTheme();
38
+ const { applyTheme, themes, chosenTheme } = useTheme();
75
39
 
76
40
  const showPopup = ref(false);
77
- onMounted(() => {
78
- initializeTheme();
79
- });
80
41
  </script>
42
+
43
+ <template>
44
+ <div class="vuetiful-theme-switcher">
45
+ <v-button :class="`vuetiful-theme-switcher__button ${classButton}`" @click="showPopup = !showPopup">
46
+ Theme
47
+ </v-button>
48
+
49
+ <div
50
+ v-if="showPopup"
51
+ class="vuetiful-theme-switcher__popup absolute z-10 mt-1 space-y-4 p-4 shadow-xl rounded-container-token"
52
+ :class="`${background} ${text} ${widthPopup} ${classList}`"
53
+ >
54
+ <section class="flex items-center justify-between">
55
+ <div class="text-lg">Mode</div>
56
+ <v-light-switch />
57
+ </section>
58
+ <nav
59
+ class="vuetiful-theme-switcher__popup-list -m-4 flex flex-col gap-4 overflow-y-auto p-4"
60
+ :class="`${heightList} ${classList}`"
61
+ >
62
+ <v-button
63
+ class="vuetiful-theme-switcher__popup-list-item h-full w-full p-2 text-center capitalize hover:cursor-pointer"
64
+ v-for="(theme, index) in themes"
65
+ :class="`${classListItem} ${chosenTheme.name === theme.name ? 'variant-filled-surface' : ''}`"
66
+ :key="index"
67
+ @click="applyTheme(theme)"
68
+ >
69
+ {{ theme.name }}
70
+ </v-button>
71
+ </nav>
72
+ </div>
73
+ </div>
74
+ </template>
@@ -1,24 +1,25 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import { Theme } from "./theme.service";
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
3
2
 
4
3
  const localStorageMock = {
5
4
  getItem: vi.fn(),
6
5
  setItem: vi.fn(),
7
6
  };
8
7
 
9
- describe("useTheme", () => {
8
+ describe('useTheme', () => {
10
9
  afterEach(() => {
11
10
  vi.resetModules();
12
11
  });
13
- describe("initializetheme", () => {
14
- describe("given not in browser", () => {
15
- it("should set the theme to the default theme if no theme is stored", async () => {
16
- const platform = await import("../platform/platform.service");
17
- vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: false });
12
+ describe('initializetheme', () => {
13
+ describe('given not in browser', () => {
14
+ test('should set the theme to the default theme if no theme is stored', async () => {
15
+ const platform = await import('../platform/platform.service');
16
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
17
+ isBrowser: false,
18
+ });
18
19
 
19
- const localStorageSpy = vi.spyOn(window.localStorage, "getItem");
20
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
20
21
 
21
- const { useTheme } = await import("./theme.service");
22
+ const { useTheme } = await import('./theme.service');
22
23
  const { initializeTheme } = useTheme();
23
24
 
24
25
  initializeTheme();
@@ -27,133 +28,242 @@ describe("useTheme", () => {
27
28
  });
28
29
  });
29
30
 
30
- describe("given in browser", () => {
31
+ describe('given in browser', () => {
31
32
  beforeEach(() => {
32
33
  window.localStorage = localStorageMock as any;
33
34
  });
34
- describe("given no theme is stored", () => {
35
- it("should set the theme to the default theme", async () => {
36
- const platform = await import("../platform/platform.service");
37
- vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
38
35
 
39
- const localStorageSpy = vi.spyOn(window.localStorage, "getItem");
36
+ describe('given no theme is stored', () => {
37
+ test('should set the theme to the default theme', async () => {
38
+ const platform = await import('../platform/platform.service');
39
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
40
+ isBrowser: true,
41
+ });
42
+
43
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
44
+
45
+ const { useTheme } = await import('./theme.service');
46
+ const { initializeTheme, themes, chosenTheme } = useTheme();
47
+
48
+ initializeTheme();
49
+
50
+ expect(localStorageSpy).not.toHaveBeenCalled();
51
+ expect(chosenTheme.value.name).toBe(themes[0].name);
52
+ });
53
+ });
54
+
55
+ describe('given a theme is stored', () => {
56
+ test('should set the theme to the stored theme', async () => {
57
+ const platform = await import('../platform/platform.service');
58
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
59
+ isBrowser: true,
60
+ });
61
+
62
+ const { useTheme } = await import('./theme.service');
63
+ const { initializeTheme, themes, chosenTheme } = useTheme();
40
64
 
41
- const { useTheme } = await import("./theme.service");
42
- const { initializeTheme, THEMES, chosenTheme } = useTheme();
65
+ const customTheme = JSON.parse(JSON.stringify(themes[1]));
66
+ customTheme.name = 'rocket';
43
67
 
68
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
69
+
70
+ document.cookie = 'vuetiful-theme=rocket';
44
71
  initializeTheme();
45
72
 
46
- expect(localStorageSpy).toHaveBeenCalledWith("vuetiful-theme");
47
- expect(chosenTheme.value).toBe(THEMES.VUETIFUL);
73
+ expect(localStorageSpy).not.toHaveBeenCalled();
74
+ expect(chosenTheme.value).toEqual(customTheme);
48
75
  });
49
76
  });
50
- describe("given a theme is stored", () => {
51
- it("should set the theme to the stored theme", async () => {
52
- const platform = await import("../platform/platform.service");
53
- vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
54
77
 
55
- const { useTheme } = await import("./theme.service");
56
- const { initializeTheme, THEMES, chosenTheme } = useTheme();
78
+ describe('given the theme cookie is not a known theme', () => {
79
+ test('should set the theme to the default theme', async () => {
80
+ const platform = await import('../platform/platform.service');
81
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
82
+ isBrowser: true,
83
+ });
84
+
85
+ const { useTheme } = await import('./theme.service');
86
+ const { initializeTheme, themes, chosenTheme } = useTheme();
57
87
 
58
- const localStorageSpy = vi.spyOn(window.localStorage, "getItem");
59
- localStorageSpy.mockReturnValueOnce(THEMES.ROCKET);
88
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
60
89
 
90
+ document.cookie = 'vuetiful-theme=not-a-theme';
61
91
  initializeTheme();
62
92
 
63
- expect(localStorageSpy).toHaveBeenCalledWith("vuetiful-theme");
64
- expect(chosenTheme.value).toBe(THEMES.ROCKET);
93
+ expect(localStorageSpy).not.toHaveBeenCalled();
94
+ expect(chosenTheme.value).toEqual(themes[0]);
65
95
  });
66
96
  });
67
97
 
68
- describe("given the theme is not valid", () => {
69
- it("should set the theme to the default theme", async () => {
70
- const platform = await import("../platform/platform.service");
71
- vi.spyOn(platform, "usePlatform").mockReturnValueOnce({ isBrowser: true });
98
+ describe('given the theme is set to custom', () => {
99
+ test('should set the custom theme from local storage', async () => {
100
+ const platform = await import('../platform/platform.service');
101
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
102
+ isBrowser: true,
103
+ });
72
104
 
73
- const { useTheme } = await import("./theme.service");
74
- const { initializeTheme, THEMES, chosenTheme } = useTheme();
105
+ const { useTheme } = await import('./theme.service');
106
+ const { initializeTheme, themes, chosenTheme } = useTheme();
75
107
 
76
- const localStorageSpy = vi.spyOn(window.localStorage, "getItem");
77
- localStorageSpy.mockReturnValueOnce("invalid-theme");
108
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
109
+ const customTheme = JSON.parse(JSON.stringify(themes[0]));
110
+ customTheme.name = 'custom';
111
+ localStorageSpy.mockReturnValueOnce(JSON.stringify(customTheme));
78
112
 
113
+ document.cookie = 'vuetiful-theme=custom';
79
114
  initializeTheme();
80
115
 
81
- expect(localStorageSpy).toHaveBeenCalledWith("vuetiful-theme");
82
- expect(chosenTheme.value).toBe(THEMES.VUETIFUL);
116
+ expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
117
+ expect(chosenTheme.value).toEqual(customTheme);
83
118
  });
119
+
120
+ test('should set the default theme if an invalid theme is stored', async () => {
121
+ const platform = await import('../platform/platform.service');
122
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
123
+ isBrowser: true,
124
+ });
125
+
126
+ const { useTheme } = await import('./theme.service');
127
+ const { initializeTheme, themes, chosenTheme } = useTheme();
128
+
129
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
130
+ localStorageSpy.mockReturnValueOnce('not-a-theme');
131
+
132
+ document.cookie = 'vuetiful-theme=custom';
133
+ initializeTheme();
134
+
135
+ expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
136
+ expect(chosenTheme.value).toEqual(themes[0]);
137
+ })
138
+
139
+ test('should set the default theme if no theme is stored', async () => {
140
+ const platform = await import('../platform/platform.service');
141
+ vi.spyOn(platform, 'usePlatform').mockReturnValueOnce({
142
+ isBrowser: true,
143
+ });
144
+
145
+ const { useTheme } = await import('./theme.service');
146
+ const { initializeTheme, themes, chosenTheme } = useTheme();
147
+
148
+ const localStorageSpy = vi.spyOn(window.localStorage, 'getItem');
149
+ localStorageSpy.mockReturnValueOnce(null);
150
+
151
+ document.cookie = 'vuetiful-theme=custom';
152
+ initializeTheme();
153
+
154
+ expect(localStorageSpy).toHaveBeenCalledWith('vuetiful-custom-theme');
155
+ expect(chosenTheme.value).toEqual(themes[0]);
156
+ })
84
157
  });
158
+
85
159
  });
86
160
  });
87
161
 
88
- describe("loadTheme", () => {
89
- describe("given theme name exists in themes", () => {
90
- it("should set the theme to the given theme", async () => {
91
- const { useTheme } = await import("./theme.service");
92
- const { loadTheme, THEMES, chosenTheme } = useTheme();
162
+ describe('registerTheme', () => {
163
+ test('should register a theme', async () => {
164
+ const { useTheme } = await import('./theme.service');
165
+ const { registerTheme, themes } = useTheme();
166
+
167
+ const newTheme = JSON.parse(JSON.stringify(themes[0]));
168
+ newTheme.name = 'new-theme';
169
+
170
+ registerTheme(newTheme);
171
+ expect(themes).toContain(newTheme);
172
+ });
173
+
174
+ describe('given the theme is already registered', () => {
175
+ test('should update the theme', async () => {
176
+ const { useTheme } = await import('./theme.service');
177
+ const { registerTheme, themes } = useTheme();
178
+
179
+ const newTheme = JSON.parse(JSON.stringify(themes[0]));
180
+ newTheme.customCss = 'new-custom-css';
93
181
 
94
- loadTheme(THEMES.ROCKET);
182
+ registerTheme(newTheme);
95
183
 
96
- expect(chosenTheme.value).toBe(THEMES.ROCKET);
184
+ expect(themes.find((theme) => theme.name === newTheme.name)).toEqual(newTheme);
97
185
  });
98
186
  });
99
- describe("given theme name does not exist in themes", () => {
100
- it("should set the theme to the default theme", async () => {
101
- const { useTheme } = await import("./theme.service");
102
- const { loadTheme, THEMES, chosenTheme } = useTheme();
187
+ });
188
+
189
+ describe('getThemeFromCookie', () => {
190
+ describe('given there is no cookie', () => {
191
+ test('should return default theme', async () => {
192
+ const { useTheme } = await import('./theme.service');
193
+ const { getThemeFromCookie, themes } = useTheme();
103
194
 
104
- loadTheme("invalid-theme");
195
+ const cookie = '';
196
+ const theme = getThemeFromCookie(cookie);
105
197
 
106
- expect(chosenTheme.value).toBe(THEMES.VUETIFUL);
198
+ expect(theme).toEqual(themes[0]);
107
199
  });
108
200
  });
109
- describe("given default theme is not in themes", () => {
110
- it("should set the theme to the first theme in the themes object", async () => {
111
- const { useTheme } = await import("./theme.service");
112
- const { loadTheme, THEMES, chosenTheme, overwriteThemes, themes } = useTheme();
113
201
 
114
- const theme: Theme = { name: "fake theme", url: "" };
115
- overwriteThemes([themes.value[1], theme]);
202
+ describe('given there is a cookie', () => {
203
+ test('should return the theme', async () => {
204
+ const { useTheme } = await import('./theme.service');
205
+ const { getThemeFromCookie, themes } = useTheme();
206
+
207
+ const cookie = 'vuetiful-theme=rocket';
116
208
 
117
- loadTheme("invalid-theme");
209
+ const theme = getThemeFromCookie(cookie);
118
210
 
119
- expect(chosenTheme.value).toBe(themes.value[0].name);
211
+ const rocketTheme = themes.find((theme) => theme.name === 'rocket');
212
+ expect(theme).toEqual(rocketTheme);
120
213
  });
121
214
  });
122
215
  });
123
216
 
124
- describe("registerAllBuiltInThemes", () => {
125
- it("should register all built in themes", async () => {
126
- const { useTheme } = await import("./theme.service");
127
- const { registerAllBuiltInThemes, THEMES, themes } = useTheme();
217
+ describe('applyThemeSSR', () => {
218
+ test('should apply the theme', async () => {
219
+ const { useTheme } = await import('./theme.service');
220
+ const { applyThemeSSR, themes } = useTheme();
128
221
 
129
- registerAllBuiltInThemes();
222
+ const theme = themes[0];
130
223
 
131
- expect(themes.value).toHaveLength(Object.keys(THEMES).length);
224
+ const preHtml = '<html><head></head><body></body></html>';
225
+ const html = applyThemeSSR(preHtml, theme);
226
+
227
+ const style = html.includes('id="vuetiful-theme"');
228
+ expect(style).toBe(true);
229
+ const body = html.includes('data-theme="vuetiful"');
230
+ expect(body).toBe(true);
132
231
  });
133
- });
134
232
 
135
- describe("registerTheme", () => {
136
- it("should register a theme", async () => {
137
- const { useTheme } = await import("./theme.service");
138
- const { registerTheme } = useTheme();
233
+ test('no customBase, no customHeadings, no gradients, no custom css', async () => {
234
+ const { useTheme } = await import('./theme.service');
235
+ const { applyThemeSSR, themes } = useTheme();
139
236
 
140
- const theme: Theme = { name: "fake theme", url: "" };
141
- expect(registerTheme(theme.name, "")).toEqual(theme);
237
+ const theme = JSON.parse(JSON.stringify(themes[0]));
238
+ theme.fonts.customBase = '';
239
+ theme.fonts.customHeadings = '';
240
+ theme.gradients.light = '';
241
+ theme.gradients.dark = '';
242
+ theme.customCss = '';
243
+
244
+ const preHtml = '<html><head></head><body></body></html>';
245
+ const html = applyThemeSSR(preHtml, theme);
246
+
247
+ const style = html.includes('id="vuetiful-theme"');
248
+ expect(style).toBe(true);
249
+ const body = html.includes('data-theme="vuetiful"');
250
+ expect(body).toBe(true);
142
251
  });
143
- });
144
252
 
145
- describe("saveThemeToStorage", () => {
146
- describe("given the theme does not exist", () => {
147
- it("should not save the theme to storage", async () => {
148
- const { useTheme } = await import("./theme.service");
149
- const { saveThemeToStorage } = useTheme();
253
+ test('invalid hex colors', async () => {
254
+ const { useTheme } = await import('./theme.service');
255
+ const { applyThemeSSR, themes } = useTheme();
150
256
 
151
- const localStorageSpy = vi.spyOn(window.localStorage, "setItem");
257
+ const theme = JSON.parse(JSON.stringify(themes[0]));
258
+ theme.colors.primary = 'invalid';
152
259
 
153
- saveThemeToStorage("invalid-theme");
260
+ const preHtml = '<html><head></head><body></body></html>';
261
+ const html = applyThemeSSR(preHtml, theme);
154
262
 
155
- expect(localStorageSpy).not.toHaveBeenCalled();
156
- });
263
+ const style = html.includes('id="vuetiful-theme"');
264
+ expect(style).toBe(true);
265
+ const body = html.includes('data-theme="vuetiful"');
266
+ expect(body).toBe(true);
157
267
  });
158
268
  });
159
269
  });