@justeattakeaway/pie-link 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -59,16 +59,19 @@ import { PieLink } from '@justeattakeaway/pie-link/dist/react';
59
59
 
60
60
  | Property | Type | Default | Description |
61
61
  | ------------- | ----------- | ------------- | ---------------------------------------------------------------------------------------------------- |
62
- | tag | `String` | `a` | The rendered HTML element of the link, one of `tags` – `a`, `button` |
62
+ | tag | `String` | `a` | The rendered HTML element of the link, one of `tags` – `a`, `button` |
63
63
  | variant | `String` | `default` | Variant of the link, one of `variants` – `default`, `high-visibility`, `inverse` |
64
64
  | size | `String` | `medium` | Size of the link, one of `sizes` – `medium`, `small` |
65
+ | underline | `String` | `default` | The underline behavior of the link, one of `underlineTypes` – `default`, `reversed` |
65
66
  | href | `String` | `undefined` | Native html `href` attribute |
66
67
  | rel | `String` | `undefined` | Native html `rel` attribute |
67
68
  | target | `String` | `undefined` | Native html `target` attribute |
68
69
  | type | `String` | `submit` | Native html `type` attribute if the tag is set to `button` |
69
70
  | isBold | `Boolean` | `false` | If `true`, sets the link text bold |
70
71
  | isStandalone | `Boolean` | `false` | If `true`, sets the link as a block element |
72
+ | hasVisited | `Boolean` | `false` | If `true`, the link will apply the styles for the visited state |
71
73
  | iconPlacement | `String` | `leading` | Icon placements of the icon slot, if provided, one of `iconPlacements` - `leading`, `trailing` |
74
+ | aria | `object` | `undefined` | The ARIA labels used for the link. |
72
75
 
73
76
  In your markup or JSX, you can then use these to set the properties for the `pie-link` component:
74
77
 
package/dist/index.d.ts CHANGED
@@ -1,12 +1,20 @@
1
1
  import type { CSSResult } from 'lit';
2
2
  import type { LitElement } from 'lit';
3
- import type { TemplateResult } from 'lit-html';
3
+ import type { TemplateResult } from 'lit';
4
+
5
+ export declare type AriaProps = {
6
+ label?: string;
7
+ };
4
8
 
5
9
  export declare const buttonTypes: readonly ["submit", "button", "reset", "menu"];
6
10
 
7
11
  export declare const iconPlacements: readonly ["leading", "trailing"];
8
12
 
9
13
  export declare interface LinkProps {
14
+ /**
15
+ * The ARIA labels used for the link.
16
+ */
17
+ aria?: AriaProps;
10
18
  /**
11
19
  * What HTML element the link should be such as a or button.
12
20
  */
@@ -19,6 +27,10 @@ export declare interface LinkProps {
19
27
  * What size the link should be.
20
28
  */
21
29
  size: typeof sizes[number];
30
+ /**
31
+ * What underline behavior the link should have such as default or reversed
32
+ */
33
+ underline: typeof underlineTypes[number];
22
34
  /**
23
35
  * The URL that the hyperlink should point to
24
36
  */
@@ -36,9 +48,13 @@ export declare interface LinkProps {
36
48
  */
37
49
  isBold: boolean;
38
50
  /**
39
- * When true, the link will be treated as a block box
51
+ * When true, the link will be treated as a block element
40
52
  */
41
53
  isStandalone: boolean;
54
+ /**
55
+ * When true, the link will apply the styles for the visited state',
56
+ */
57
+ hasVisited: boolean;
42
58
  /**
43
59
  * The placement of the icon slot, if provided, such as leading or trailing
44
60
  */
@@ -57,13 +73,34 @@ export declare class PieLink extends LitElement implements LinkProps {
57
73
  tag: LinkProps['tag'];
58
74
  variant: LinkProps['variant'];
59
75
  size: LinkProps['size'];
76
+ underline: LinkProps['underline'];
60
77
  iconPlacement: LinkProps['iconPlacement'];
61
78
  href?: string;
62
79
  target?: string;
63
80
  rel?: string;
64
81
  isBold: boolean;
65
82
  isStandalone: boolean;
83
+ hasVisited: boolean;
66
84
  type: LinkProps['type'];
85
+ aria: AriaProps;
86
+ /**
87
+ * Renders the link content.
88
+ *
89
+ * @private
90
+ */
91
+ private renderContent;
92
+ /**
93
+ * Renders the link as a button element.
94
+ *
95
+ * @private
96
+ */
97
+ private renderButton;
98
+ /**
99
+ * Renders the link as an anchor element.
100
+ *
101
+ * @private
102
+ */
103
+ private renderAnchor;
67
104
  render(): TemplateResult;
68
105
  static styles: CSSResult;
69
106
  }
@@ -72,6 +109,8 @@ export declare const sizes: readonly ["small", "medium"];
72
109
 
73
110
  export declare const tags: readonly ["a", "button"];
74
111
 
112
+ export declare const underlineTypes: readonly ["default", "reversed"];
113
+
75
114
  export declare const variants: readonly ["default", "high-visibility", "inverse"];
76
115
 
77
116
  export { }
package/dist/index.js CHANGED
@@ -1,110 +1,151 @@
1
- import { unsafeCSS as b, LitElement as $, nothing as p } from "lit";
2
- import { unsafeStatic as z, html as u } from "lit/static-html.js";
3
- import { property as l } from "lit/decorators.js";
4
- import "lit/decorators/property.js";
5
- const g = (f, n, a) => function(t, i) {
6
- const o = `#${i}`;
7
- Object.defineProperty(t, i, {
1
+ import { unsafeCSS as g, LitElement as k, html as u, nothing as s } from "lit";
2
+ import { property as i } from "lit/decorators.js";
3
+ const d = (h, t, c) => function(o, r) {
4
+ const l = `#${r}`;
5
+ Object.defineProperty(o, r, {
8
6
  get() {
9
- return this[o];
7
+ return this[l];
10
8
  },
11
- set(c) {
12
- const v = this[o];
13
- n.includes(c) ? this[o] = c : (console.error(
14
- `<${f}> Invalid value "${c}" provided for property "${i}".`,
15
- `Must be one of: ${n.join(" | ")}.`,
16
- `Falling back to default value: "${a}"`
17
- ), this[o] = a), this.requestUpdate(i, v);
9
+ set(v) {
10
+ const f = this[l];
11
+ t.includes(v) ? this[l] = v : (console.error(
12
+ `<${h}> Invalid value "${v}" provided for property "${r}".`,
13
+ `Must be one of: ${t.join(" | ")}.`,
14
+ `Falling back to default value: "${c}"`
15
+ ), this[l] = c), this.requestUpdate(r, f);
18
16
  }
19
17
  });
20
- }, x = `.c-link{--link-font-family: var(--dt-font-interactive-m-family);--link-font-size: calc(var(--dt-font-size-16) * 1px);--link-line-height: calc(var(--dt-font-size-16-line-height) * 1px);--link-font-weight: var(--dt-font-weight-regular);--link-text-color: var(--dt-color-content-link);--link-text-decoration: var(--dt-font-style-underline);--link-icon-size: 16px;display:inline-block;font-family:var(--link-font-family);font-size:var(--link-font-size);line-height:var(--link-line-height);font-weight:var(--link-font-weight);color:var(--link-text-color);text-decoration:var(--link-text-decoration);cursor:pointer}.c-link[tag=button]{outline:none;border:none;user-select:none;background:transparent;padding:0}.c-link[variant=high-visibility]{--link-text-color: var(--dt-color-content-link-distinct)}.c-link[variant=inverse]{--link-text-color: var(--dt-color-content-link-inverse)}.c-link[size=small]{--link-font-size: calc(var(--dt-font-size-14) * 1px);--link-line-height: calc(var(--dt-font-size-14-line-height) * 1px)}.c-link[isBold]{--link-font-weight: var(--dt-font-weight-bold)}.c-link[isStandalone]{display:block}.c-link-content{display:flex;gap:var(--dt-spacing-a)}::slotted(.c-pieIcon),::slotted(svg){display:inline-flex;margin-block-start:var(--dt-spacing-a);height:var(--link-icon-size);width:var(--link-icon-size)}
21
- `, S = ["default", "high-visibility", "inverse"], P = ["small", "medium"], w = ["leading", "trailing"], B = ["a", "button"], _ = ["submit", "button", "reset", "menu"];
22
- var O = Object.defineProperty, j = Object.getOwnPropertyDescriptor, r = (f, n, a, s) => {
23
- for (var t = s > 1 ? void 0 : s ? j(n, a) : n, i = f.length - 1, o; i >= 0; i--)
24
- (o = f[i]) && (t = (s ? o(n, a, t) : o(t)) || t);
25
- return s && t && O(n, a, t), t;
18
+ }, y = `.c-link{--link-font-family: var(--dt-font-interactive-m-family);--link-font-size: calc(var(--dt-font-size-16) * 1px);--link-line-height: calc(var(--dt-font-size-16-line-height) * 1px);--link-font-weight: var(--dt-font-weight-regular);--link-text-color: var(--dt-color-content-link);--link-text-decoration: var(--dt-font-style-underline);--link-icon-size: 16px;display:inline-block;font-family:var(--link-font-family);font-size:var(--link-font-size);line-height:var(--link-line-height);font-weight:var(--link-font-weight);color:var(--link-text-color);text-decoration:var(--link-text-decoration);cursor:pointer}.c-link[tag=button]{outline:none;border:none;user-select:none;background:transparent;padding:0}.c-link[variant=high-visibility]{--link-text-color: var(--dt-color-content-link-distinct)}.c-link[variant=inverse]{--link-text-color: var(--dt-color-content-link-inverse)}.c-link[size=small]{--link-font-size: calc(var(--dt-font-size-14) * 1px);--link-line-height: calc(var(--dt-font-size-14-line-height) * 1px)}.c-link[underline=default]:hover,.c-link[underline=default]:active,.c-link[underline=reversed]{--link-text-decoration: none}.c-link[underline=reversed]:hover,.c-link[underline=reversed]:active{--link-text-decoration: var(--dt-font-style-underline)}.c-link[isBold]{--link-font-weight: var(--dt-font-weight-bold)}.c-link[isStandalone]{display:block}.c-link[hasVisited]:visited{color:var(--dt-color-content-link-visited)}.c-link[hasVisited]:visited[variant=inverse]{color:var(--dt-color-content-link-visited-inverse)}.c-link:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-outer)}.c-link-content{display:flex;gap:var(--dt-spacing-a)}::slotted(.c-pieIcon),::slotted(svg){display:inline-flex;margin-block-start:var(--dt-spacing-a);height:var(--link-icon-size);width:var(--link-icon-size)}
19
+ `, m = ["default", "high-visibility", "inverse"], b = ["small", "medium"], $ = ["leading", "trailing"], x = ["a", "button"], z = ["submit", "button", "reset", "menu"], S = ["default", "reversed"];
20
+ var B = Object.defineProperty, P = Object.getOwnPropertyDescriptor, n = (h, t, c, p) => {
21
+ for (var o = p > 1 ? void 0 : p ? P(t, c) : t, r = h.length - 1, l; r >= 0; r--)
22
+ (l = h[r]) && (o = (p ? l(t, c, o) : l(o)) || o);
23
+ return p && o && B(t, c, o), o;
26
24
  };
27
- const d = "pie-link";
28
- class e extends $ {
25
+ const a = "pie-link";
26
+ class e extends k {
29
27
  constructor() {
30
- super(...arguments), this.tag = "a", this.variant = "default", this.size = "medium", this.iconPlacement = "leading", this.isBold = !1, this.isStandalone = !1, this.type = "submit";
28
+ super(...arguments), this.tag = "a", this.variant = "default", this.size = "medium", this.underline = "default", this.iconPlacement = "leading", this.isBold = !1, this.isStandalone = !1, this.hasVisited = !1, this.type = "submit";
31
29
  }
32
- render() {
33
- const {
34
- tag: n,
35
- variant: a,
36
- size: s,
37
- iconPlacement: t,
38
- isBold: i,
39
- isStandalone: o,
40
- href: c,
41
- target: v,
42
- rel: m,
43
- type: y
44
- } = this, k = z(n), h = n === "button";
30
+ /**
31
+ * Renders the link content.
32
+ *
33
+ * @private
34
+ */
35
+ renderContent() {
36
+ const { iconPlacement: t } = this;
37
+ return u`
38
+ <span class="c-link-content">
39
+ ${t === "leading" ? u`<slot name="icon"></slot>` : s}
40
+ <slot></slot>
41
+ ${t === "trailing" ? u`<slot name="icon"></slot>` : s}
42
+ </span>`;
43
+ }
44
+ /**
45
+ * Renders the link as a button element.
46
+ *
47
+ * @private
48
+ */
49
+ renderButton() {
50
+ var t;
45
51
  return u`
46
- <${k}
52
+ <button
47
53
  data-test-id="pie-link"
48
54
  class="c-link"
49
- tag="${n}"
50
- variant=${a}
51
- size=${s}
52
- ?isBold=${i}
53
- ?isStandalone=${o}
54
- href=${!h && c ? c : p}
55
- target=${!h && v ? v : p}
56
- rel=${!h && m ? m : p}
57
- type=${h && y ? y : p}>
58
- <span class="c-link-content">
59
- ${t === "leading" ? u`<slot name="icon"></slot>` : p}
60
- <slot></slot>
61
- ${t === "trailing" ? u`<slot name="icon"></slot>` : p}
62
- </span>
63
- </${k}>`;
55
+ tag=${this.tag}
56
+ variant=${this.variant}
57
+ size=${this.size}
58
+ underline=${this.underline}
59
+ ?isBold=${this.isBold}
60
+ ?isStandalone=${this.isStandalone}
61
+ ?hasVisited=${this.hasVisited}
62
+ type=${this.type || s}
63
+ aria-label=${((t = this.aria) == null ? void 0 : t.label) || s}>
64
+ ${this.renderContent()}
65
+ </button>`;
66
+ }
67
+ /**
68
+ * Renders the link as an anchor element.
69
+ *
70
+ * @private
71
+ */
72
+ renderAnchor() {
73
+ var t;
74
+ return u`
75
+ <a
76
+ data-test-id="pie-link"
77
+ class="c-link"
78
+ tag=${this.tag}
79
+ variant=${this.variant}
80
+ size=${this.size}
81
+ underline=${this.underline}
82
+ ?isBold=${this.isBold}
83
+ ?isStandalone=${this.isStandalone}
84
+ ?hasVisited=${this.hasVisited}
85
+ href=${this.href || s}
86
+ target=${this.target || s}
87
+ rel=${this.rel || s}
88
+ aria-label=${((t = this.aria) == null ? void 0 : t.label) || s}>
89
+ ${this.renderContent()}
90
+ </a>`;
91
+ }
92
+ render() {
93
+ return this.tag === "button" ? this.renderButton() : this.renderAnchor();
64
94
  }
65
95
  }
66
- e.styles = b(x);
67
- r([
68
- l(),
69
- g(d, B, "a")
96
+ e.styles = g(y);
97
+ n([
98
+ i(),
99
+ d(a, x, "a")
70
100
  ], e.prototype, "tag", 2);
71
- r([
72
- l({ type: String }),
73
- g(d, S, "default")
101
+ n([
102
+ i({ type: String }),
103
+ d(a, m, "default")
74
104
  ], e.prototype, "variant", 2);
75
- r([
76
- l({ type: String }),
77
- g(d, P, "medium")
105
+ n([
106
+ i({ type: String }),
107
+ d(a, b, "medium")
78
108
  ], e.prototype, "size", 2);
79
- r([
80
- l({ type: String }),
81
- g(d, w, "leading")
109
+ n([
110
+ i({ type: String }),
111
+ d(a, S, "default")
112
+ ], e.prototype, "underline", 2);
113
+ n([
114
+ i({ type: String }),
115
+ d(a, $, "leading")
82
116
  ], e.prototype, "iconPlacement", 2);
83
- r([
84
- l({ type: String, reflect: !0 })
117
+ n([
118
+ i({ type: String, reflect: !0 })
85
119
  ], e.prototype, "href", 2);
86
- r([
87
- l({ type: String, reflect: !0 })
120
+ n([
121
+ i({ type: String, reflect: !0 })
88
122
  ], e.prototype, "target", 2);
89
- r([
90
- l({ type: String, reflect: !0 })
123
+ n([
124
+ i({ type: String, reflect: !0 })
91
125
  ], e.prototype, "rel", 2);
92
- r([
93
- l({ type: Boolean })
126
+ n([
127
+ i({ type: Boolean })
94
128
  ], e.prototype, "isBold", 2);
95
- r([
96
- l({ type: Boolean })
129
+ n([
130
+ i({ type: Boolean })
97
131
  ], e.prototype, "isStandalone", 2);
98
- r([
99
- l(),
100
- g(d, _, "submit")
132
+ n([
133
+ i({ type: Boolean })
134
+ ], e.prototype, "hasVisited", 2);
135
+ n([
136
+ i(),
137
+ d(a, z, "submit")
101
138
  ], e.prototype, "type", 2);
102
- customElements.define(d, e);
139
+ n([
140
+ i({ type: Object })
141
+ ], e.prototype, "aria", 2);
142
+ customElements.define(a, e);
103
143
  export {
104
144
  e as PieLink,
105
- _ as buttonTypes,
106
- w as iconPlacements,
107
- P as sizes,
108
- B as tags,
109
- S as variants
145
+ z as buttonTypes,
146
+ $ as iconPlacements,
147
+ b as sizes,
148
+ x as tags,
149
+ S as underlineTypes,
150
+ m as variants
110
151
  };
package/dist/react.d.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  import type { CSSResult } from 'lit';
2
2
  import type { LitElement } from 'lit';
3
3
  import type { ReactWebComponent } from '@lit-labs/react';
4
- import type { TemplateResult } from 'lit-html';
4
+ import type { TemplateResult } from 'lit';
5
+
6
+ export declare type AriaProps = {
7
+ label?: string;
8
+ };
5
9
 
6
10
  export declare const buttonTypes: readonly ["submit", "button", "reset", "menu"];
7
11
 
8
12
  export declare const iconPlacements: readonly ["leading", "trailing"];
9
13
 
10
14
  export declare interface LinkProps {
15
+ /**
16
+ * The ARIA labels used for the link.
17
+ */
18
+ aria?: AriaProps;
11
19
  /**
12
20
  * What HTML element the link should be such as a or button.
13
21
  */
@@ -20,6 +28,10 @@ export declare interface LinkProps {
20
28
  * What size the link should be.
21
29
  */
22
30
  size: typeof sizes[number];
31
+ /**
32
+ * What underline behavior the link should have such as default or reversed
33
+ */
34
+ underline: typeof underlineTypes[number];
23
35
  /**
24
36
  * The URL that the hyperlink should point to
25
37
  */
@@ -37,9 +49,13 @@ export declare interface LinkProps {
37
49
  */
38
50
  isBold: boolean;
39
51
  /**
40
- * When true, the link will be treated as a block box
52
+ * When true, the link will be treated as a block element
41
53
  */
42
54
  isStandalone: boolean;
55
+ /**
56
+ * When true, the link will apply the styles for the visited state',
57
+ */
58
+ hasVisited: boolean;
43
59
  /**
44
60
  * The placement of the icon slot, if provided, such as leading or trailing
45
61
  */
@@ -60,13 +76,34 @@ declare class PieLink_2 extends LitElement implements LinkProps {
60
76
  tag: LinkProps['tag'];
61
77
  variant: LinkProps['variant'];
62
78
  size: LinkProps['size'];
79
+ underline: LinkProps['underline'];
63
80
  iconPlacement: LinkProps['iconPlacement'];
64
81
  href?: string;
65
82
  target?: string;
66
83
  rel?: string;
67
84
  isBold: boolean;
68
85
  isStandalone: boolean;
86
+ hasVisited: boolean;
69
87
  type: LinkProps['type'];
88
+ aria: AriaProps;
89
+ /**
90
+ * Renders the link content.
91
+ *
92
+ * @private
93
+ */
94
+ private renderContent;
95
+ /**
96
+ * Renders the link as a button element.
97
+ *
98
+ * @private
99
+ */
100
+ private renderButton;
101
+ /**
102
+ * Renders the link as an anchor element.
103
+ *
104
+ * @private
105
+ */
106
+ private renderAnchor;
70
107
  render(): TemplateResult;
71
108
  static styles: CSSResult;
72
109
  }
@@ -75,6 +112,8 @@ export declare const sizes: readonly ["small", "medium"];
75
112
 
76
113
  export declare const tags: readonly ["a", "button"];
77
114
 
115
+ export declare const underlineTypes: readonly ["default", "reversed"];
116
+
78
117
  export declare const variants: readonly ["default", "high-visibility", "inverse"];
79
118
 
80
119
  export { }
package/dist/react.js CHANGED
@@ -1,32 +1,30 @@
1
1
  import * as L from "react";
2
2
  import { PieLink as g } from "./index.js";
3
- import { buttonTypes as O, iconPlacements as S, sizes as T, tags as _, variants as z } from "./index.js";
3
+ import { buttonTypes as j, iconPlacements as D, sizes as G, tags as O, underlineTypes as S, variants as _ } from "./index.js";
4
4
  import "lit";
5
- import "lit/static-html.js";
6
5
  import "lit/decorators.js";
7
- import "lit/decorators/property.js";
8
6
  /**
9
7
  * @license
10
8
  * Copyright 2018 Google LLC
11
9
  * SPDX-License-Identifier: BSD-3-Clause
12
10
  */
13
- const E = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), w = /* @__PURE__ */ new WeakMap(), P = (m, l, p, d, h) => {
11
+ const E = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), w = /* @__PURE__ */ new WeakMap(), P = (d, l, p, m, h) => {
14
12
  const n = h == null ? void 0 : h[l];
15
- n === void 0 || p === d ? p == null && l in HTMLElement.prototype ? m.removeAttribute(l) : m[l] = p : ((s, t, u) => {
13
+ n === void 0 || p === m ? p == null && l in HTMLElement.prototype ? d.removeAttribute(l) : d[l] = p : ((s, t, u) => {
16
14
  let o = w.get(s);
17
15
  o === void 0 && w.set(s, o = /* @__PURE__ */ new Map());
18
16
  let a = o.get(t);
19
17
  u !== void 0 ? a === void 0 ? (o.set(t, a = { handleEvent: u }), s.addEventListener(t, a)) : a.handleEvent = u : a !== void 0 && (o.delete(t), s.removeEventListener(t, a));
20
- })(m, n, p);
18
+ })(d, n, p);
21
19
  };
22
- function b(m = window.React, l, p, d, h) {
20
+ function b(d = window.React, l, p, m, h) {
23
21
  let n, s, t;
24
22
  if (l === void 0) {
25
- const r = m;
26
- ({ tagName: s, elementClass: t, events: d, displayName: h } = r), n = r.react;
23
+ const r = d;
24
+ ({ tagName: s, elementClass: t, events: m, displayName: h } = r), n = r.react;
27
25
  } else
28
- n = m, t = p, s = l;
29
- const u = n.Component, o = n.createElement, a = new Set(Object.keys(d ?? {}));
26
+ n = d, t = p, s = l;
27
+ const u = n.Component, o = n.createElement, a = new Set(Object.keys(m ?? {}));
30
28
  class f extends u {
31
29
  constructor() {
32
30
  super(...arguments), this.o = null;
@@ -34,7 +32,7 @@ function b(m = window.React, l, p, d, h) {
34
32
  t(e) {
35
33
  if (this.o !== null)
36
34
  for (const v in this.i)
37
- P(this.o, v, this.props[v], e ? e[v] : void 0, d);
35
+ P(this.o, v, this.props[v], e ? e[v] : void 0, m);
38
36
  }
39
37
  componentDidMount() {
40
38
  this.t();
@@ -59,7 +57,7 @@ function b(m = window.React, l, p, d, h) {
59
57
  const N = n.forwardRef((r, e) => o(f, { ...r, _$Gl: e }, r == null ? void 0 : r.children));
60
58
  return N.displayName = f.displayName, N;
61
59
  }
62
- const j = b({
60
+ const R = b({
63
61
  displayName: "PieLink",
64
62
  elementClass: g,
65
63
  react: L,
@@ -67,10 +65,11 @@ const j = b({
67
65
  events: {}
68
66
  });
69
67
  export {
70
- j as PieLink,
71
- O as buttonTypes,
72
- S as iconPlacements,
73
- T as sizes,
74
- _ as tags,
75
- z as variants
68
+ R as PieLink,
69
+ j as buttonTypes,
70
+ D as iconPlacements,
71
+ G as sizes,
72
+ O as tags,
73
+ S as underlineTypes,
74
+ _ as variants
76
75
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-link",
3
3
  "description": "PIE Design System Link built using Web Components",
4
- "version": "0.3.0",
4
+ "version": "0.5.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
package/src/defs.ts CHANGED
@@ -3,9 +3,18 @@ export const sizes = ['small', 'medium'] as const;
3
3
  export const iconPlacements = ['leading', 'trailing'] as const;
4
4
  export const tags = ['a', 'button'] as const;
5
5
  export const buttonTypes = ['submit', 'button', 'reset', 'menu'] as const;
6
+ export const underlineTypes = ['default', 'reversed'] as const;
7
+
8
+ export type AriaProps = {
9
+ label?: string;
10
+ };
6
11
 
7
12
  export interface LinkProps {
8
- /**
13
+ /**
14
+ * The ARIA labels used for the link.
15
+ */
16
+ aria?: AriaProps;
17
+ /**
9
18
  * What HTML element the link should be such as a or button.
10
19
  */
11
20
  tag: typeof tags[number];
@@ -17,6 +26,10 @@ export interface LinkProps {
17
26
  * What size the link should be.
18
27
  */
19
28
  size: typeof sizes[number];
29
+ /**
30
+ * What underline behavior the link should have such as default or reversed
31
+ */
32
+ underline: typeof underlineTypes[number];
20
33
  /**
21
34
  * The URL that the hyperlink should point to
22
35
  */
@@ -34,9 +47,13 @@ export interface LinkProps {
34
47
  */
35
48
  isBold: boolean;
36
49
  /**
37
- * When true, the link will be treated as a block box
50
+ * When true, the link will be treated as a block element
38
51
  */
39
52
  isStandalone: boolean;
53
+ /**
54
+ * When true, the link will apply the styles for the visited state',
55
+ */
56
+ hasVisited: boolean;
40
57
  /**
41
58
  * The placement of the icon slot, if provided, such as leading or trailing
42
59
  */
package/src/index.ts CHANGED
@@ -1,10 +1,18 @@
1
- import { LitElement, unsafeCSS, nothing } from 'lit';
2
- import { html, unsafeStatic } from 'lit/static-html.js';
1
+ import {
2
+ html, LitElement, unsafeCSS, nothing, TemplateResult,
3
+ } from 'lit';
3
4
  import { property } from 'lit/decorators.js';
4
5
  import { validPropertyValues } from '@justeattakeaway/pie-webc-core';
5
6
  import styles from './link.scss?inline';
6
7
  import {
7
- LinkProps, variants, sizes, iconPlacements, tags, buttonTypes,
8
+ LinkProps,
9
+ variants,
10
+ sizes,
11
+ iconPlacements,
12
+ tags,
13
+ buttonTypes,
14
+ underlineTypes,
15
+ type AriaProps,
8
16
  } from './defs';
9
17
 
10
18
  // Valid values available to consumers
@@ -30,6 +38,10 @@ export class PieLink extends LitElement implements LinkProps {
30
38
  @validPropertyValues(componentSelector, sizes, 'medium')
31
39
  public size: LinkProps['size'] = 'medium';
32
40
 
41
+ @property({ type: String })
42
+ @validPropertyValues(componentSelector, underlineTypes, 'default')
43
+ public underline: LinkProps['underline'] = 'default';
44
+
33
45
  @property({ type: String })
34
46
  @validPropertyValues(componentSelector, iconPlacements, 'leading')
35
47
  public iconPlacement: LinkProps['iconPlacement'] = 'leading';
@@ -49,46 +61,83 @@ export class PieLink extends LitElement implements LinkProps {
49
61
  @property({ type: Boolean })
50
62
  public isStandalone = false;
51
63
 
64
+ @property({ type: Boolean })
65
+ public hasVisited = false;
66
+
52
67
  @property()
53
68
  @validPropertyValues(componentSelector, buttonTypes, 'submit')
54
69
  public type: LinkProps['type'] = 'submit';
55
70
 
56
- render () {
57
- const {
58
- tag,
59
- variant,
60
- size,
61
- iconPlacement,
62
- isBold,
63
- isStandalone,
64
- href,
65
- target,
66
- rel,
67
- type,
68
- } = this;
69
-
70
- const element = unsafeStatic(tag);
71
- const isButton = tag === 'button';
71
+ @property({ type: Object })
72
+ public aria!: AriaProps;
73
+
74
+ /**
75
+ * Renders the link content.
76
+ *
77
+ * @private
78
+ */
79
+ private renderContent (): TemplateResult {
80
+ const { iconPlacement } = this;
81
+ return html`
82
+ <span class="c-link-content">
83
+ ${iconPlacement === 'leading' ? html`<slot name="icon"></slot>` : nothing}
84
+ <slot></slot>
85
+ ${iconPlacement === 'trailing' ? html`<slot name="icon"></slot>` : nothing}
86
+ </span>`;
87
+ }
72
88
 
89
+ /**
90
+ * Renders the link as a button element.
91
+ *
92
+ * @private
93
+ */
94
+ private renderButton (): TemplateResult {
73
95
  return html`
74
- <${element}
96
+ <button
75
97
  data-test-id="pie-link"
76
98
  class="c-link"
77
- tag="${tag}"
78
- variant=${variant}
79
- size=${size}
80
- ?isBold=${isBold}
81
- ?isStandalone=${isStandalone}
82
- href=${!isButton && href ? href : nothing}
83
- target=${!isButton && target ? target : nothing}
84
- rel=${!isButton && rel ? rel : nothing}
85
- type=${isButton && type ? type : nothing}>
86
- <span class="c-link-content">
87
- ${iconPlacement === 'leading' ? html`<slot name="icon"></slot>` : nothing}
88
- <slot></slot>
89
- ${iconPlacement === 'trailing' ? html`<slot name="icon"></slot>` : nothing}
90
- </span>
91
- </${element}>`;
99
+ tag=${this.tag}
100
+ variant=${this.variant}
101
+ size=${this.size}
102
+ underline=${this.underline}
103
+ ?isBold=${this.isBold}
104
+ ?isStandalone=${this.isStandalone}
105
+ ?hasVisited=${this.hasVisited}
106
+ type=${this.type || nothing}
107
+ aria-label=${this.aria?.label || nothing}>
108
+ ${this.renderContent()}
109
+ </button>`;
110
+ }
111
+
112
+ /**
113
+ * Renders the link as an anchor element.
114
+ *
115
+ * @private
116
+ */
117
+ private renderAnchor (): TemplateResult {
118
+ return html`
119
+ <a
120
+ data-test-id="pie-link"
121
+ class="c-link"
122
+ tag=${this.tag}
123
+ variant=${this.variant}
124
+ size=${this.size}
125
+ underline=${this.underline}
126
+ ?isBold=${this.isBold}
127
+ ?isStandalone=${this.isStandalone}
128
+ ?hasVisited=${this.hasVisited}
129
+ href=${this.href || nothing}
130
+ target=${this.target || nothing}
131
+ rel=${this.rel || nothing}
132
+ aria-label=${this.aria?.label || nothing}>
133
+ ${this.renderContent()}
134
+ </a>`;
135
+ }
136
+
137
+ render () {
138
+ const isButton = this.tag === 'button';
139
+
140
+ return isButton ? this.renderButton() : this.renderAnchor();
92
141
  }
93
142
 
94
143
  // Renders a `CSSResult` generated from SCSS by Vite
package/src/link.scss CHANGED
@@ -1,5 +1,17 @@
1
1
  @use '@justeattakeaway/pie-css/scss' as p;
2
2
 
3
+
4
+ @mixin link-interactive-states($mode: 'default') {
5
+ &:hover,
6
+ &:active {
7
+ @if $mode == 'reversed' {
8
+ --link-text-decoration: var(--dt-font-style-underline);
9
+ } @else {
10
+ --link-text-decoration: none;
11
+ }
12
+ }
13
+ }
14
+
3
15
  .c-link {
4
16
  --link-font-family: var(--dt-font-interactive-m-family);
5
17
  --link-font-size: #{p.font-size(--dt-font-size-16)};
@@ -51,6 +63,18 @@
51
63
  /* Same as default styles */
52
64
  }
53
65
 
66
+ &[underline='default'] {
67
+ /* Same as default styles */
68
+
69
+ @include link-interactive-states('default');
70
+ }
71
+
72
+ &[underline='reversed'] {
73
+ --link-text-decoration: none;
74
+
75
+ @include link-interactive-states('reversed');
76
+ }
77
+
54
78
  &[isBold] {
55
79
  --link-font-weight: var(--dt-font-weight-bold);
56
80
  }
@@ -58,6 +82,18 @@
58
82
  &[isStandalone] {
59
83
  display: block;
60
84
  }
85
+
86
+ &[hasVisited]:visited {
87
+ color: var(--dt-color-content-link-visited);
88
+
89
+ &[variant='inverse'] {
90
+ color: var(--dt-color-content-link-visited-inverse);
91
+ }
92
+ }
93
+
94
+ &:focus-visible {
95
+ box-shadow: 0 0 0 2px var(--dt-color-focus-outer);
96
+ }
61
97
  }
62
98
 
63
99
  .c-link-content {