Skip to content

Commit c3cef09

Browse files
committed
feat(label): add link variant
Closes #2499 Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1377f14 commit c3cef09

4 files changed

Lines changed: 68 additions & 1 deletion

File tree

.changeset/label-link-variant.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@patternfly/elements": minor
3+
---
4+
5+
`pf-label`: added link variant. Set the `href` attribute to render the
6+
label as a clickable link.

elements/pf-label/pf-label.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,23 @@ pf-button {
240240
margin-left: var(--pf-c-label__c-button--MarginLeft, 0.25rem);
241241
}
242242

243+
#link {
244+
color: inherit;
245+
text-decoration: underline;
246+
}
247+
248+
#link:hover {
249+
text-decoration: none;
250+
}
251+
252+
.link {
253+
/** link label background color */
254+
--pf-c-label--BackgroundColor: transparent;
255+
/** link label border width */
256+
--pf-c-label__content--before--BorderWidth: 0;
257+
cursor: pointer;
258+
}
259+
243260
svg {
244261
vertical-align:-0.125em;
245262
fill: currentColor;

elements/pf-label/pf-label.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LitElement, html, isServer, type TemplateResult } from 'lit';
22
import { customElement } from 'lit/decorators/custom-element.js';
33
import { property } from 'lit/decorators/property.js';
44
import { classMap } from 'lit/directives/class-map.js';
5+
import { ifDefined } from 'lit/directives/if-defined.js';
56

67
import { SlotController } from '@patternfly/pfe-core/controllers/slot-controller.js';
78

@@ -68,6 +69,9 @@ export class PfLabel extends LitElement {
6869
/** Text label for a removable label's close button */
6970
@property({ attribute: 'close-button-label' }) closeButtonLabel?: string;
7071

72+
/** When set, the label becomes a link. The label text renders inside an anchor element. */
73+
@property({ reflect: true }) href?: string;
74+
7175
/** Represents the state of the anonymous and icon slots */
7276
#slots = new SlotController(this, null, 'icon');
7377

@@ -79,15 +83,17 @@ export class PfLabel extends LitElement {
7983
}
8084

8185
override render(): TemplateResult<1> {
82-
const { compact, truncated } = this;
86+
const { compact, truncated, href } = this;
8387
const { variant, color, icon } = this;
8488
const hasIcon = !!icon || this.#slots.hasSlotted('icon');
89+
const isLink = !!href;
8590
return html`
8691
<span id="container"
8792
class="${classMap({
8893
hasIcon,
8994
compact,
9095
truncated,
96+
link: isLink,
9197
[variant ?? '']: !!variant,
9298
[color ?? '']: !!color })}">
9399
<!-- slot:
@@ -101,7 +107,11 @@ export class PfLabel extends LitElement {
101107
.icon="${this.icon || undefined as unknown as string}"></pf-icon>
102108
</slot>
103109
<!-- summary: Must contain the text for the label. -->
110+
${isLink ? html`
111+
<a id="link" href="${ifDefined(href)}"><slot id="text"></slot></a>
112+
` : html`
104113
<slot id="text"></slot>
114+
`}
105115
<!-- summary: container for removable labels' close button -->
106116
<span part="close-button" ?hidden=${!this.removable}>
107117
<pf-button plain

elements/pf-label/test/pf-label.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const exampleWithCompactAttribute = html`
2626
<pf-label compact>Default Compact</pf-label>
2727
`;
2828

29+
const exampleWithHref = html`
30+
<pf-label href="https://example.com">Link Label</pf-label>
31+
`;
32+
2933

3034
describe('<pf-label>', function() {
3135
before(function() {
@@ -140,4 +144,34 @@ describe('<pf-label>', function() {
140144
expect(containerStyles.getPropertyValue('padding-bottom')).to.equal('0px');
141145
expect(containerStyles.getPropertyValue('padding-left')).to.equal('8px'); // 0.5rem = 8px @ 16px browser default
142146
});
147+
148+
describe('with href attribute', function() {
149+
let element: PfLabel;
150+
beforeEach(async function() {
151+
element = await createFixture<PfLabel>(exampleWithHref);
152+
await element.updateComplete;
153+
});
154+
155+
it('should render an anchor element', function() {
156+
const link = element.shadowRoot!.querySelector('#link');
157+
expect(link).to.be.an.instanceOf(HTMLAnchorElement);
158+
});
159+
160+
it('should set the href on the anchor', function() {
161+
const link = element.shadowRoot!.querySelector('#link') as HTMLAnchorElement;
162+
expect(link.href).to.equal('https://example.com/');
163+
});
164+
165+
it('should have a transparent background', function() {
166+
const container = element.shadowRoot!.querySelector('#container')!;
167+
expect(getComputedStyle(container).getPropertyValue('background-color')).to.equal('rgba(0, 0, 0, 0)');
168+
});
169+
170+
it('should not render an anchor when href is not set', async function() {
171+
const el = await createFixture<PfLabel>(example);
172+
await el.updateComplete;
173+
const link = el.shadowRoot!.querySelector('#link');
174+
expect(link).to.be.null;
175+
});
176+
});
143177
});

0 commit comments

Comments
 (0)