@justeattakeaway/pie-button 0.30.0 → 0.32.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,8 +32,52 @@ 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, {}>;
@@ -43,6 +87,10 @@ export declare const PieButton: ReactWebComponent<PieButton_2, {}>;
43
87
  * @slot - Default slot
44
88
  */
45
89
  declare class PieButton_2 extends LitElement implements ButtonProps {
90
+ static formAssociated: boolean;
91
+ private readonly _internals;
92
+ get form(): HTMLFormElement | null;
93
+ constructor();
46
94
  size: ButtonProps['size'];
47
95
  type: ButtonProps['type'];
48
96
  variant: ButtonProps['variant'];
@@ -50,6 +98,23 @@ declare class PieButton_2 extends LitElement implements ButtonProps {
50
98
  disabled: boolean;
51
99
  isLoading: boolean;
52
100
  isFullWidth: boolean;
101
+ name?: string;
102
+ value?: string;
103
+ formaction: ButtonProps['formaction'];
104
+ formenctype: ButtonProps['formenctype'];
105
+ formmethod: ButtonProps['formmethod'];
106
+ formnovalidate: ButtonProps['formnovalidate'];
107
+ formtarget: ButtonProps['formtarget'];
108
+ /**
109
+ * This method creates an invisible button of the same type as pie-button. It is then clicked, and immediately removed from the DOM.
110
+ * 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
111
+ * should be neglible, however this should be monitored.
112
+ * This is the only viable way of guaranteeing native button behaviour when using a web component in place of an actual HTML button.
113
+ *
114
+ * TODO: if we need to repeat this logic elsewhere, then we should consider moving this code to a shared class or mixin.
115
+ */
116
+ private _simulateNativeButtonClick;
117
+ private _handleClick;
53
118
  render(): TemplateResult<1>;
54
119
  focus(): void;
55
120
  static styles: CSSResult;
@@ -57,7 +122,7 @@ declare class PieButton_2 extends LitElement implements ButtonProps {
57
122
 
58
123
  export declare const sizes: readonly ["xsmall", "small-productive", "small-expressive", "medium", "large"];
59
124
 
60
- export declare const types: readonly ["submit", "button", "reset", "menu"];
125
+ export declare const types: readonly ["submit", "button", "reset"];
61
126
 
62
127
  export declare type Variant = typeof variants[number];
63
128
 
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.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "PIE design system button built using web components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,13 +30,16 @@
30
30
  "devDependencies": {
31
31
  "@justeat/pie-design-tokens": "5.8.2",
32
32
  "@justeattakeaway/pie-components-config": "0.4.0",
33
- "@justeattakeaway/pie-css": "0.5.1",
34
- "@justeattakeaway/pie-webc-core": "0.9.1"
33
+ "@justeattakeaway/pie-css": "0.6.0",
34
+ "@justeattakeaway/pie-webc-core": "0.10.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
@@ -3,10 +3,11 @@ import {
3
3
  } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
5
  import { validPropertyValues } from '@justeattakeaway/pie-webc-core';
6
- import styles from './button.scss?inline';
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';
@@ -18,6 +19,20 @@ const componentSelector = 'pie-button';
18
19
  * @slot - Default slot
19
20
  */
20
21
  export class PieButton extends LitElement implements ButtonProps {
22
+ // TODO - we may want to consider making the element internals code reusable for other form controls.
23
+ static formAssociated = true;
24
+
25
+ private readonly _internals: ElementInternals;
26
+
27
+ public get form () {
28
+ return this._internals.form;
29
+ }
30
+
31
+ constructor () {
32
+ super();
33
+ this._internals = this.attachInternals();
34
+ }
35
+
21
36
  @property()
22
37
  @validPropertyValues(componentSelector, sizes, 'medium')
23
38
  public size: ButtonProps['size'] = 'medium';
@@ -43,6 +58,95 @@ export class PieButton extends LitElement implements ButtonProps {
43
58
  @property({ type: Boolean })
44
59
  public isFullWidth = false;
45
60
 
61
+ @property({ type: String })
62
+ public name?: string;
63
+
64
+ @property({ type: String })
65
+ public value?: string;
66
+
67
+ @property()
68
+ public formaction: ButtonProps['formaction'];
69
+
70
+ @property()
71
+ public formenctype: ButtonProps['formenctype'];
72
+
73
+ @property()
74
+ public formmethod: ButtonProps['formmethod'];
75
+
76
+ @property({ type: Boolean })
77
+ public formnovalidate: ButtonProps['formnovalidate'];
78
+
79
+ @property()
80
+ public formtarget: ButtonProps['formtarget'];
81
+
82
+ /**
83
+ * This method creates an invisible button of the same type as pie-button. It is then clicked, and immediately removed from the DOM.
84
+ * 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
85
+ * should be neglible, however this should be monitored.
86
+ * This is the only viable way of guaranteeing native button behaviour when using a web component in place of an actual HTML button.
87
+ *
88
+ * TODO: if we need to repeat this logic elsewhere, then we should consider moving this code to a shared class or mixin.
89
+ */
90
+ private _simulateNativeButtonClick (btnType: 'submit' | 'reset') {
91
+ if (!this.form) return;
92
+
93
+ const btn = document.createElement('button');
94
+ btn.type = btnType;
95
+
96
+ // Visually hidden styles
97
+ btn.style.position = 'absolute';
98
+ btn.style.width = '1px';
99
+ btn.style.height = '1px';
100
+ btn.style.padding = '0';
101
+ btn.style.margin = '-1px';
102
+ btn.style.overflow = 'hidden';
103
+ btn.style.border = '0';
104
+ btn.style.whiteSpace = 'nowrap';
105
+
106
+ if (btnType === 'submit') {
107
+ if (this.name) {
108
+ btn.name = this.name;
109
+ }
110
+ if (this.value) {
111
+ btn.value = this.value;
112
+ }
113
+ if (this.formaction) {
114
+ btn.setAttribute('formaction', this.formaction);
115
+ }
116
+ if (this.formenctype) {
117
+ btn.setAttribute('formenctype', this.formenctype);
118
+ }
119
+ if (this.formmethod) {
120
+ btn.setAttribute('formmethod', this.formmethod);
121
+ }
122
+ if (this.formnovalidate) {
123
+ btn.setAttribute('formnovalidate', 'formnovalidate');
124
+ }
125
+ if (this.formtarget) {
126
+ btn.setAttribute('formtarget', this.formtarget);
127
+ }
128
+ }
129
+
130
+ this.form.append(btn);
131
+ btn.click();
132
+ btn.remove();
133
+ }
134
+
135
+ private _handleClick () {
136
+ if (!this.isLoading && this.form) {
137
+ if (this.type === 'submit') {
138
+ // only submit the form if either formnovalidate is set, or the form passes validation checks (triggers native form validation)
139
+ if (this.formnovalidate || this.form.reportValidity()) {
140
+ this._simulateNativeButtonClick('submit');
141
+ }
142
+ }
143
+
144
+ if (this.type === 'reset') {
145
+ this._simulateNativeButtonClick('reset');
146
+ }
147
+ }
148
+ }
149
+
46
150
  render () {
47
151
  const {
48
152
  type,
@@ -56,6 +160,7 @@ export class PieButton extends LitElement implements ButtonProps {
56
160
 
57
161
  return html`
58
162
  <button
163
+ @click=${this._handleClick}
59
164
  class="o-btn"
60
165
  type=${type}
61
166
  variant=${variant}
package/LICENSE DELETED
@@ -1,17 +0,0 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- Copyright (c) Just Eat Takeaway.com
6
-
7
- Licensed under the Apache License, Version 2.0 (the "License");
8
- you may not use this file except in compliance with the License.
9
- You may obtain a copy of the License at
10
-
11
- http://www.apache.org/licenses/LICENSE-2.0
12
-
13
- Unless required by applicable law or agreed to in writing, software
14
- distributed under the License is distributed on an "AS IS" BASIS,
15
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- See the License for the specific language governing permissions and
17
- limitations under the License.