Skip to content

Commit 06a2dd7

Browse files
authored
feature: Add href to cards (#554)
* add href to card * add href to user card * add href to content node * add href to media card * remove depricated comment and naming * add target and rel * update stories
1 parent 04ef2e2 commit 06a2dd7

File tree

7 files changed

+125
-26
lines changed

7 files changed

+125
-26
lines changed

packages/uui-card-content-node/lib/uui-card-content-node.element.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
33
import { UUICardElement } from '@umbraco-ui/uui-card/lib';
44
import { css, html, nothing } from 'lit';
55
import { property, state } from 'lit/decorators.js';
6+
import { ifDefined } from 'lit/directives/if-defined.js';
67

78
/**
89
* @element uui-card-content-node
@@ -113,21 +114,40 @@ export class UUICardContentNodeElement extends UUICardElement {
113114
return html`<uui-icon .svg="${this.fallbackIcon}"></uui-icon>`;
114115
}
115116

117+
#renderButton() {
118+
return html`<div
119+
id="open-part"
120+
tabindex=${this.disabled ? (nothing as any) : 0}
121+
@click=${this.handleOpenClick}
122+
@keydown=${this.handleOpenKeydown}>
123+
<span id="icon">
124+
<slot name="icon" @slotchange=${this._onSlotIconChange}></slot>
125+
${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''}
126+
</span>
127+
<span id="name"> ${this.name} </span>
128+
</div>`;
129+
}
130+
131+
#renderLink() {
132+
return html`<a
133+
id="open-part"
134+
tabindex=${this.disabled ? (nothing as any) : 0}
135+
href=${ifDefined(!this.disabled ? this.href : undefined)}
136+
target=${ifDefined(this.target || undefined)}
137+
rel=${ifDefined(
138+
this.target === '_blank' ? 'noopener noreferrer' : undefined
139+
)}>
140+
<span id="icon">
141+
<slot name="icon" @slotchange=${this._onSlotIconChange}></slot>
142+
${this._iconSlotHasContent === false ? this._renderFallbackIcon() : ''}
143+
</span>
144+
<span id="name"> ${this.name} </span>
145+
</a>`;
146+
}
147+
116148
public render() {
117149
return html`
118-
<div
119-
id="open-part"
120-
tabindex=${this.disabled ? (nothing as any) : 0}
121-
@click=${this.handleOpenClick}
122-
@keydown=${this.handleOpenKeydown}>
123-
<span id="icon">
124-
<slot name="icon" @slotchange=${this._onSlotIconChange}></slot>
125-
${this._iconSlotHasContent === false
126-
? this._renderFallbackIcon()
127-
: ''}
128-
</span>
129-
<span id="name"> ${this.name} </span>
130-
</div>
150+
${this.href ? this.#renderLink() : this.#renderButton()}
131151
<!-- Select border must be right after #open-part -->
132152
<div id="select-border"></div>
133153

packages/uui-card-content-node/lib/uui-card-content-node.story.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ export const AAAOverview: Story = props => html`
4646
?selectable=${props.selectable}
4747
?selected=${props.selected}
4848
?error=${props.error}
49-
?disabled=${props.disabled}>
49+
?disabled=${props.disabled}
50+
href=${props.href}
51+
target=${props.target}>
5052
<uui-tag size="s" slot="tag" color="positive">Published</uui-tag>
5153
${cardContent}
5254
</uui-card-content-node>

packages/uui-card-media/lib/uui-card-media.element.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
33
import { UUICardElement } from '@umbraco-ui/uui-card/lib';
44
import { css, html, nothing } from 'lit';
55
import { property, state } from 'lit/decorators.js';
6+
import { ifDefined } from 'lit/directives/if-defined.js';
67

78
import '@umbraco-ui/uui-symbol-folder/lib';
89
import '@umbraco-ui/uui-symbol-file/lib';
@@ -163,9 +164,8 @@ export class UUICardMediaElement extends UUICardElement {
163164
type="${this.fileExt}"></uui-symbol-file>`;
164165
}
165166

166-
public render() {
167-
return html` ${this.renderMedia()}
168-
<slot @slotchange=${this.queryPreviews}></slot>
167+
#renderButton() {
168+
return html`
169169
<button
170170
id="open-part"
171171
tabindex=${this.disabled ? (nothing as any) : '0'}
@@ -181,6 +181,36 @@ export class UUICardMediaElement extends UUICardElement {
181181
-->
182182
<span>${this.name}</span>
183183
</button>
184+
`;
185+
}
186+
187+
#renderLink() {
188+
return html`
189+
<a
190+
id="open-part"
191+
tabindex=${this.disabled ? (nothing as any) : '0'}
192+
href=${ifDefined(!this.disabled ? this.href : undefined)}
193+
target=${ifDefined(this.target || undefined)}
194+
rel=${ifDefined(
195+
this.target === '_blank' ? 'noopener noreferrer' : undefined
196+
)}>
197+
<!--
198+
TODO: Implement when pop-out is ready
199+
<uui-icon
200+
id="info-icon"
201+
name="info"
202+
style="color: currentColor">
203+
</uui-icon>
204+
-->
205+
<span>${this.name}</span>
206+
</a>
207+
`;
208+
}
209+
210+
public render() {
211+
return html` ${this.renderMedia()}
212+
<slot @slotchange=${this.queryPreviews}></slot>
213+
${this.href ? this.#renderLink() : this.#renderButton()}
184214
<!-- Select border must be right after .open-part -->
185215
<div id="select-border"></div>
186216

packages/uui-card-media/lib/uui-card-media.story.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export const AAAOverview: Story = props => html`
2626
?selectable=${props.selectable}
2727
?selected=${props.selected}
2828
?error=${props.error}
29-
?disabled=${props.disabled}></uui-card-media>
29+
?disabled=${props.disabled}
30+
href=${props.href}
31+
target=${props.target}></uui-card-media>
3032
`;
3133
AAAOverview.storyName = 'Overview';
3234

packages/uui-card-user/lib/uui-card-user.element.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { demandCustomElement } from '@umbraco-ui/uui-base/lib/utils';
33
import { UUICardElement } from '@umbraco-ui/uui-card/lib';
44
import { css, html, nothing } from 'lit';
55
import { property } from 'lit/decorators.js';
6+
import { ifDefined } from 'lit/directives/if-defined.js';
67

78
/**
89
* @element uui-card-user
@@ -105,16 +106,33 @@ export class UUICardUserElement extends UUICardElement {
105106
demandCustomElement(this, 'uui-avatar');
106107
}
107108

109+
#renderButton() {
110+
return html`<div
111+
id="open-part"
112+
tabindex=${this.disabled ? (nothing as any) : '0'}
113+
@click=${this.handleOpenClick}
114+
@keydown=${this.handleOpenKeydown}>
115+
<span> ${this.name} </span>
116+
</div>`;
117+
}
118+
119+
#renderLink() {
120+
return html`<a
121+
id="open-part"
122+
tabindex=${this.disabled ? (nothing as any) : '0'}
123+
href=${ifDefined(!this.disabled ? this.href : undefined)}
124+
target=${ifDefined(this.target || undefined)}
125+
rel=${ifDefined(
126+
this.target === '_blank' ? 'noopener noreferrer' : undefined
127+
)}>
128+
<span>${this.name}</span>
129+
</a>`;
130+
}
131+
108132
public render() {
109133
return html`
110134
<uui-avatar id="avatar" name=${this.name} size="m"></uui-avatar>
111-
<div
112-
id="open-part"
113-
tabindex=${this.disabled ? (nothing as any) : '0'}
114-
@click=${this.handleOpenClick}
115-
@keydown=${this.handleOpenKeydown}>
116-
<span> ${this.name} </span>
117-
</div>
135+
${this.href ? this.#renderLink() : this.#renderButton()}
118136
<slot></slot>
119137
<slot name="tag"></slot>
120138
<slot name="actions"></slot>

packages/uui-card-user/lib/uui-card-user.story.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ const Template: StoryFn = props => html`
3434
?select-only=${props.selectOnly}
3535
?selected=${props.selected}
3636
?error=${props.error}
37-
?disabled=${props.disabled}>
37+
?disabled=${props.disabled}
38+
href=${props.href}
39+
target=${props.target}>
3840
${cardContent}
3941
</uui-card-user>
4042
`;

packages/uui-card/lib/uui-card.element.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ export class UUICardElement extends SelectOnlyMixin(
9797
:host([select-only]) ::slotted(*) {
9898
pointer-events: none;
9999
}
100+
101+
a {
102+
text-decoration: none;
103+
color: inherit;
104+
}
100105
`,
101106
];
102107

@@ -119,12 +124,32 @@ export class UUICardElement extends SelectOnlyMixin(
119124
@property({ type: Boolean, reflect: true })
120125
error = false;
121126

127+
/**
128+
* Set an href, this will turns the name of the card into an anchor tag.
129+
* @type {string}
130+
* @attr
131+
* @default undefined
132+
*/
133+
@property({ type: String })
134+
public href?: string;
135+
136+
/**
137+
* Set an anchor tag target, only used when using href.
138+
* @type {string}
139+
* @attr
140+
* @default undefined
141+
*/
142+
@property({ type: String })
143+
public target?: '_blank' | '_parent' | '_self' | '_top';
144+
145+
// This is deprecated - use href instead
122146
protected handleOpenClick(e: Event) {
123147
if (this.disabled) return;
124148

125149
e.stopPropagation();
126150
this.dispatchEvent(new UUICardEvent(UUICardEvent.OPEN));
127151
}
152+
// This is deprecated - use href instead
128153
protected handleOpenKeydown(e: KeyboardEvent) {
129154
if (this.disabled) return;
130155
if (e.key !== 'Enter') return;

0 commit comments

Comments
 (0)