@justeattakeaway/pie-switch 2.3.34 → 2.4.1

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
@@ -19,6 +19,7 @@
19
19
  - [Events](#events)
20
20
  - [Forms Usage](#forms-usage)
21
21
  - [Form Validation](#form-validation)
22
+ - [External Labels](#external-labels)
22
23
  - [Usage Examples](#usage-examples)
23
24
  - [Questions and Support](#questions-and-support)
24
25
  - [Contributing](#contributing)
@@ -84,6 +85,39 @@ const switch = document.querySelector('pie-switch');
84
85
  switch.setCustomValidity('Please toggle the switch');
85
86
  ```
86
87
 
88
+ ## External Labels
89
+
90
+ In addition to the built-in `label` property, `pie-switch` can be associated with one or more external `<label>` elements: either by `for="…"` referencing the switch's `id`, or by wrapping the switch in a `<label>`. Clicking any associated label toggles the switch, mirroring native `<input type="checkbox">` behaviour. The labels will be narrated by screen readers.
91
+ Do not use the `label` property when associating the switch with external labels.
92
+
93
+ **Warning!** Be mindful of using the `aria` object property when associating with external labels as it could cause a messy screen reader narration. Always test your switches with screen readers.
94
+
95
+ **Using `for="…"`:**
96
+
97
+ ```html
98
+ <label for="notifications">Enable notifications</label>
99
+ <pie-switch id="notifications" name="notifications"></pie-switch>
100
+ ```
101
+
102
+ **Using a wrapping label:**
103
+
104
+ ```html
105
+ <label>
106
+ Enable notifications
107
+ <pie-switch name="notifications"></pie-switch>
108
+ </label>
109
+ ```
110
+
111
+ **Multiple labels for the same switch (not a very common pattern):**
112
+
113
+ ```html
114
+ <label for="notifications">Enable notifications</label>
115
+ <pie-switch id="notifications" name="notifications"></pie-switch>
116
+ <label for="notifications">(tap to toggle)</label>
117
+ ```
118
+
119
+ Each associated label will toggle the switch when clicked, and is announced by screen readers.
120
+
87
121
  ## Usage Examples
88
122
 
89
123
  **For HTML:**
@@ -151,6 +151,14 @@
151
151
  },
152
152
  "privacy": "public"
153
153
  },
154
+ {
155
+ "kind": "field",
156
+ "name": "_abortController",
157
+ "type": {
158
+ "text": "AbortController"
159
+ },
160
+ "privacy": "private"
161
+ },
154
162
  {
155
163
  "kind": "field",
156
164
  "name": "_isAnimationAllowed",
package/dist/index.d.ts CHANGED
@@ -39,8 +39,11 @@ export declare class PieSwitch extends PieSwitch_base implements SwitchProps, PI
39
39
  disabled: boolean;
40
40
  private input;
41
41
  focusTarget: HTMLElement;
42
+ private _abortController;
42
43
  private _isAnimationAllowed;
43
44
  protected firstUpdated(): void;
45
+ connectedCallback(): void;
46
+ disconnectedCallback(): void;
44
47
  protected updated(): void;
45
48
  static styles: CSSResult;
46
49
  /**
package/dist/index.js CHANGED
@@ -2,20 +2,20 @@ import { LitElement as St, nothing as L, html as N, unsafeCSS as Vt } from "lit"
2
2
  import { property as E, query as ut, state as Tt } from "lit/decorators.js";
3
3
  import { classMap as Lt } from "lit/directives/class-map.js";
4
4
  import { ifDefined as Nt } from "lit/directives/if-defined.js";
5
- import { FormControlMixin as Ot, DelegatesFocusMixin as Pt, wrapNativeEvent as _t, validPropertyValues as Dt, safeCustomElement as $t } from "@justeattakeaway/pie-webc-core";
5
+ import { FormControlMixin as Ot, DelegatesFocusMixin as _t, wrapNativeEvent as Pt, validPropertyValues as Dt, safeCustomElement as $t } from "@justeattakeaway/pie-webc-core";
6
6
  import "@justeattakeaway/pie-icons-webc/dist/IconCheck.js";
7
7
  const O = class O extends St {
8
8
  willUpdate() {
9
9
  this.getAttribute("v") || this.setAttribute("v", O.v);
10
10
  }
11
11
  };
12
- O.v = "2.3.34";
12
+ O.v = "2.4.1";
13
13
  let q = O;
14
14
  var dt = {}, ht;
15
15
  function Rt() {
16
16
  return ht || (ht = 1, (function() {
17
17
  (function(l) {
18
- const n = /* @__PURE__ */ new WeakMap(), h = /* @__PURE__ */ new WeakMap(), m = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new WeakMap(), g = /* @__PURE__ */ new WeakMap(), k = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap(), y = /* @__PURE__ */ new WeakMap(), B = /* @__PURE__ */ new WeakMap(), A = /* @__PURE__ */ new WeakMap(), U = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), K = /* @__PURE__ */ new WeakMap(), G = /* @__PURE__ */ new WeakMap(), C = /* @__PURE__ */ new WeakMap(), F = {
18
+ const n = /* @__PURE__ */ new WeakMap(), h = /* @__PURE__ */ new WeakMap(), m = /* @__PURE__ */ new WeakMap(), c = /* @__PURE__ */ new WeakMap(), g = /* @__PURE__ */ new WeakMap(), k = /* @__PURE__ */ new WeakMap(), _ = /* @__PURE__ */ new WeakMap(), y = /* @__PURE__ */ new WeakMap(), B = /* @__PURE__ */ new WeakMap(), A = /* @__PURE__ */ new WeakMap(), U = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), K = /* @__PURE__ */ new WeakMap(), G = /* @__PURE__ */ new WeakMap(), C = /* @__PURE__ */ new WeakMap(), F = {
19
19
  ariaAtomic: "aria-atomic",
20
20
  ariaAutoComplete: "aria-autocomplete",
21
21
  ariaBusy: "aria-busy",
@@ -67,8 +67,8 @@ function Rt() {
67
67
  get() {
68
68
  return a;
69
69
  },
70
- set(s) {
71
- a = s, e.isConnected ? e.setAttribute(r, s) : A.set(e, t);
70
+ set(o) {
71
+ a = o, e.isConnected ? e.setAttribute(r, o) : A.set(e, t);
72
72
  }
73
73
  });
74
74
  }
@@ -79,8 +79,8 @@ function Rt() {
79
79
  }
80
80
  const J = (e, t = !1) => {
81
81
  const i = document.createTreeWalker(e, NodeFilter.SHOW_ELEMENT, {
82
- acceptNode(s) {
83
- return c.has(s) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
82
+ acceptNode(o) {
83
+ return c.has(o) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
84
84
  }
85
85
  });
86
86
  let a = i.nextNode();
@@ -96,23 +96,23 @@ function Rt() {
96
96
  }
97
97
  }
98
98
  }) : {};
99
- function _(e) {
99
+ function P(e) {
100
100
  e.forEach((t) => {
101
- const { addedNodes: i, removedNodes: a } = t, r = Array.from(i), s = Array.from(a);
102
- r.forEach((o) => {
101
+ const { addedNodes: i, removedNodes: a } = t, r = Array.from(i), o = Array.from(a);
102
+ r.forEach((s) => {
103
103
  var u;
104
- if (c.has(o) && o.constructor.formAssociated && Y(o), A.has(o)) {
105
- const d = A.get(o);
104
+ if (c.has(s) && s.constructor.formAssociated && Y(s), A.has(s)) {
105
+ const d = A.get(s);
106
106
  Object.keys(F).filter((b) => d[b] !== null).forEach((b) => {
107
- o.setAttribute(F[b], d[b]);
108
- }), A.delete(o);
107
+ s.setAttribute(F[b], d[b]);
108
+ }), A.delete(s);
109
109
  }
110
- if (C.has(o)) {
111
- const d = C.get(o);
112
- o.setAttribute("internals-valid", d.validity.valid.toString()), o.setAttribute("internals-invalid", (!d.validity.valid).toString()), o.setAttribute("aria-invalid", (!d.validity.valid).toString()), C.delete(o);
110
+ if (C.has(s)) {
111
+ const d = C.get(s);
112
+ s.setAttribute("internals-valid", d.validity.valid.toString()), s.setAttribute("internals-invalid", (!d.validity.valid).toString()), s.setAttribute("aria-invalid", (!d.validity.valid).toString()), C.delete(s);
113
113
  }
114
- if (o.localName === "form") {
115
- const d = y.get(o), w = document.createTreeWalker(o, NodeFilter.SHOW_ELEMENT, {
114
+ if (s.localName === "form") {
115
+ const d = y.get(s), w = document.createTreeWalker(s, NodeFilter.SHOW_ELEMENT, {
116
116
  acceptNode(z) {
117
117
  return c.has(z) && z.constructor.formAssociated && !(d && d.has(z)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
118
118
  }
@@ -121,10 +121,10 @@ function Rt() {
121
121
  for (; b; )
122
122
  Y(b), b = w.nextNode();
123
123
  }
124
- o.localName === "fieldset" && ((u = I.observe) === null || u === void 0 || u.call(I, o, Q), J(o, !0));
125
- }), s.forEach((o) => {
126
- const u = c.get(o);
127
- u && m.get(u) && X(u), P.has(o) && P.get(o).disconnect();
124
+ s.localName === "fieldset" && ((u = I.observe) === null || u === void 0 || u.call(I, s, Q), J(s, !0));
125
+ }), o.forEach((s) => {
126
+ const u = c.get(s);
127
+ u && m.get(u) && X(u), _.has(s) && _.get(s).disconnect();
128
128
  });
129
129
  });
130
130
  }
@@ -142,7 +142,7 @@ function Rt() {
142
142
  const a = new MutationObserver(mt);
143
143
  !((t = window == null ? void 0 : window.ShadyDOM) === null || t === void 0) && t.inUse && e.mode && e.host && (e = e.host), (i = a.observe) === null || i === void 0 || i.call(a, e, { childList: !0 }), K.set(e, a);
144
144
  };
145
- T() && new MutationObserver(_);
145
+ T() && new MutationObserver(P);
146
146
  const D = {
147
147
  childList: !0,
148
148
  subtree: !0
@@ -165,7 +165,7 @@ function Rt() {
165
165
  t[0].id || (i = `${t[0].htmlFor}_Label`, t[0].id = i), e.setAttribute("aria-labelledby", i);
166
166
  }
167
167
  }, S = (e) => {
168
- const t = Array.from(e.elements).filter((s) => !s.tagName.includes("-") && s.validity).map((s) => s.validity.valid), i = y.get(e) || [], a = Array.from(i).filter((s) => s.isConnected).map((s) => c.get(s).validity.valid), r = [...t, ...a].includes(!1);
168
+ const t = Array.from(e.elements).filter((o) => !o.tagName.includes("-") && o.validity).map((o) => o.validity.valid), i = y.get(e) || [], a = Array.from(i).filter((o) => o.isConnected).map((o) => c.get(o).validity.valid), r = [...t, ...a].includes(!1);
169
169
  e.toggleAttribute("internals-invalid", r), e.toggleAttribute("internals-valid", !r);
170
170
  }, vt = (e) => {
171
171
  S(V(e.target));
@@ -262,7 +262,7 @@ function Rt() {
262
262
  }), i;
263
263
  }
264
264
  }
265
- function st(e, t, i, a) {
265
+ function ot(e, t, i, a) {
266
266
  if (typeof t == "function" ? e !== t || !a : !t.has(e)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
267
267
  return i === "m" ? a : i === "a" ? a.call(e) : a ? a.value : t.get(e);
268
268
  }
@@ -281,10 +281,10 @@ function Rt() {
281
281
  Object.freeze(this);
282
282
  }
283
283
  get length() {
284
- return st(this, x, "f").length;
284
+ return ot(this, x, "f").length;
285
285
  }
286
286
  [(x = /* @__PURE__ */ new WeakMap(), Symbol.iterator)]() {
287
- return st(this, x, "f")[Symbol.iterator]();
287
+ return ot(this, x, "f")[Symbol.iterator]();
288
288
  }
289
289
  item(t) {
290
290
  return this[t] == null ? null : this[t];
@@ -298,26 +298,26 @@ function Rt() {
298
298
  HTMLFormElement.prototype.checkValidity = i;
299
299
  const t = HTMLFormElement.prototype.reportValidity;
300
300
  HTMLFormElement.prototype.reportValidity = a;
301
- function i(...s) {
302
- let o = e.apply(this, s);
303
- return it(this, o, "checkValidity");
301
+ function i(...o) {
302
+ let s = e.apply(this, o);
303
+ return it(this, s, "checkValidity");
304
304
  }
305
- function a(...s) {
306
- let o = t.apply(this, s);
307
- return it(this, o, "reportValidity");
305
+ function a(...o) {
306
+ let s = t.apply(this, o);
307
+ return it(this, s, "reportValidity");
308
308
  }
309
309
  const { get: r } = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, "elements");
310
310
  Object.defineProperty(HTMLFormElement.prototype, "elements", {
311
- get(...s) {
312
- const o = r.call(this, ...s), u = Array.from(y.get(this) || []);
311
+ get(...o) {
312
+ const s = r.call(this, ...o), u = Array.from(y.get(this) || []);
313
313
  if (u.length === 0)
314
- return o;
315
- const d = Array.from(o).concat(u).sort((w, b) => w.compareDocumentPosition ? w.compareDocumentPosition(b) & 2 ? 1 : -1 : 0);
314
+ return s;
315
+ const d = Array.from(s).concat(u).sort((w, b) => w.compareDocumentPosition ? w.compareDocumentPosition(b) & 2 ? 1 : -1 : 0);
316
316
  return new Ct(d);
317
317
  }
318
318
  });
319
319
  }
320
- class ot {
320
+ class st {
321
321
  static get isPolyfilled() {
322
322
  return !0;
323
323
  }
@@ -372,8 +372,8 @@ function Rt() {
372
372
  }
373
373
  } else t != null && t instanceof FormData && Array.from(t).reverse().forEach(([a, r]) => {
374
374
  if (typeof r == "string") {
375
- const s = Z(i, this);
376
- s.name = a, s.value = r;
375
+ const o = Z(i, this);
376
+ o.name = a, o.value = r;
377
377
  }
378
378
  });
379
379
  B.set(i, t);
@@ -383,13 +383,13 @@ function Rt() {
383
383
  if (v(r, "Failed to execute 'setValidity' on 'ElementInternals': The target element is not a form-associated custom element."), !t)
384
384
  throw new TypeError("Failed to execute 'setValidity' on 'ElementInternals': 1 argument required, but only 0 present.");
385
385
  j.set(this, a);
386
- const s = h.get(this), o = {};
386
+ const o = h.get(this), s = {};
387
387
  for (const w in t)
388
- o[w] = t[w];
389
- Object.keys(o).length === 0 && kt(s);
390
- const u = Object.assign(Object.assign({}, s), o);
388
+ s[w] = t[w];
389
+ Object.keys(s).length === 0 && kt(o);
390
+ const u = Object.assign(Object.assign({}, o), s);
391
391
  delete u.valid;
392
- const { valid: d } = Mt(s, u, this.form);
392
+ const { valid: d } = Mt(o, u, this.form);
393
393
  if (!d && !i)
394
394
  throw new DOMException("Failed to execute 'setValidity' on 'ElementInternals': The second argument should not be empty if one or more flags in the first argument are true.");
395
395
  g.set(this, d ? "" : i), r.isConnected ? (r.toggleAttribute("internals-invalid", !d), r.toggleAttribute("internals-valid", d), r.setAttribute("aria-invalid", `${!d}`)) : C.set(r, this);
@@ -444,13 +444,13 @@ function Rt() {
444
444
  }
445
445
  function ct(e = !0) {
446
446
  if (!nt) {
447
- if (nt = !0, typeof window < "u" && (window.ElementInternals = ot), typeof CustomElementRegistry < "u") {
447
+ if (nt = !0, typeof window < "u" && (window.ElementInternals = st), typeof CustomElementRegistry < "u") {
448
448
  const t = CustomElementRegistry.prototype.define;
449
449
  CustomElementRegistry.prototype.define = function(i, a, r) {
450
450
  if (a.formAssociated) {
451
- const s = a.prototype.connectedCallback;
451
+ const o = a.prototype.connectedCallback;
452
452
  a.prototype.connectedCallback = function() {
453
- G.has(this) || (G.set(this, !0), this.hasAttribute("disabled") && $(this, !0)), s != null && s.apply(this), at(this);
453
+ G.has(this) || (G.set(this, !0), this.hasAttribute("disabled") && $(this, !0)), o != null && o.apply(this), at(this);
454
454
  };
455
455
  }
456
456
  t.call(this, i, a, r);
@@ -463,20 +463,20 @@ function Rt() {
463
463
  } else return {};
464
464
  if (c.has(this))
465
465
  throw new DOMException("DOMException: Failed to execute 'attachInternals' on 'HTMLElement': ElementInternals for the specified element was already attached.");
466
- return new ot(this);
466
+ return new st(this);
467
467
  }), typeof Element < "u") {
468
468
  let t = function(...a) {
469
469
  const r = i.apply(this, a);
470
470
  if (U.set(this, r), T()) {
471
- const s = new MutationObserver(_);
472
- window.ShadyDOM ? s.observe(this, D) : s.observe(r, D), P.set(this, s);
471
+ const o = new MutationObserver(P);
472
+ window.ShadyDOM ? o.observe(this, D) : o.observe(r, D), _.set(this, o);
473
473
  }
474
474
  return r;
475
475
  };
476
476
  const i = Element.prototype.attachShadow;
477
477
  Element.prototype.attachShadow = t;
478
478
  }
479
- T() && typeof document < "u" && new MutationObserver(_).observe(document.documentElement, D), typeof HTMLFormElement < "u" && Ft(), (e || typeof window < "u" && !window.CustomStateSet) && W();
479
+ T() && typeof document < "u" && new MutationObserver(P).observe(document.documentElement, D), typeof HTMLFormElement < "u" && Ft(), (e || typeof window < "u" && !window.CustomStateSet) && W();
480
480
  }
481
481
  }
482
482
  return !!customElements.polyfillWrapFlushCallback || (It() ? typeof window < "u" && !window.CustomStateSet && W(HTMLElement.prototype.attachInternals) : ct(!1)), l.forceCustomStateSetPolyfill = W, l.forceElementInternalsPolyfill = ct, Object.defineProperty(l, "__esModule", { value: !0 }), l;
@@ -497,7 +497,7 @@ var zt = Object.defineProperty, qt = Object.getOwnPropertyDescriptor, f = (l, n,
497
497
  return m && c && zt(n, h, c), c;
498
498
  };
499
499
  const Bt = "pie-switch";
500
- let p = class extends Ot(Pt(q)) {
500
+ let p = class extends Ot(_t(q)) {
501
501
  constructor() {
502
502
  super(...arguments), this.labelPlacement = M.labelPlacement, this.checked = M.checked, this.required = M.required, this.value = M.value, this.disabled = M.disabled, this._isAnimationAllowed = !1;
503
503
  }
@@ -506,6 +506,17 @@ let p = class extends Ot(Pt(q)) {
506
506
  this.dispatchEvent(new Event("invalid", l));
507
507
  });
508
508
  }
509
+ connectedCallback() {
510
+ super.connectedCallback(), this._abortController = new AbortController();
511
+ const { signal: l } = this._abortController;
512
+ this.addEventListener("click", (n) => {
513
+ n.composedPath()[0] === this && this.input.click();
514
+ }, { signal: l });
515
+ }
516
+ disconnectedCallback() {
517
+ var l;
518
+ super.disconnectedCallback(), (l = this._abortController) == null || l.abort();
519
+ }
509
520
  updated() {
510
521
  this.handleFormAssociation();
511
522
  }
@@ -522,7 +533,7 @@ let p = class extends Ot(Pt(q)) {
522
533
  handleChange(l) {
523
534
  const { checked: n } = l == null ? void 0 : l.currentTarget;
524
535
  this.checked = n;
525
- const h = _t(l);
536
+ const h = Pt(l);
526
537
  this._isAnimationAllowed || (this._isAnimationAllowed = !0), this.dispatchEvent(h), this.handleFormAssociation();
527
538
  }
528
539
  /**
package/dist/react.d.ts CHANGED
@@ -42,8 +42,11 @@ declare class PieSwitch_2 extends PieSwitch_base implements SwitchProps, PIEInpu
42
42
  disabled: boolean;
43
43
  private input;
44
44
  focusTarget: HTMLElement;
45
+ private _abortController;
45
46
  private _isAnimationAllowed;
46
47
  protected firstUpdated(): void;
48
+ connectedCallback(): void;
49
+ disconnectedCallback(): void;
47
50
  protected updated(): void;
48
51
  static styles: CSSResult;
49
52
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-switch",
3
3
  "description": "PIE Design System Switch built using Web Components",
4
- "version": "2.3.34",
4
+ "version": "2.4.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/justeattakeaway/pie",
@@ -45,11 +45,11 @@
45
45
  "devDependencies": {
46
46
  "@justeattakeaway/pie-components-config": "0.21.3",
47
47
  "@justeattakeaway/pie-css": "1.1.2",
48
- "@justeattakeaway/pie-monorepo-utils": "0.9.3",
48
+ "@justeattakeaway/pie-monorepo-utils": "0.9.4",
49
49
  "@justeattakeaway/pie-wrapper-react": "0.14.5"
50
50
  },
51
51
  "dependencies": {
52
- "@justeattakeaway/pie-icons-webc": "1.24.3",
52
+ "@justeattakeaway/pie-icons-webc": "1.25.0",
53
53
  "@justeattakeaway/pie-webc-core": "14.0.2",
54
54
  "element-internals-polyfill": "1.3.11"
55
55
  },
package/src/index.ts CHANGED
@@ -62,6 +62,8 @@ export class PieSwitch extends FormControlMixin(DelegatesFocusMixin(PieElement))
62
62
  @query('label')
63
63
  public focusTarget!: HTMLElement;
64
64
 
65
+ private _abortController!: AbortController;
66
+
65
67
  @state()
66
68
  private _isAnimationAllowed = false;
67
69
 
@@ -74,6 +76,27 @@ export class PieSwitch extends FormControlMixin(DelegatesFocusMixin(PieElement))
74
76
  });
75
77
  }
76
78
 
79
+ connectedCallback (): void {
80
+ super.connectedCallback();
81
+
82
+ this._abortController = new AbortController();
83
+ const { signal } = this._abortController;
84
+
85
+ this.addEventListener('click', (event: Event) => {
86
+ // Only programmatically click the input if the explicit target
87
+ // of the click was the host element itself (e.g., via an external label).
88
+ // This ignores clicks bubbling up from the internal shadow DOM and prevents loops.
89
+ if (event.composedPath()[0] === this) {
90
+ this.input.click();
91
+ }
92
+ }, { signal });
93
+ }
94
+
95
+ disconnectedCallback () : void {
96
+ super.disconnectedCallback();
97
+ this._abortController?.abort();
98
+ }
99
+
77
100
  protected updated (): void {
78
101
  this.handleFormAssociation();
79
102
  }