Skip to content

Commit e5dc48e

Browse files
author
Erbil Nas
committed
feat(link): add all anchor attrs, non-standalone links support slotted icons
1 parent a6a08cc commit e5dc48e

File tree

5 files changed

+304
-164
lines changed

5 files changed

+304
-164
lines changed

src/components/link/bl-link.css

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@
2222
/* States */
2323
.link:hover {
2424
text-decoration: none;
25-
}
26-
27-
.link:hover:not(.disabled) {
2825
color: var(--hover-color);
2926
}
3027

31-
.link:active:not(.disabled) {
28+
.link:active {
3229
color: var(--active-color);
3330
}
3431

@@ -50,8 +47,8 @@
5047
color: var(--bl-color-primary);
5148
}
5249

53-
.link.standalone.kind-primary:hover:not(.disabled),
54-
.link.standalone.kind-primary:active:not(.disabled) {
50+
.link.standalone.kind-primary:hover,
51+
.link.standalone.kind-primary:active {
5552
color: var(--bl-color-primary-highlight);
5653
}
5754

@@ -60,8 +57,8 @@
6057
color: var(--bl-color-neutral);
6158
}
6259

63-
.link.standalone.kind-neutral:hover:not(.disabled),
64-
.link.standalone.kind-neutral:active:not(.disabled) {
60+
.link.standalone.kind-neutral:hover,
61+
.link.standalone.kind-neutral:active {
6562
color: var(--bl-color-neutral-highlight);
6663
}
6764

@@ -82,25 +79,9 @@
8279
}
8380

8481
/* Icon styles */
85-
.icon {
82+
::slotted([slot="icon"]) {
8683
margin-inline-start: var(--bl-size-3xs);
87-
}
88-
89-
/* Disabled state */
90-
.link.disabled {
91-
opacity: 0.5;
92-
cursor: not-allowed;
93-
}
94-
95-
/* Screen reader only text */
96-
.visually-hidden {
97-
position: absolute;
98-
block-size: 1px;
99-
inline-size: 1px;
100-
padding: 0;
101-
margin: -1px;
102-
overflow: hidden;
103-
clip: rect(0, 0, 0, 0);
104-
white-space: nowrap;
105-
border: 0;
84+
margin-inline-end: var(--bl-size-3xs);
85+
display: inline-block;
86+
vertical-align: middle;
10687
}

src/components/link/bl-link.stories.ts

Lines changed: 119 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,21 @@ import { centeredLayout } from "../../utilities/chromatic-decorators";
55

66

77
interface LinkArgs {
8-
target?: string;
8+
href?: string;
99
variant?: "inline" | "standalone";
1010
size?: "small" | "medium" | "large";
1111
kind?: "primary" | "neutral";
12-
external?: boolean;
13-
disabled?: boolean;
12+
target?: HTMLAnchorElement["target"];
13+
rel?: HTMLAnchorElement["rel"];
14+
hreflang?: HTMLAnchorElement["hreflang"];
15+
type?: HTMLAnchorElement["type"];
16+
referrerPolicy?: HTMLAnchorElement["referrerPolicy"];
17+
download?: HTMLAnchorElement["download"];
18+
ping?: HTMLAnchorElement["ping"];
1419
"aria-label"?: string;
1520
content?: string;
1621
customStyles?: string;
22+
icon?: string;
1723
}
1824

1925
const FIGMA_LINK = "https://www.figma.com/design/RrcLH0mWpIUy4vwuTlDeKN/Baklava-Design-Guide?node-id=23617-1414";
@@ -34,15 +40,17 @@ const meta: Meta<LinkArgs> = {
3440
component:
3541
"<div class=\"bl-docs-container\">" +
3642
"<p class=\"bl-docs-description\">" +
37-
"The Link component is used for navigation between pages or to external URLs." +
43+
"The Link component is used for navigation between pages or to external URLs. " +
44+
"It supports all native anchor tag attributes and provides additional styling variants. " +
45+
"When not using the standalone variant, you can provide a custom icon using the icon slot." +
3846
"</p>" +
3947

4048
"<div class=\"bl-docs-links\" style=\"display: flex; gap: var(--bl-size-2xs); margin-top: var(--bl-size-m);\">" +
4149
"<bl-badge icon=\"document\" style=\"--bl-badge-background: var(--bl-color-surface-hover)\">" +
42-
"<bl-link variant='inline' target='" + ADR_LINK + "' external>ADR</bl-link>" +
50+
"<bl-link variant='inline' href='" + ADR_LINK + "' target='_blank'>ADR</bl-link>" +
4351
"</bl-badge>" +
4452
"<bl-badge icon=\"puzzle\" style=\"--bl-badge-background: var(--bl-color-surface-hover)\">" +
45-
"<bl-link variant='inline' target='" + FIGMA_LINK + "' external>Figma</bl-link>" +
53+
"<bl-link variant='inline' href='" + FIGMA_LINK + "' target='_blank'>Figma</bl-link>" +
4654
"</bl-badge>" +
4755
"</div>" +
4856
"</div>",
@@ -53,9 +61,9 @@ const meta: Meta<LinkArgs> = {
5361
centeredLayout,
5462
],
5563
argTypes: {
56-
target: {
64+
href: {
5765
control: "text",
58-
description: "Target URL for the link",
66+
description: "URL that the hyperlink points to",
5967
table: {
6068
type: { summary: "string" },
6169
},
@@ -87,20 +95,65 @@ const meta: Meta<LinkArgs> = {
8795
defaultValue: { summary: "primary" },
8896
},
8997
},
90-
external: {
91-
control: "boolean",
92-
description: "Whether the link is external",
98+
target: {
99+
control: { type: "select" },
100+
options: ["_self", "_blank", "_parent", "_top"],
101+
description: "Where to display the linked URL",
102+
table: {
103+
type: { summary: "HTMLAnchorElement['target']" },
104+
defaultValue: { summary: "_self" },
105+
},
106+
},
107+
rel: {
108+
control: "text",
109+
description: "Relationship between documents (e.g., noopener noreferrer)",
110+
table: {
111+
type: { summary: "HTMLAnchorElement['rel']" },
112+
},
113+
},
114+
hreflang: {
115+
control: "text",
116+
description: "Language of the linked document",
117+
table: {
118+
type: { summary: "HTMLAnchorElement['hreflang']" },
119+
},
120+
},
121+
type: {
122+
control: "text",
123+
description: "MIME type of the linked document",
124+
table: {
125+
type: { summary: "HTMLAnchorElement['type']" },
126+
},
127+
},
128+
referrerPolicy: {
129+
control: { type: "select" },
130+
options: [
131+
"no-referrer",
132+
"no-referrer-when-downgrade",
133+
"origin",
134+
"origin-when-cross-origin",
135+
"same-origin",
136+
"strict-origin",
137+
"strict-origin-when-cross-origin",
138+
"unsafe-url",
139+
],
140+
description: "Referrer policy for the link",
141+
table: {
142+
type: { summary: "HTMLAnchorElement['referrerPolicy']" },
143+
},
144+
},
145+
download: {
146+
control: "text",
147+
description: "Whether to download the resource instead of navigating to it",
93148
table: {
94-
type: { summary: "boolean" },
95-
defaultValue: { summary: false },
149+
type: { summary: "HTMLAnchorElement['download']" },
96150
},
97151
},
98-
disabled: {
99-
control: "boolean",
100-
description: "Whether the link appears disabled (changes cursor to not-allowed)",
152+
ping: {
153+
control: "text",
154+
description: "URLs to be notified when following the link",
101155
table: {
102-
type: { summary: "boolean" },
103-
defaultValue: { summary: false },
156+
type: { summary: "HTMLAnchorElement['ping']" },
104157
},
105158
},
106159
"aria-label": {
@@ -110,6 +163,13 @@ const meta: Meta<LinkArgs> = {
110163
type: { summary: "string" },
111164
},
112165
},
166+
icon: {
167+
control: "text",
168+
description: "Icon name for custom icon (only applies to non-standalone variants)",
169+
table: {
170+
type: { summary: "string" },
171+
},
172+
},
113173
},
114174
};
115175

@@ -119,30 +179,36 @@ type Story = StoryObj<LinkArgs>;
119179

120180
const LinkTemplate = (args: LinkArgs) => html`
121181
<bl-link
122-
target=${ifDefined(args.target)}
182+
href=${ifDefined(args.href)}
123183
variant=${ifDefined(args.variant)}
124184
size=${ifDefined(args.size)}
125185
kind=${ifDefined(args.kind)}
126-
?external=${args.external}
127-
?disabled=${args.disabled}
186+
target=${ifDefined(args.target)}
187+
rel=${ifDefined(args.rel)}
188+
hreflang=${ifDefined(args.hreflang)}
189+
type=${ifDefined(args.type)}
190+
referrerpolicy=${ifDefined(args.referrerPolicy)}
191+
download=${ifDefined(args.download)}
192+
ping=${ifDefined(args.ping)}
128193
aria-label=${ifDefined(args["aria-label"])}
129194
style=${ifDefined(args.customStyles)}
130195
>
131196
${args.content || "Link Text"}
197+
${args.icon ? html`<bl-icon name="${args.icon}" slot="icon"></bl-icon>` : ""}
132198
</bl-link>
133199
`;
134200

135201
export const Default: Story = {
136202
args: {
137-
target: "/",
203+
href: "/",
138204
content: "Link",
139205
},
140206
render: LinkTemplate,
141207
};
142208

143209
export const InlineLink: Story = {
144210
args: {
145-
target: "/",
211+
href: "/",
146212
variant: "inline",
147213
content: "Link",
148214
},
@@ -176,65 +242,68 @@ export const InlineLink: Story = {
176242

177243
export const StandaloneLink: Story = {
178244
args: {
179-
target: "/",
245+
href: "/",
180246
variant: "standalone",
181247
content: "Link",
182248
},
183249
render: LinkTemplate,
184250
};
185251

186-
export const SizeVariants: Story = {
252+
export const CustomIconLink: Story = {
187253
args: {
188-
variant: "standalone",
254+
href: "/",
255+
content: "Link with Custom Icon",
256+
icon: "external_link",
189257
},
190-
render: () => html`
191-
<div style="display: flex; gap: 16px; align-items: center;">
192-
${LinkTemplate({ target: "/", variant: "standalone", size: "small", content: "Small" })}
193-
${LinkTemplate({ target: "/", variant: "standalone", size: "medium", content: "Medium" })}
194-
${LinkTemplate({ target: "/", variant: "standalone", size: "large", content: "Large" })}
195-
</div>
196-
`,
258+
render: LinkTemplate,
197259
};
198260

199-
export const KindVariants: Story = {
261+
export const SizeVariants: Story = {
200262
args: {
201263
variant: "standalone",
202264
},
203265
render: () => html`
204266
<div style="display: flex; gap: 16px; align-items: center;">
205-
${LinkTemplate({ target: "/", variant: "standalone", kind: "primary", content: "Primary" })}
206-
${LinkTemplate({ target: "/", variant: "standalone", kind: "neutral", content: "Neutral" })}
267+
${LinkTemplate({ href: "/", variant: "standalone", size: "small", content: "Small" })}
268+
${LinkTemplate({ href: "/", variant: "standalone", size: "medium", content: "Medium" })}
269+
${LinkTemplate({ href: "/", variant: "standalone", size: "large", content: "Large" })}
207270
</div>
208271
`,
209272
};
210273

211-
export const ExternalLinks: Story = {
274+
export const KindVariants: Story = {
212275
args: {
213-
external: true,
276+
variant: "standalone",
214277
},
215278
render: () => html`
216279
<div style="display: flex; gap: 16px; align-items: center;">
217-
${LinkTemplate({ target: "https://example.com", external: true, content: "External Link" })}
280+
${LinkTemplate({ href: "/", variant: "standalone", kind: "primary", content: "Primary" })}
281+
${LinkTemplate({ href: "/", variant: "standalone", kind: "neutral", content: "Neutral" })}
218282
</div>
219283
`,
220284
};
221285

222-
export const DisabledLinks: Story = {
286+
export const NativeAnchorAttributes: Story = {
223287
args: {
224-
disabled: true,
288+
href: "https://example.com",
289+
target: "_blank",
290+
rel: "noopener noreferrer",
291+
hreflang: "en",
292+
type: "text/html",
293+
referrerPolicy: "no-referrer",
294+
download: "file.pdf",
295+
ping: "https://analytics.example.com",
296+
content: "External Link with Native Attributes",
225297
},
226-
render: () => html`
227-
<div style="display: flex; gap: 16px; align-items: center;">
228-
${LinkTemplate({ target: "/", disabled: true, content: "Disabled Link" })}
229-
</div>
230-
`,
298+
render: LinkTemplate,
231299
};
232300

233301
export const AccessibleLink: Story = {
234302
args: {
235-
target: "/",
303+
href: "/",
236304
"aria-label": "View your profile settings",
237305
content: "Profile",
306+
238307
},
239308
render: LinkTemplate,
240309
};
@@ -243,17 +312,17 @@ export const CustomInlineColors: Story = {
243312
render: () => html`
244313
<div style="display: flex; gap: 16px; align-items: center;">
245314
${LinkTemplate({
246-
target: "/",
315+
href: "/",
247316
content: "Success Link",
248317
customStyles: "--bl-link-color: var(--bl-color-success); --bl-link-hover-color: var(--bl-color-success-highlight); --bl-link-active-color: var(--bl-color-success-highlight);"
249318
})}
250319
${LinkTemplate({
251-
target: "/",
320+
href: "/",
252321
content: "Warning Link",
253322
customStyles: "--bl-link-color: var(--bl-color-warning); --bl-link-hover-color: var(--bl-color-warning-highlight); --bl-link-active-color: var(--bl-color-warning-highlight);"
254323
})}
255324
${LinkTemplate({
256-
target: "/",
325+
href: "/",
257326
content: "Danger Link",
258327
customStyles: "--bl-link-color: var(--bl-color-danger); --bl-link-hover-color: var(--bl-color-danger-highlight); --bl-link-active-color: var(--bl-color-danger-highlight);"
259328
})}

0 commit comments

Comments
 (0)