@justeattakeaway/pie-button 0.31.0 → 0.33.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/dist/react.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare interface ButtonProps {
9
9
  */
10
10
  size: typeof sizes[number];
11
11
  /**
12
- * What type attribute should be applied to the button. For example submit, button or menu.
12
+ * What type attribute should be applied to the button. For example submit, button.
13
13
  */
14
14
  type: typeof types[number];
15
15
  /**
@@ -32,17 +32,66 @@ export declare interface ButtonProps {
32
32
  * When true, displays a loading indicator inside the button.
33
33
  */
34
34
  isLoading: boolean;
35
+ /**
36
+ * The name of the button, submitted as a pair with the button's value as part of the form data, when that button is used to submit the form.
37
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
38
+ */
39
+ name?: string;
40
+ /**
41
+ * Defines the value associated with the button's name when it's submitted with the form data. This value is passed to the server in params when the form is submitted using this button.
42
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
43
+ */
44
+ value?: string;
45
+ /**
46
+ * The URL that processes the information submitted by the button. Overrides the action attribute of the button's form owner. Does nothing if there is no form owner.
47
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
48
+ */
49
+ formaction?: string;
50
+ /**
51
+ * If the button is a submit button (it's inside/associated with a <form> and doesn't have type="button"), specifies how to encode the form data that is submitted.
52
+ * If this attribute is specified, it overrides the enctype attribute of the button's form owner.
53
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
54
+ */
55
+ formenctype?: typeof formEncodingtypes[number];
56
+ /**
57
+ * If the button is a submit button (it's inside/associated with a <form> and doesn't have type="button"), this attribute specifies the HTTP method used to submit the form.
58
+ * If specified, this attribute overrides the method attribute of the button's form owner.
59
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
60
+ */
61
+ formmethod?: typeof formMethodTypes[number];
62
+ /**
63
+ * If the button is a submit button, this Boolean attribute specifies that the form is not to be validated when it is submitted.
64
+ * If this attribute is specified, it overrides the novalidate attribute of the button's form owner.
65
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
66
+ */
67
+ formnovalidate?: boolean;
68
+ /**
69
+ * If the button is a submit button, this attribute is an author-defined name or standardized, underscore-prefixed keyword indicating where to display the response from submitting the form.
70
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
71
+ */
72
+ formtarget?: typeof formTargetTypes[number];
35
73
  }
36
74
 
75
+ export declare const formEncodingtypes: readonly ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
76
+
77
+ export declare const formMethodTypes: readonly ["post", "get", "dialog"];
78
+
79
+ export declare const formTargetTypes: readonly ["_self", "_blank", "_parent", "_top"];
80
+
37
81
  export declare const iconPlacements: readonly ["leading", "trailing"];
38
82
 
39
83
  export declare const PieButton: ReactWebComponent<PieButton_2, {}>;
40
84
 
41
85
  /**
86
+ * @tagname pie-button
42
87
  * @slot icon - The icon slot
43
88
  * @slot - Default slot
44
89
  */
45
90
  declare class PieButton_2 extends LitElement implements ButtonProps {
91
+ static formAssociated: boolean;
92
+ private readonly _internals;
93
+ get form(): HTMLFormElement | null;
94
+ constructor();
46
95
  size: ButtonProps['size'];
47
96
  type: ButtonProps['type'];
48
97
  variant: ButtonProps['variant'];
@@ -50,6 +99,23 @@ declare class PieButton_2 extends LitElement implements ButtonProps {
50
99
  disabled: boolean;
51
100
  isLoading: boolean;
52
101
  isFullWidth: boolean;
102
+ name?: string;
103
+ value?: string;
104
+ formaction: ButtonProps['formaction'];
105
+ formenctype: ButtonProps['formenctype'];
106
+ formmethod: ButtonProps['formmethod'];
107
+ formnovalidate: ButtonProps['formnovalidate'];
108
+ formtarget: ButtonProps['formtarget'];
109
+ /**
110
+ * This method creates an invisible button of the same type as pie-button. It is then clicked, and immediately removed from the DOM.
111
+ * This is done so that we trigger native form actions, such as submit and reset in the browser. The performance impact of adding and removing a single button to the DOM
112
+ * should be neglible, however this should be monitored.
113
+ * This is the only viable way of guaranteeing native button behaviour when using a web component in place of an actual HTML button.
114
+ *
115
+ * TODO: if we need to repeat this logic elsewhere, then we should consider moving this code to a shared class or mixin.
116
+ */
117
+ private _simulateNativeButtonClick;
118
+ private _handleClick;
53
119
  render(): TemplateResult<1>;
54
120
  focus(): void;
55
121
  static styles: CSSResult;
@@ -57,7 +123,7 @@ declare class PieButton_2 extends LitElement implements ButtonProps {
57
123
 
58
124
  export declare const sizes: readonly ["xsmall", "small-productive", "small-expressive", "medium", "large"];
59
125
 
60
- export declare const types: readonly ["submit", "button", "reset", "menu"];
126
+ export declare const types: readonly ["submit", "button", "reset"];
61
127
 
62
128
  export declare type Variant = typeof variants[number];
63
129
 
package/dist/react.js CHANGED
@@ -1,6 +1,6 @@
1
- import * as g from "react";
2
- import { PieButton as P } from "./index.js";
3
- import { iconPlacements as k, sizes as D, types as G, variants as O } from "./index.js";
1
+ import * as E from "react";
2
+ import { PieButton as M } from "./index.js";
3
+ import { formEncodingtypes as j, formMethodTypes as k, formTargetTypes as D, iconPlacements as G, sizes as O, types as S, variants as _ } from "./index.js";
4
4
  import "lit";
5
5
  import "lit/decorators.js";
6
6
  /**
@@ -8,31 +8,31 @@ import "lit/decorators.js";
8
8
  * Copyright 2018 Google LLC
9
9
  * SPDX-License-Identifier: BSD-3-Clause
10
10
  */
11
- const b = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), E = /* @__PURE__ */ new WeakMap(), B = (d, l, m, p, u) => {
12
- const s = u == null ? void 0 : u[l];
13
- s === void 0 || m === p ? m == null && l in HTMLElement.prototype ? d.removeAttribute(l) : d[l] = m : ((i, t, h) => {
14
- let o = E.get(i);
15
- o === void 0 && E.set(i, o = /* @__PURE__ */ new Map());
16
- let a = o.get(t);
17
- h !== void 0 ? a === void 0 ? (o.set(t, a = { handleEvent: h }), i.addEventListener(t, a)) : a.handleEvent = h : a !== void 0 && (o.delete(t), i.removeEventListener(t, a));
18
- })(d, s, m);
11
+ const P = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), w = /* @__PURE__ */ new WeakMap(), b = (m, r, p, d, u) => {
12
+ const o = u == null ? void 0 : u[r];
13
+ o === void 0 || p === d ? p == null && r in HTMLElement.prototype ? m.removeAttribute(r) : m[r] = p : ((s, t, h) => {
14
+ let i = w.get(s);
15
+ i === void 0 && w.set(s, i = /* @__PURE__ */ new Map());
16
+ let a = i.get(t);
17
+ h !== void 0 ? a === void 0 ? (i.set(t, a = { handleEvent: h }), s.addEventListener(t, a)) : a.handleEvent = h : a !== void 0 && (i.delete(t), s.removeEventListener(t, a));
18
+ })(m, o, p);
19
19
  };
20
- function M(d = window.React, l, m, p, u) {
21
- let s, i, t;
22
- if (l === void 0) {
23
- const r = d;
24
- ({ tagName: i, elementClass: t, events: p, displayName: u } = r), s = r.react;
20
+ function B(m = window.React, r, p, d, u) {
21
+ let o, s, t;
22
+ if (r === void 0) {
23
+ const l = m;
24
+ ({ tagName: s, elementClass: t, events: d, displayName: u } = l), o = l.react;
25
25
  } else
26
- s = d, t = m, i = l;
27
- const h = s.Component, o = s.createElement, a = new Set(Object.keys(p ?? {}));
28
- class f extends h {
26
+ o = m, t = p, s = r;
27
+ const h = o.Component, i = o.createElement, a = new Set(Object.keys(d ?? {}));
28
+ class v extends h {
29
29
  constructor() {
30
30
  super(...arguments), this.o = null;
31
31
  }
32
32
  t(e) {
33
33
  if (this.o !== null)
34
- for (const v in this.i)
35
- B(this.o, v, this.props[v], e ? e[v] : void 0, p);
34
+ for (const f in this.i)
35
+ b(this.o, f, this.props[f], e ? e[f] : void 0, d);
36
36
  }
37
37
  componentDidMount() {
38
38
  this.t();
@@ -41,33 +41,36 @@ function M(d = window.React, l, m, p, u) {
41
41
  this.t(e);
42
42
  }
43
43
  render() {
44
- const { _$Gl: e, ...v } = this.props;
44
+ const { _$Gl: e, ...f } = this.props;
45
45
  this.h !== e && (this.u = (n) => {
46
- e !== null && ((c, w) => {
47
- typeof c == "function" ? c(w) : c.current = w;
46
+ e !== null && ((c, g) => {
47
+ typeof c == "function" ? c(g) : c.current = g;
48
48
  })(e, n), this.o = n, this.h = e;
49
49
  }), this.i = {};
50
50
  const y = { ref: this.u };
51
- for (const [n, c] of Object.entries(v))
52
- b.has(n) ? y[n === "className" ? "class" : n] = c : a.has(n) || n in t.prototype ? this.i[n] = c : y[n] = c;
53
- return o(i, y);
51
+ for (const [n, c] of Object.entries(f))
52
+ P.has(n) ? y[n === "className" ? "class" : n] = c : a.has(n) || n in t.prototype ? this.i[n] = c : y[n] = c;
53
+ return i(s, y);
54
54
  }
55
55
  }
56
- f.displayName = u ?? t.name;
57
- const N = s.forwardRef((r, e) => o(f, { ...r, _$Gl: e }, r == null ? void 0 : r.children));
58
- return N.displayName = f.displayName, N;
56
+ v.displayName = u ?? t.name;
57
+ const N = o.forwardRef((l, e) => i(v, { ...l, _$Gl: e }, l == null ? void 0 : l.children));
58
+ return N.displayName = v.displayName, N;
59
59
  }
60
- const R = M({
60
+ const L = B({
61
61
  displayName: "PieButton",
62
- elementClass: P,
63
- react: g,
62
+ elementClass: M,
63
+ react: E,
64
64
  tagName: "pie-button",
65
65
  events: {}
66
66
  });
67
67
  export {
68
- R as PieButton,
69
- k as iconPlacements,
70
- D as sizes,
71
- G as types,
72
- O as variants
68
+ L as PieButton,
69
+ j as formEncodingtypes,
70
+ k as formMethodTypes,
71
+ D as formTargetTypes,
72
+ G as iconPlacements,
73
+ O as sizes,
74
+ S as types,
75
+ _ as variants
73
76
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-button",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "PIE design system button built using web components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,12 +31,15 @@
31
31
  "@justeat/pie-design-tokens": "5.8.2",
32
32
  "@justeattakeaway/pie-components-config": "0.4.0",
33
33
  "@justeattakeaway/pie-css": "0.6.0",
34
- "@justeattakeaway/pie-webc-core": "0.10.0"
34
+ "@justeattakeaway/pie-webc-core": "0.11.0"
35
35
  },
36
36
  "volta": {
37
37
  "extends": "../../../package.json"
38
38
  },
39
39
  "sideEffects": [
40
40
  "dist/*.js"
41
- ]
41
+ ],
42
+ "dependencies": {
43
+ "element-internals-polyfill": "1.3.8"
44
+ }
42
45
  }
package/src/defs.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const sizes = ['xsmall', 'small-productive', 'small-expressive', 'medium', 'large'] as const;
2
- export const types = ['submit', 'button', 'reset', 'menu'] as const;
2
+ export const types = ['submit', 'button', 'reset'] as const;
3
3
  export const variants = [
4
4
  'primary', 'secondary', 'outline', 'outline-inverse', 'ghost',
5
5
  'inverse', 'ghost-inverse', 'destructive', 'destructive-ghost',
@@ -8,13 +8,17 @@ export const iconPlacements = ['leading', 'trailing'] as const;
8
8
 
9
9
  export type Variant = typeof variants[number];
10
10
 
11
+ export const formEncodingtypes = ['application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain'] as const;
12
+ export const formMethodTypes = ['post', 'get', 'dialog'] as const;
13
+ export const formTargetTypes = ['_self', '_blank', '_parent', '_top'] as const;
14
+
11
15
  export interface ButtonProps {
12
16
  /**
13
17
  * What size the button should be.
14
18
  */
15
19
  size: typeof sizes[number];
16
20
  /**
17
- * What type attribute should be applied to the button. For example submit, button or menu.
21
+ * What type attribute should be applied to the button. For example submit, button.
18
22
  */
19
23
  type: typeof types[number];
20
24
  /**
@@ -37,4 +41,49 @@ export interface ButtonProps {
37
41
  * When true, displays a loading indicator inside the button.
38
42
  */
39
43
  isLoading: boolean;
44
+
45
+ /**
46
+ * The name of the button, submitted as a pair with the button's value as part of the form data, when that button is used to submit the form.
47
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
48
+ */
49
+ name?: string;
50
+
51
+ /**
52
+ * Defines the value associated with the button's name when it's submitted with the form data. This value is passed to the server in params when the form is submitted using this button.
53
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
54
+ */
55
+ value?: string;
56
+
57
+ /**
58
+ * The URL that processes the information submitted by the button. Overrides the action attribute of the button's form owner. Does nothing if there is no form owner.
59
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
60
+ */
61
+ formaction?: string;
62
+
63
+ /**
64
+ * If the button is a submit button (it's inside/associated with a <form> and doesn't have type="button"), specifies how to encode the form data that is submitted.
65
+ * If this attribute is specified, it overrides the enctype attribute of the button's form owner.
66
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
67
+ */
68
+ formenctype?: typeof formEncodingtypes[number]
69
+
70
+ /**
71
+ * If the button is a submit button (it's inside/associated with a <form> and doesn't have type="button"), this attribute specifies the HTTP method used to submit the form.
72
+ * If specified, this attribute overrides the method attribute of the button's form owner.
73
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
74
+ */
75
+ formmethod?: typeof formMethodTypes[number]
76
+
77
+ /**
78
+ * If the button is a submit button, this Boolean attribute specifies that the form is not to be validated when it is submitted.
79
+ * If this attribute is specified, it overrides the novalidate attribute of the button's form owner.
80
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
81
+ */
82
+ formnovalidate?: boolean;
83
+
84
+ /**
85
+ * If the button is a submit button, this attribute is an author-defined name or standardized, underscore-prefixed keyword indicating where to display the response from submitting the form.
86
+ * [MDN reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes)
87
+ */
88
+ formtarget?: typeof formTargetTypes[number]
40
89
  }
package/src/index.ts CHANGED
@@ -2,11 +2,12 @@ import {
2
2
  LitElement, html, unsafeCSS, nothing,
3
3
  } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
- import { validPropertyValues } from '@justeattakeaway/pie-webc-core';
6
- import styles from './button.scss?inline';
5
+ import { validPropertyValues, defineCustomElement } from '@justeattakeaway/pie-webc-core';
7
6
  import {
8
7
  ButtonProps, sizes, types, variants, iconPlacements,
9
8
  } from './defs';
9
+ import styles from './button.scss?inline';
10
+ import 'element-internals-polyfill';
10
11
 
11
12
  // Valid values available to consumers
12
13
  export * from './defs';
@@ -14,10 +15,25 @@ export * from './defs';
14
15
  const componentSelector = 'pie-button';
15
16
 
16
17
  /**
18
+ * @tagname pie-button
17
19
  * @slot icon - The icon slot
18
20
  * @slot - Default slot
19
21
  */
20
22
  export class PieButton extends LitElement implements ButtonProps {
23
+ // TODO - we may want to consider making the element internals code reusable for other form controls.
24
+ static formAssociated = true;
25
+
26
+ private readonly _internals: ElementInternals;
27
+
28
+ public get form () {
29
+ return this._internals.form;
30
+ }
31
+
32
+ constructor () {
33
+ super();
34
+ this._internals = this.attachInternals();
35
+ }
36
+
21
37
  @property()
22
38
  @validPropertyValues(componentSelector, sizes, 'medium')
23
39
  public size: ButtonProps['size'] = 'medium';
@@ -43,6 +59,95 @@ export class PieButton extends LitElement implements ButtonProps {
43
59
  @property({ type: Boolean })
44
60
  public isFullWidth = false;
45
61
 
62
+ @property({ type: String })
63
+ public name?: string;
64
+
65
+ @property({ type: String })
66
+ public value?: string;
67
+
68
+ @property()
69
+ public formaction: ButtonProps['formaction'];
70
+
71
+ @property()
72
+ public formenctype: ButtonProps['formenctype'];
73
+
74
+ @property()
75
+ public formmethod: ButtonProps['formmethod'];
76
+
77
+ @property({ type: Boolean })
78
+ public formnovalidate: ButtonProps['formnovalidate'];
79
+
80
+ @property()
81
+ public formtarget: ButtonProps['formtarget'];
82
+
83
+ /**
84
+ * This method creates an invisible button of the same type as pie-button. It is then clicked, and immediately removed from the DOM.
85
+ * This is done so that we trigger native form actions, such as submit and reset in the browser. The performance impact of adding and removing a single button to the DOM
86
+ * should be neglible, however this should be monitored.
87
+ * This is the only viable way of guaranteeing native button behaviour when using a web component in place of an actual HTML button.
88
+ *
89
+ * TODO: if we need to repeat this logic elsewhere, then we should consider moving this code to a shared class or mixin.
90
+ */
91
+ private _simulateNativeButtonClick (btnType: 'submit' | 'reset') {
92
+ if (!this.form) return;
93
+
94
+ const btn = document.createElement('button');
95
+ btn.type = btnType;
96
+
97
+ // Visually hidden styles
98
+ btn.style.position = 'absolute';
99
+ btn.style.width = '1px';
100
+ btn.style.height = '1px';
101
+ btn.style.padding = '0';
102
+ btn.style.margin = '-1px';
103
+ btn.style.overflow = 'hidden';
104
+ btn.style.border = '0';
105
+ btn.style.whiteSpace = 'nowrap';
106
+
107
+ if (btnType === 'submit') {
108
+ if (this.name) {
109
+ btn.name = this.name;
110
+ }
111
+ if (this.value) {
112
+ btn.value = this.value;
113
+ }
114
+ if (this.formaction) {
115
+ btn.setAttribute('formaction', this.formaction);
116
+ }
117
+ if (this.formenctype) {
118
+ btn.setAttribute('formenctype', this.formenctype);
119
+ }
120
+ if (this.formmethod) {
121
+ btn.setAttribute('formmethod', this.formmethod);
122
+ }
123
+ if (this.formnovalidate) {
124
+ btn.setAttribute('formnovalidate', 'formnovalidate');
125
+ }
126
+ if (this.formtarget) {
127
+ btn.setAttribute('formtarget', this.formtarget);
128
+ }
129
+ }
130
+
131
+ this.form.append(btn);
132
+ btn.click();
133
+ btn.remove();
134
+ }
135
+
136
+ private _handleClick () {
137
+ if (!this.isLoading && this.form) {
138
+ if (this.type === 'submit') {
139
+ // only submit the form if either formnovalidate is set, or the form passes validation checks (triggers native form validation)
140
+ if (this.formnovalidate || this.form.reportValidity()) {
141
+ this._simulateNativeButtonClick('submit');
142
+ }
143
+ }
144
+
145
+ if (this.type === 'reset') {
146
+ this._simulateNativeButtonClick('reset');
147
+ }
148
+ }
149
+ }
150
+
46
151
  render () {
47
152
  const {
48
153
  type,
@@ -56,6 +161,7 @@ export class PieButton extends LitElement implements ButtonProps {
56
161
 
57
162
  return html`
58
163
  <button
164
+ @click=${this._handleClick}
59
165
  class="o-btn"
60
166
  type=${type}
61
167
  variant=${variant}
@@ -77,7 +183,7 @@ export class PieButton extends LitElement implements ButtonProps {
77
183
  static styles = unsafeCSS(styles);
78
184
  }
79
185
 
80
- customElements.define(componentSelector, PieButton);
186
+ defineCustomElement(componentSelector, PieButton);
81
187
 
82
188
  declare global {
83
189
  interface HTMLElementTagNameMap {