srcdev-nuxt-components 9.0.3 → 9.0.5

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.
@@ -0,0 +1,140 @@
1
+ import LinkText from "../LinkText.vue";
2
+ import type { Meta, StoryObj } from "@nuxtjs/storybook";
3
+
4
+ const meta: Meta<typeof LinkText> = {
5
+ title: "Atoms/Text Blocks/LinkText",
6
+ component: LinkText,
7
+ argTypes: {
8
+ to: {
9
+ control: "text",
10
+ description: "Link destination (internal path or external URL)",
11
+ },
12
+ linkText: {
13
+ control: "text",
14
+ description: "Visible link label",
15
+ },
16
+ external: {
17
+ control: "boolean",
18
+ description: "Force external link handling",
19
+ defaultValue: false,
20
+ },
21
+ target: {
22
+ control: { type: "select" },
23
+ options: [undefined, "_blank", "_self"],
24
+ description: "Link target attribute",
25
+ },
26
+ styleClassPassthrough: {
27
+ control: "object",
28
+ description: "Additional classes",
29
+ defaultValue: [],
30
+ },
31
+ },
32
+ };
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof LinkText>;
36
+
37
+ export const Default: Story = {
38
+ args: {
39
+ to: "/",
40
+ linkText: "Read More",
41
+ external: false,
42
+ styleClassPassthrough: [],
43
+ },
44
+ render: (args) => ({
45
+ components: { LinkText },
46
+ setup() {
47
+ return { args };
48
+ },
49
+ template: `<LinkText v-bind="args" />`,
50
+ }),
51
+ };
52
+
53
+ export const WithLeftIcon: Story = {
54
+ args: {
55
+ to: "/",
56
+ linkText: "Go Back",
57
+ styleClassPassthrough: [],
58
+ },
59
+ render: (args) => ({
60
+ components: { LinkText },
61
+ setup() {
62
+ return { args };
63
+ },
64
+ template: `
65
+ <LinkText v-bind="args">
66
+ <template #left>
67
+ <Icon name="lucide:arrow-left" />
68
+ </template>
69
+ </LinkText>
70
+ `,
71
+ }),
72
+ };
73
+
74
+ export const WithRightIcon: Story = {
75
+ args: {
76
+ to: "/",
77
+ linkText: "Learn More",
78
+ styleClassPassthrough: [],
79
+ },
80
+ render: (args) => ({
81
+ components: { LinkText },
82
+ setup() {
83
+ return { args };
84
+ },
85
+ template: `
86
+ <LinkText v-bind="args">
87
+ <template #right>
88
+ <Icon name="lucide:arrow-right" />
89
+ </template>
90
+ </LinkText>
91
+ `,
92
+ }),
93
+ };
94
+
95
+ export const WithBothIcons: Story = {
96
+ args: {
97
+ to: "/",
98
+ linkText: "Explore",
99
+ styleClassPassthrough: [],
100
+ },
101
+ render: (args) => ({
102
+ components: { LinkText },
103
+ setup() {
104
+ return { args };
105
+ },
106
+ template: `
107
+ <LinkText v-bind="args">
108
+ <template #left>
109
+ <Icon name="lucide:sparkles" />
110
+ </template>
111
+ <template #right>
112
+ <Icon name="lucide:arrow-right" />
113
+ </template>
114
+ </LinkText>
115
+ `,
116
+ }),
117
+ };
118
+
119
+ export const ExternalLink: Story = {
120
+ args: {
121
+ to: "https://nuxt.com",
122
+ linkText: "Nuxt Docs",
123
+ external: true,
124
+ target: "_blank",
125
+ styleClassPassthrough: [],
126
+ },
127
+ render: (args) => ({
128
+ components: { LinkText },
129
+ setup() {
130
+ return { args };
131
+ },
132
+ template: `
133
+ <LinkText v-bind="args">
134
+ <template #right>
135
+ <Icon name="lucide:external-link" />
136
+ </template>
137
+ </LinkText>
138
+ `,
139
+ }),
140
+ };
@@ -0,0 +1,168 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mountSuspended } from "@nuxt/test-utils/runtime";
3
+ import LinkText from "../LinkText.vue";
4
+
5
+ describe("LinkText", () => {
6
+ // ─── Mount ───────────────────────────────────────────────────────────────
7
+
8
+ it("mounts without error", async () => {
9
+ const wrapper = await mountSuspended(LinkText, {
10
+ props: { to: "/about", linkText: "About Us" },
11
+ });
12
+ expect(wrapper.vm).toBeTruthy();
13
+ });
14
+
15
+ // ─── Snapshots ───────────────────────────────────────────────────────────
16
+
17
+ it("renders correct HTML structure with default props", async () => {
18
+ const wrapper = await mountSuspended(LinkText, {
19
+ props: { to: "/about", linkText: "About Us" },
20
+ });
21
+ expect(wrapper.html()).toMatchSnapshot();
22
+ });
23
+
24
+ it("renders correct HTML structure with left slot", async () => {
25
+ const wrapper = await mountSuspended(LinkText, {
26
+ props: { to: "/about", linkText: "About Us" },
27
+ slots: { left: "<svg data-testid='icon-left' />" },
28
+ });
29
+ expect(wrapper.html()).toMatchSnapshot();
30
+ });
31
+
32
+ it("renders correct HTML structure with right slot", async () => {
33
+ const wrapper = await mountSuspended(LinkText, {
34
+ props: { to: "/about", linkText: "About Us" },
35
+ slots: { right: "<svg data-testid='icon-right' />" },
36
+ });
37
+ expect(wrapper.html()).toMatchSnapshot();
38
+ });
39
+
40
+ it("renders correct HTML structure with both slots", async () => {
41
+ const wrapper = await mountSuspended(LinkText, {
42
+ props: { to: "/about", linkText: "About Us" },
43
+ slots: {
44
+ left: "<svg data-testid='icon-left' />",
45
+ right: "<svg data-testid='icon-right' />",
46
+ },
47
+ });
48
+ expect(wrapper.html()).toMatchSnapshot();
49
+ });
50
+
51
+ it("renders correct HTML structure with all props set", async () => {
52
+ const wrapper = await mountSuspended(LinkText, {
53
+ props: {
54
+ to: "https://example.com",
55
+ linkText: "Visit Us",
56
+ external: true,
57
+ target: "_blank",
58
+ styleClassPassthrough: ["custom-class"],
59
+ },
60
+ });
61
+ expect(wrapper.html()).toMatchSnapshot();
62
+ });
63
+
64
+ // ─── Base class ──────────────────────────────────────────────────────────
65
+
66
+ it("always has the link-text class", async () => {
67
+ const wrapper = await mountSuspended(LinkText, {
68
+ props: { to: "/", linkText: "Home" },
69
+ });
70
+ expect(wrapper.classes()).toContain("link-text");
71
+ });
72
+
73
+ // ─── linkText ────────────────────────────────────────────────────────────
74
+
75
+ it("renders the linkText in a label span", async () => {
76
+ const wrapper = await mountSuspended(LinkText, {
77
+ props: { to: "/", linkText: "Read More" },
78
+ });
79
+ expect(wrapper.find(".link-text__label").text()).toBe("Read More");
80
+ });
81
+
82
+ it("renders linkText with special characters", async () => {
83
+ const wrapper = await mountSuspended(LinkText, {
84
+ props: { to: "/", linkText: "Édition & Co." },
85
+ });
86
+ expect(wrapper.find(".link-text__label").text()).toBe("Édition & Co.");
87
+ });
88
+
89
+ // ─── Slots ───────────────────────────────────────────────────────────────
90
+
91
+ it("renders no icon spans when no slots provided", async () => {
92
+ const wrapper = await mountSuspended(LinkText, {
93
+ props: { to: "/", linkText: "No Icons" },
94
+ });
95
+ expect(wrapper.find(".link-text__icon--left").exists()).toBe(false);
96
+ expect(wrapper.find(".link-text__icon--right").exists()).toBe(false);
97
+ });
98
+
99
+ it("renders left icon span when left slot is provided", async () => {
100
+ const wrapper = await mountSuspended(LinkText, {
101
+ props: { to: "/", linkText: "With Left Icon" },
102
+ slots: { left: "<svg />" },
103
+ });
104
+ expect(wrapper.find(".link-text__icon--left").exists()).toBe(true);
105
+ expect(wrapper.find(".link-text__icon--right").exists()).toBe(false);
106
+ });
107
+
108
+ it("renders right icon span when right slot is provided", async () => {
109
+ const wrapper = await mountSuspended(LinkText, {
110
+ props: { to: "/", linkText: "With Right Icon" },
111
+ slots: { right: "<svg />" },
112
+ });
113
+ expect(wrapper.find(".link-text__icon--left").exists()).toBe(false);
114
+ expect(wrapper.find(".link-text__icon--right").exists()).toBe(true);
115
+ });
116
+
117
+ it("renders both icon spans when both slots are provided", async () => {
118
+ const wrapper = await mountSuspended(LinkText, {
119
+ props: { to: "/", linkText: "Both Icons" },
120
+ slots: { left: "<svg />", right: "<svg />" },
121
+ });
122
+ expect(wrapper.find(".link-text__icon--left").exists()).toBe(true);
123
+ expect(wrapper.find(".link-text__icon--right").exists()).toBe(true);
124
+ });
125
+
126
+ // ─── Props ───────────────────────────────────────────────────────────────
127
+
128
+ it("passes target prop to NuxtLink", async () => {
129
+ const wrapper = await mountSuspended(LinkText, {
130
+ props: { to: "https://example.com", linkText: "External", target: "_blank" },
131
+ });
132
+ expect(wrapper.attributes("target")).toBe("_blank");
133
+ });
134
+
135
+ // ─── styleClassPassthrough ───────────────────────────────────────────────
136
+
137
+ it("applies a styleClassPassthrough array", async () => {
138
+ const wrapper = await mountSuspended(LinkText, {
139
+ props: {
140
+ to: "/",
141
+ linkText: "Styled",
142
+ styleClassPassthrough: ["class-a", "class-b"],
143
+ },
144
+ });
145
+ expect(wrapper.classes()).toContain("class-a");
146
+ expect(wrapper.classes()).toContain("class-b");
147
+ });
148
+
149
+ it("applies a single styleClassPassthrough string", async () => {
150
+ const wrapper = await mountSuspended(LinkText, {
151
+ props: { to: "/", linkText: "Styled", styleClassPassthrough: "solo-class" },
152
+ });
153
+ expect(wrapper.classes()).toContain("solo-class");
154
+ });
155
+
156
+ // ─── DOM order ───────────────────────────────────────────────────────────
157
+
158
+ it("renders left icon before label and right icon after label", async () => {
159
+ const wrapper = await mountSuspended(LinkText, {
160
+ props: { to: "/", linkText: "Ordered" },
161
+ slots: { left: "<span class='l' />", right: "<span class='r' />" },
162
+ });
163
+ const children = wrapper.element.children;
164
+ expect(children[0].classList.contains("link-text__icon--left")).toBe(true);
165
+ expect(children[1].classList.contains("link-text__label")).toBe(true);
166
+ expect(children[2].classList.contains("link-text__icon--right")).toBe(true);
167
+ });
168
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.0.3",
4
+ "version": "9.0.5",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",
@@ -33,9 +33,8 @@
33
33
  "playwright:update": "SRCDEV_STANDALONE=true npx playwright test --update-snapshots"
34
34
  },
35
35
  "files": [
36
+ ".claude/",
36
37
  "app/",
37
- "types/",
38
- "app/types/components/",
39
38
  "nuxt.config.ts",
40
39
  "types.d.ts"
41
40
  ],