@forsakringskassan/vue-lib-template 1.0.4 → 1.2.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 (69) hide show
  1. package/files/Jenkinsfile +2 -0
  2. package/files/README.md +6 -5
  3. package/files/_editorconfig +3 -0
  4. package/files/_gitignore +3 -0
  5. package/files/_htmlvalidateignore +5 -0
  6. package/files/_prettierignore +4 -1
  7. package/files/api-extractor.lib.json +1 -1
  8. package/files/api-extractor.selectors.json +1 -1
  9. package/files/babel.config.js +2 -0
  10. package/files/cypress/support/common.ts +7 -6
  11. package/files/cypress/support/component.ts +5 -25
  12. package/files/cypress/support/main.scss +2 -0
  13. package/files/cypress/tsconfig.json +3 -3
  14. package/files/cypress.config.ts +14 -75
  15. package/files/docs/build.mts +16 -0
  16. package/files/docs/index.md +2 -0
  17. package/files/docs/kattinformation/index.md +28 -0
  18. package/files/docs/package.json +5 -2
  19. package/files/docs/src/bundle-mocks.mjs +22 -0
  20. package/files/docs/src/global.d.ts +7 -0
  21. package/files/docs/src/main.js +17 -0
  22. package/files/docs/src/main.scss +22 -0
  23. package/files/docs/src/setup.ts +6 -1
  24. package/files/docs/tsconfig.json +3 -0
  25. package/files/eslint.config.mjs +2 -0
  26. package/files/etc/lib.api.md +1 -1
  27. package/files/etc/selectors.api.md +13 -0
  28. package/files/package.json +47 -33
  29. package/files/renovate.json +2 -3
  30. package/files/src/CatInfoEdit.vue +57 -0
  31. package/files/src/CatInfoLogic.spec.ts +118 -0
  32. package/files/src/CatInfoShow.vue +27 -0
  33. package/files/src/FcCatInfoComponent.cy.ts +74 -0
  34. package/files/src/FcCatInfoComponent.vue +81 -0
  35. package/files/src/FcCatInfoComponentLiveExample.vue +42 -0
  36. package/files/src/cat-api-mock.spec.ts +27 -0
  37. package/files/src/cat-api.mock.mts +26 -0
  38. package/files/src/cat-api.spec.ts +65 -0
  39. package/files/src/cat-api.ts +15 -0
  40. package/files/src/cat-data.ts +40 -0
  41. package/files/src/cat-info-logic.ts +52 -0
  42. package/files/src/cat-types.ts +11 -0
  43. package/files/src/examples/CatInfoExample.vue +20 -0
  44. package/files/src/index.ts +3 -1
  45. package/files/src/local.ts +1 -1
  46. package/files/src/mocks.mts +6 -0
  47. package/files/src/selectors/FcCatInfoComponent.selectors.ts +74 -0
  48. package/files/src/selectors/index.ts +1 -1
  49. package/files/src/style.scss +1 -0
  50. package/files/src/vite-dev/app.vue +1 -1
  51. package/files/tsconfig.cypress.json +1 -1
  52. package/files/tsconfig.json +1 -1
  53. package/files/tsconfig.lib.json +8 -2
  54. package/files/tsconfig.selectors.json +1 -1
  55. package/files/vite.config.mts +5 -1
  56. package/hooks/install.mjs +9 -1
  57. package/index.js +1 -0
  58. package/package.json +25 -16
  59. package/files/cypress/support/commands/forced-colors.ts +0 -33
  60. package/files/cypress/support/component-index.html +0 -14
  61. package/files/cypress/types/cy-mount.d.ts +0 -9
  62. package/files/cypress/types/forced-colors.d.ts +0 -18
  63. package/files/docs/components/AwesomeComponent.md +0 -27
  64. package/files/docs/components/index.md +0 -5
  65. package/files/src/AwesomeComponent.cy.ts +0 -9
  66. package/files/src/AwesomeComponent.vue +0 -45
  67. package/files/src/AwesomeComponentLogic.spec.ts +0 -30
  68. package/files/src/awesome-component-logic.ts +0 -28
  69. package/files/src/examples/DummyExample.vue +0 -7
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { FButton, FNumericTextField, FTextField, FValidationForm } from "@fkui/vue";
3
+ import { type Cat } from "./cat-types";
4
+
5
+ interface Props {
6
+ cat: Cat;
7
+ }
8
+
9
+ const props = defineProps<Props>();
10
+
11
+ const emit = defineEmits<{
12
+ (e: "submit", cat: Cat): void;
13
+ (e: "cancel"): void;
14
+ }>();
15
+
16
+ // Create a local copy for editing
17
+ const editForm = { ...props.cat };
18
+ </script>
19
+
20
+ <template>
21
+ <div data-test="cat-info-edit">
22
+ <f-validation-form @submit="emit('submit', editForm)">
23
+ <template #error-message> Oj, du har glömt fylla i något. Gå till: </template>
24
+ <template #default>
25
+ <f-text-field v-model="editForm.name" v-validation.required.maxLength="100" v-test="'edit-name'">
26
+ Namn
27
+ </f-text-field>
28
+
29
+ <f-numeric-text-field v-model="editForm.age" v-validation.required v-test="'edit-age'">
30
+ Ålder
31
+ </f-numeric-text-field>
32
+
33
+ <f-text-field v-model="editForm.breed" v-validation.required.maxLength="100" v-test="'edit-breed'">
34
+ Ras
35
+ </f-text-field>
36
+
37
+ <f-text-field v-model="editForm.color" v-validation.required.maxLength="100" v-test="'edit-color'">
38
+ Färg
39
+ </f-text-field>
40
+
41
+ <f-text-field
42
+ v-model="editForm.favoriteFood"
43
+ v-validation.maxLength="500"
44
+ v-test="'edit-favorite-food'"
45
+ >
46
+ Favoritmat
47
+ </f-text-field>
48
+ <div class="button-group">
49
+ <f-button class="button-group__item" size="large" variant="primary" type="submit"> Spara </f-button>
50
+ <f-button class="button-group__item" size="large" variant="secondary" @click="emit('cancel')">
51
+ Avbryt
52
+ </f-button>
53
+ </div>
54
+ </template>
55
+ </f-validation-form>
56
+ </div>
57
+ </template>
@@ -0,0 +1,118 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { catGetById } from "./cat-api";
3
+ import { useCatInfo } from "./cat-info-logic";
4
+
5
+ vi.mock(import("./cat-api"));
6
+ //vi.useFakeTimers();
7
+
8
+ describe("useCatInfo", () => {
9
+ beforeEach(() => {
10
+ vi.mocked(catGetById).mockReset();
11
+ });
12
+
13
+ it("should have initial state", () => {
14
+ expect.assertions(3);
15
+ const { loading, cat, error } = useCatInfo();
16
+ expect(loading.value).toBe(false);
17
+ expect(cat.value).toBeNull();
18
+ expect(error.value).toBeNull();
19
+ });
20
+
21
+ it("should set loading then data for valid cat ID", async () => {
22
+ expect.assertions(5);
23
+ const { loading, cat, error, fetchCat } = useCatInfo();
24
+ const mockCat = {
25
+ id: "whiskers-001",
26
+ name: "Whiskers McFluffington",
27
+ age: 3,
28
+ breed: "European Shorthair",
29
+ color: "Orange Tabby",
30
+ favoriteFood: "Tuna",
31
+ };
32
+ vi.mocked(catGetById).mockResolvedValue(mockCat);
33
+
34
+ const fetchPromise = fetchCat("whiskers-001");
35
+
36
+ // loading should be true immediately
37
+ expect(loading.value).toBe(true);
38
+
39
+ await fetchPromise;
40
+
41
+ expect(loading.value).toBe(false);
42
+ expect(cat.value).not.toBeNull();
43
+ expect(cat.value?.name).toBe("Whiskers McFluffington");
44
+ expect(error.value).toBeNull();
45
+ });
46
+
47
+ it("should set error for invalid cat ID", async () => {
48
+ expect.assertions(4);
49
+ const { loading, cat, error, fetchCat } = useCatInfo();
50
+ vi.mocked(catGetById).mockRejectedValue(
51
+ new Error("Cat with ID 'invalid-id' not found"),
52
+ );
53
+
54
+ await fetchCat("invalid-id");
55
+
56
+ expect(loading.value).toBe(false);
57
+ expect(cat.value).toBeNull();
58
+ expect(error.value).not.toBeNull();
59
+ expect(error.value).toContain("not found");
60
+ });
61
+
62
+ it("should modify cat data", () => {
63
+ expect.assertions(2);
64
+ const { cat, updateCat } = useCatInfo();
65
+ const testCat = {
66
+ id: "test",
67
+ name: "Original",
68
+ age: 5,
69
+ breed: "Test",
70
+ color: "Test",
71
+ favoriteFood: "Test",
72
+ };
73
+ cat.value = testCat;
74
+
75
+ updateCat({ ...testCat, name: "Updated" });
76
+
77
+ expect(cat.value.name).toBe("Updated");
78
+ expect(cat.value.age).toBe(5);
79
+ });
80
+
81
+ it("should fetch cat for luna-002", async () => {
82
+ expect.assertions(2);
83
+ const { cat, fetchCat } = useCatInfo();
84
+ const mockCat = {
85
+ id: "luna-002",
86
+ name: "Luna the Midnight Prowler",
87
+ age: 2,
88
+ breed: "Bombay",
89
+ color: "Midnight Black",
90
+ favoriteFood: "Salmon",
91
+ };
92
+ vi.mocked(catGetById).mockResolvedValue(mockCat);
93
+
94
+ await fetchCat("luna-002");
95
+
96
+ expect(cat.value!.name).toBe("Luna the Midnight Prowler");
97
+ expect(cat.value!.color).toBe("Midnight Black");
98
+ });
99
+
100
+ it("should fetch cat for muffin-003", async () => {
101
+ expect.assertions(2);
102
+ const { cat, fetchCat } = useCatInfo();
103
+ const mockCat = {
104
+ id: "muffin-003",
105
+ name: "Sir Muffin III",
106
+ age: 5,
107
+ breed: "Maine Coon",
108
+ color: "Calico",
109
+ favoriteFood: "Chicken",
110
+ };
111
+ vi.mocked(catGetById).mockResolvedValue(mockCat);
112
+
113
+ await fetchCat("muffin-003");
114
+
115
+ expect(cat.value!.name).toBe("Sir Muffin III");
116
+ expect(cat.value!.breed).toBe("Maine Coon");
117
+ });
118
+ });
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import { computed } from "vue";
3
+ import { FDefinitionList } from "@fkui/vue";
4
+ import { type Cat } from "./cat-types";
5
+
6
+ interface Props {
7
+ cat: Cat;
8
+ }
9
+
10
+ const props = defineProps<Props>();
11
+
12
+ const catDefinitions = computed(() => {
13
+ return [
14
+ { term: "Namn", definition: props.cat.name },
15
+ { term: "Ålder", definition: `${props.cat.age} år` },
16
+ { term: "Ras", definition: props.cat.breed },
17
+ { term: "Färg", definition: props.cat.color },
18
+ { term: "Favoritmat", definition: props.cat.favoriteFood },
19
+ ];
20
+ });
21
+ </script>
22
+
23
+ <template>
24
+ <div data-test="cat-info-view">
25
+ <f-definition-list :definitions="catDefinitions" />
26
+ </div>
27
+ </template>
@@ -0,0 +1,74 @@
1
+ import { FLoaderSelectors } from "@fkui/vue/selectors";
2
+ import FcCatInfoComponent from "./FcCatInfoComponent.vue";
3
+ import { cats } from "./cat-data";
4
+ import { FcCatInfoComponentSelectors } from "./selectors";
5
+
6
+ describe("FcCatInfoComponent", () => {
7
+ const testCat = cats[0];
8
+ const catInfoSelectors = FcCatInfoComponentSelectors();
9
+ const loaderSelectors = FLoaderSelectors();
10
+
11
+ it("verifies loading text", () => {
12
+ cy.mount(FcCatInfoComponent, {
13
+ props: {
14
+ catId: testCat.id,
15
+ },
16
+ });
17
+
18
+ cy.get(catInfoSelectors.loadingContainer()).should("be.visible");
19
+ cy.get(loaderSelectors.waitText()).should(
20
+ "contain.text",
21
+ "Hämtar kattinformation...",
22
+ );
23
+ });
24
+
25
+ it("verifies all fields in view mode have correct data", () => {
26
+ cy.mount(FcCatInfoComponent, {
27
+ props: {
28
+ catId: testCat.id,
29
+ isEditable: false,
30
+ },
31
+ });
32
+
33
+ // Wait for loading to disappear
34
+ cy.get(catInfoSelectors.loadingContainer()).should("not.exist");
35
+
36
+ const view = catInfoSelectors.viewContainer();
37
+ cy.get(view).should("be.visible");
38
+
39
+ cy.get(view).contains(testCat.name).should("be.visible");
40
+ cy.get(view).contains(`${testCat.age} år`).should("be.visible");
41
+ cy.get(view).contains(testCat.breed).should("be.visible");
42
+ cy.get(view).contains(testCat.color).should("be.visible");
43
+ cy.get(view).contains(testCat.favoriteFood).should("be.visible");
44
+ });
45
+
46
+ it("verifies all fields in edit mode have correct data", () => {
47
+ cy.mount(FcCatInfoComponent, {
48
+ props: {
49
+ catId: testCat.id,
50
+ isEditable: true,
51
+ },
52
+ });
53
+
54
+ // Wait for loading to disappear
55
+ cy.get(catInfoSelectors.loadingContainer()).should("not.exist");
56
+ cy.get(catInfoSelectors.editContainer()).should("be.visible");
57
+
58
+ cy.get(catInfoSelectors.nameInput())
59
+ .should("be.visible")
60
+ .should("have.value", testCat.name);
61
+ cy.get(catInfoSelectors.ageInput())
62
+ .should("be.visible")
63
+ .should("have.value", testCat.age.toString());
64
+ cy.get(catInfoSelectors.breedInput())
65
+ .should("be.visible")
66
+ .should("have.value", testCat.breed);
67
+ cy.get(catInfoSelectors.colorInput())
68
+ .should("be.visible")
69
+ .should("have.value", testCat.color);
70
+ cy.get(catInfoSelectors.favoriteFoodInput())
71
+ .should("be.visible")
72
+ .should("have.value", testCat.favoriteFood);
73
+ });
74
+ });
@@ -0,0 +1,81 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch } from "vue";
3
+ import { FCard, FLoader } from "@fkui/vue";
4
+ import CatInfoEdit from "./CatInfoEdit.vue";
5
+ import CatInfoShow from "./CatInfoShow.vue";
6
+ import { useCatInfo } from "./cat-info-logic";
7
+ import { type Cat } from "./cat-types";
8
+
9
+ interface Props {
10
+ /**
11
+ * The cat ID to display
12
+ */
13
+ catId: string;
14
+ /**
15
+ * Whether the cat information should be editable
16
+ */
17
+ isEditable?: boolean;
18
+ }
19
+
20
+ const props = defineProps<Props>();
21
+
22
+ const { loading, cat, fetchCat, updateCat } = useCatInfo();
23
+
24
+ const editForm = ref({ id: "", name: "", age: 0, breed: "", color: "", favoriteFood: "" });
25
+
26
+ watch(
27
+ () => props.catId,
28
+ async (newCatId) => {
29
+ if (!newCatId) {
30
+ return;
31
+ }
32
+
33
+ await fetchCat(newCatId);
34
+ if (cat.value) {
35
+ editForm.value = { ...cat.value };
36
+ }
37
+ },
38
+ { immediate: true },
39
+ );
40
+
41
+ /**
42
+ * Submit the edited cat information
43
+ */
44
+ function onSubmit(updatedCat: Cat): void {
45
+ updateCat(updatedCat);
46
+ }
47
+
48
+ /**
49
+ * Cancel editing and reload original data
50
+ */
51
+ async function onCancel(): Promise<void> {
52
+ if (!props.catId) {
53
+ return;
54
+ }
55
+
56
+ await fetchCat(props.catId);
57
+ if (cat.value) {
58
+ editForm.value = { ...cat.value };
59
+ }
60
+ }
61
+ </script>
62
+
63
+ <template>
64
+ <f-card>
65
+ <template #header>
66
+ <h2>Kattinformation</h2>
67
+ </template>
68
+ <template #default>
69
+ <div>
70
+ <!-- Loading state -->
71
+ <f-loader v-if="loading" show data-test="cat-info-loading"> Hämtar kattinformation... </f-loader>
72
+
73
+ <!-- Cat data - View mode -->
74
+ <cat-info-show v-else-if="cat && !isEditable" :cat />
75
+
76
+ <!-- Cat data - Edit mode -->
77
+ <cat-info-edit v-else-if="cat && isEditable" :cat @submit="onSubmit" @cancel="onCancel" />
78
+ </div>
79
+ </template>
80
+ </f-card>
81
+ </template>
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import { defineComponent } from "vue";
3
+ import { FCheckboxField, FSelectField } from "@fkui/vue";
4
+ import { LiveExample } from "@forsakringskassan/docs-live-example";
5
+ import FcCatInfoComponent from "./FcCatInfoComponent.vue";
6
+ import { getCatIds } from "./cat-data";
7
+
8
+ export default defineComponent({
9
+ name: "FcCatInfoComponentLiveExample",
10
+ components: { LiveExample, FSelectField, FCheckboxField },
11
+ data() {
12
+ return {
13
+ selectedCatId: "whiskers-001",
14
+ isEditable: false,
15
+ catIds: getCatIds(),
16
+ };
17
+ },
18
+ computed: {
19
+ components(): object {
20
+ return { FcCatInfoComponent };
21
+ },
22
+ editableAttr(): string {
23
+ return this.isEditable ? " is-editable" : "";
24
+ },
25
+ template(): string {
26
+ return /* HTML */ ` <fc-cat-info-component cat-id="${this.selectedCatId}" ${this.editableAttr} /> `;
27
+ },
28
+ },
29
+ });
30
+ </script>
31
+
32
+ <template>
33
+ <live-example :components :template>
34
+ <f-select-field v-model="selectedCatId">
35
+ <template #label>Välj katt</template>
36
+ <option value="whiskers-001">Whiskers McFluffington</option>
37
+ <option value="luna-002">Luna the Midnight Prowler</option>
38
+ <option value="muffin-003">Sir Muffin III</option>
39
+ </f-select-field>
40
+ <f-checkbox-field v-model="isEditable" :value="true"> Redigerbar </f-checkbox-field>
41
+ </live-example>
42
+ </template>
@@ -0,0 +1,27 @@
1
+ import { matchRequest } from "@forsakringskassan/apimock-express/browser";
2
+ import { describe, expect, it } from "vitest";
3
+ import mocks from "./mocks.mjs";
4
+
5
+ describe("Cat API Mock Reproduction", () => {
6
+ it("should return 200 for a valid cat id /api/cat?id=whiskers-001", async () => {
7
+ expect.assertions(2);
8
+ const req = new Request(
9
+ "http://localhost:8080/api/cat?id=whiskers-001",
10
+ );
11
+ const response = await matchRequest(mocks, req);
12
+ expect(response.status).toBe(200);
13
+
14
+ const data = await response.json();
15
+ expect(data.id).toBe("whiskers-001");
16
+ });
17
+
18
+ it("should return 404 for an invalid cat id /api/cat?id=unknown", async () => {
19
+ expect.assertions(2);
20
+ const req = new Request("http://localhost:8080/api/cat?id=unknown");
21
+ const response = await matchRequest(mocks, req);
22
+ expect(response.status).toBe(404);
23
+
24
+ const data = await response.json();
25
+ expect(data.error).toContain("not found");
26
+ });
27
+ });
@@ -0,0 +1,26 @@
1
+ import { defineMock } from "@forsakringskassan/apimock-express/helpers";
2
+ import { cats } from "./cat-data.ts";
3
+ import type { Cat } from "./cat-types.ts";
4
+
5
+ export default defineMock<Cat | { error: string }>({
6
+ meta: {
7
+ url: "/cat",
8
+ method: "GET",
9
+ },
10
+ defaultResponse(params) {
11
+ const id = params.parameters?.id;
12
+ const cat = cats.find((c) => c.id === id);
13
+ if (cat) {
14
+ return {
15
+ status: 200,
16
+ body: cat,
17
+ delay: 300,
18
+ };
19
+ }
20
+ return {
21
+ status: 404,
22
+ body: { error: `Cat with ID "${id}" not found` },
23
+ delay: 300,
24
+ };
25
+ },
26
+ });
@@ -0,0 +1,65 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { catGetById } from "./cat-api";
3
+
4
+ describe("catGetById", () => {
5
+ beforeEach(() => {
6
+ vi.stubGlobal("fetch", vi.fn());
7
+ });
8
+
9
+ it("should return a cat when the response is ok", async () => {
10
+ expect.assertions(2);
11
+ const mockCat = {
12
+ id: "whiskers-001",
13
+ name: "Whiskers McFluffington",
14
+ age: 3,
15
+ breed: "European Shorthair",
16
+ color: "Orange Tabby",
17
+ favoriteFood: "Tuna",
18
+ };
19
+
20
+ vi.mocked(fetch).mockResolvedValue({
21
+ ok: true,
22
+ json: () => Promise.resolve(mockCat),
23
+ } as Response);
24
+
25
+ const result = await catGetById("whiskers-001");
26
+
27
+ expect(fetch).toHaveBeenCalledWith("/api/cat?id=whiskers-001");
28
+ expect(result).toEqual(mockCat);
29
+ });
30
+
31
+ it("should throw an error with API message when response is not ok", async () => {
32
+ expect.assertions(1);
33
+ const errorResponse = { error: "Cat with ID 'unknown' not found" };
34
+
35
+ vi.mocked(fetch).mockResolvedValue({
36
+ ok: false,
37
+ json: () => Promise.resolve(errorResponse),
38
+ } as Response);
39
+
40
+ await expect(catGetById("unknown")).rejects.toThrow(
41
+ "Cat with ID 'unknown' not found",
42
+ );
43
+ });
44
+
45
+ it("should throw a generic error when response is not ok and no error message is provided", async () => {
46
+ expect.assertions(1);
47
+ vi.mocked(fetch).mockResolvedValue({
48
+ ok: false,
49
+ json: () => Promise.resolve({}),
50
+ } as Response);
51
+
52
+ await expect(catGetById("unknown")).rejects.toThrow(
53
+ "Failed to fetch cat with ID unknown",
54
+ );
55
+ });
56
+
57
+ it("should throw an error when fetch fails", async () => {
58
+ expect.assertions(1);
59
+ vi.mocked(fetch).mockRejectedValue(new Error("Network failure"));
60
+
61
+ await expect(catGetById("whiskers-001")).rejects.toThrow(
62
+ "Network failure",
63
+ );
64
+ });
65
+ });
@@ -0,0 +1,15 @@
1
+ import type { Cat } from "./cat-types";
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export async function catGetById(id: string): Promise<Cat> {
7
+ const response = await fetch(`/api/cat?id=${id}`);
8
+
9
+ if (!response.ok) {
10
+ const errorData = (await response.json()) as { error?: string };
11
+ throw new Error(errorData.error ?? `Failed to fetch cat with ID ${id}`);
12
+ }
13
+
14
+ return (await response.json()) as Cat;
15
+ }
@@ -0,0 +1,40 @@
1
+ import type { Cat } from "./cat-types";
2
+
3
+ /**
4
+ * Mock cat data for the FcCatInfoComponent
5
+ */
6
+
7
+ export const cats: Cat[] = [
8
+ {
9
+ id: "whiskers-001",
10
+ name: "Whiskers McFluffington",
11
+ age: 3,
12
+ breed: "Norsk skogkatt",
13
+ color: "Orange Tabby",
14
+ favoriteFood: "Sardiner i champagne-sås",
15
+ },
16
+ {
17
+ id: "luna-002",
18
+ name: "Luna the Midnight Prowler",
19
+ age: 5,
20
+ breed: "Svart huskatt",
21
+ color: "Midnight Black",
22
+ favoriteFood: "Döda flugor",
23
+ },
24
+ {
25
+ id: "muffin-003",
26
+ name: "Sir Muffin III",
27
+ age: 2,
28
+ breed: "Maine Coon",
29
+ color: "Silver and Gray",
30
+ favoriteFood: "Lyxig tonfisk-mousse med en touch av kattmynta",
31
+ },
32
+ ];
33
+
34
+ /**
35
+ * Get all available cat IDs
36
+ * @returns Array of cat IDs
37
+ */
38
+ export function getCatIds(): string[] {
39
+ return cats.map((cat) => cat.id);
40
+ }
@@ -0,0 +1,52 @@
1
+ import type { Ref } from "vue";
2
+ import { ref } from "vue";
3
+ import { catGetById } from "./cat-api";
4
+ import type { Cat } from "./cat-types";
5
+
6
+ /**
7
+ * Business logic for the FcCatInfoComponent.
8
+ * Manages fetching and updating cat information from mock backend.
9
+ */
10
+ export function useCatInfo(): {
11
+ loading: Ref<boolean>;
12
+ cat: Ref<Cat | null>;
13
+ error: Ref<string | null>;
14
+ fetchCat: (catId: string) => Promise<void>;
15
+ updateCat: (newCat: Cat) => void;
16
+ } {
17
+ const loading = ref(false);
18
+ const cat = ref<Cat | null>(null);
19
+ const error = ref<string | null>(null);
20
+
21
+ /**
22
+ * Fetch cat data from mock backend
23
+ */
24
+ async function fetchCat(catId: string): Promise<void> {
25
+ loading.value = true;
26
+ error.value = null;
27
+
28
+ try {
29
+ // Get cat from mock backend
30
+ const fetchedCat = await catGetById(catId);
31
+ cat.value = { ...fetchedCat };
32
+ } catch (err) {
33
+ error.value =
34
+ err instanceof Error ? err.message : "Unknown error occurred";
35
+ cat.value = null;
36
+ } finally {
37
+ loading.value = false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Update cat information
43
+ * @param updates - Partial cat object with fields to update
44
+ */
45
+ function updateCat(newCat: Cat): void {
46
+ if (cat.value) {
47
+ cat.value = { ...newCat };
48
+ }
49
+ }
50
+
51
+ return { loading, cat, error, fetchCat, updateCat };
52
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Type definitions for cats
3
+ */
4
+ export interface Cat {
5
+ id: string;
6
+ name: string;
7
+ age: number;
8
+ breed: string;
9
+ color: string;
10
+ favoriteFood: string;
11
+ }
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { ref } from "vue";
3
+ import FcCatInfoComponent from "../FcCatInfoComponent.vue";
4
+
5
+ const selectedCatId = ref("whiskers-001");
6
+ const isEditable = ref(false);
7
+ </script>
8
+
9
+ <template>
10
+ <div class="cat-example__component">
11
+ <fc-cat-info-component :cat-id="selectedCatId" :is-editable />
12
+ </div>
13
+ </template>
14
+
15
+ <style lang="scss" scoped>
16
+ .cat-example__component {
17
+ margin: 2rem;
18
+ max-width: 800px;
19
+ }
20
+ </style>
@@ -1 +1,3 @@
1
- export { default as AwesomeComponent } from "./AwesomeComponent.vue";
1
+ import "./style.scss";
2
+
3
+ export { default as FcCatInfoComponent } from "./FcCatInfoComponent.vue";
@@ -6,9 +6,9 @@ import {
6
6
  ValidationPlugin,
7
7
  setRunningContext,
8
8
  } from "@fkui/vue";
9
- import "./local.scss";
10
9
  import { type SetupOptions } from "@forsakringskassan/vite-lib-config";
11
10
  import "@fkui/icon-lib-default/dist/f";
11
+ import "./local.scss";
12
12
 
13
13
  export function setup(options: SetupOptions): void {
14
14
  const { rootComponent, selector } = options;