@justeattakeaway/pie-button 0.10.2 → 0.12.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.
@@ -3,9 +3,9 @@ transforming...
3
3
  ✓ 4 modules transformed.
4
4
  rendering chunks...
5
5
  computing gzip size...
6
- dist/index.js 3.85 kB │ gzip: 1.61 kB
6
+ dist/index.js 4.32 kB │ gzip: 1.65 kB
7
7
  
8
8
  [vite:dts] Start generate declaration files...
9
- ✓ built in 8.24s
10
- [vite:dts] Declaration files built in 7676ms.
9
+ ✓ built in 19.30s
10
+ [vite:dts] Declaration files built in 18529ms.
11
11
  
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [Changed] - extended readme file to mention props, events and enum exports ([#434](https://github.com/justeattakeaway/pie/pull/434)) by [@jamieomaguire](https://github.com/jamieomaguire)
8
+
9
+ - [Changed] - Prevent hover and active status on disabled btns and change outline to border for safari support ([#434](https://github.com/justeattakeaway/pie/pull/434)) by [@jamieomaguire](https://github.com/jamieomaguire)
10
+
11
+ - [Removed] - custom event handler and updated tests accordingly ([#434](https://github.com/justeattakeaway/pie/pull/434)) by [@jamieomaguire](https://github.com/jamieomaguire)
12
+
13
+ - [Added] - isFullWidth prop to button ([#434](https://github.com/justeattakeaway/pie/pull/434)) by [@jamieomaguire](https://github.com/jamieomaguire)
14
+
15
+ ## 0.11.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [Changed] - Improved readme files ([#418](https://github.com/justeattakeaway/pie/pull/418)) by [@jamieomaguire](https://github.com/jamieomaguire)
20
+
21
+ - [Changed] - Refactor SCSS to reduce duplication ([#416](https://github.com/justeattakeaway/pie/pull/416)) by [@jamieomaguire](https://github.com/jamieomaguire)
22
+
23
+ - [Changed] - Copy playwright projects from monorepo root ([#416](https://github.com/justeattakeaway/pie/pull/416)) by [@jamieomaguire](https://github.com/jamieomaguire)
24
+
25
+ - [Changed] - Split small size into expressive and productive ([#416](https://github.com/justeattakeaway/pie/pull/416)) by [@jamieomaguire](https://github.com/jamieomaguire)
26
+
27
+ ### Patch Changes
28
+
29
+ - [Removed] the custom decorator and uses the native `customElement` decorator instead ([#417](https://github.com/justeattakeaway/pie/pull/417)) by [@fernandofranca](https://github.com/fernandofranca)
30
+
3
31
  ## 0.10.2
4
32
 
5
33
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,31 @@
1
- # pie-button
1
+ <p align="center">
2
+ <img align="center" src="../../../readme_image.png" height="200" alt="">
3
+ </p>
2
4
 
3
- This button is a Web Component built using Lit.
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/@justeattakeaway/pie-button">
7
+ <img alt="GitHub Workflow Status" src="https://img.shields.io/npm/v/@justeattakeaway/pie-button.svg">
8
+ </a>
9
+ </p>
10
+
11
+ # Table of Contents
12
+
13
+ 1. [Introduction](#pie-button)
14
+ 2. [Local Development](#local-development)
15
+ 3. [Props](#props)
16
+ 4. [Events](#events)
17
+ - [HTML example](#html)
18
+ - [Vue example (using Nuxt 3)](#vue-templates-using-nuxt-3)
19
+ - [React example (using Next 13)](#react-templates-using-next-13)
20
+ 5. [TypeScript Enum Exports](#typescript-enum-exports)
21
+ 6. [Testing](#testing)
22
+ - [Browser Tests](#browser-tests)
23
+ - [Visual Tests](#visual-tests)
24
+
25
+
26
+ ## `pie-button`
27
+
28
+ `pie-button` is a Web Component built using the Lit library. It offers a simple and accessible button component for web applications. This component can be easily integrated into various frontend frameworks and customized through a set of properties. For TypeScript projects, it also provides exported enums for type safety and autocompletion.
4
29
 
5
30
  ## Local development
6
31
 
@@ -24,3 +49,76 @@ Local dev server using Vite (with hot module reloading)
24
49
  ```
25
50
  yarn dev
26
51
  ```
52
+ ## Props
53
+
54
+ | Property | Type | Default | Description |
55
+ |-------------|-----------|-----------------|----------------------------------------------------------------------|
56
+ | size | `String` | `BUTTON_SIZE.MEDIUM` | Size of the button, one of `BUTTON_SIZE` enum values (TypeScript Enum) or a raw string value such as `'large'` |
57
+ | type | `String` | `BUTTON_TYPE.SUBMIT` | Type of the button, one of `BUTTON_TYPE` enum values (TypeScript Enum) or a raw string value such as `'submit'` |
58
+ | variant | `String` | `BUTTON_VARIANT.PRIMARY` | Variant of the button, one of `BUTTON_VARIANT` enum values (TypeScript Enum) or a raw string value such as `'primary'` |
59
+ | disabled | `Boolean` | `false` | If `true`, disables the button. |
60
+ | isFullWidth | `Boolean` | `false` | If `true`, sets the button width to 100% of it's container. |
61
+
62
+
63
+ ## Events
64
+
65
+ This component does not use any custom event handlers. In order to add event listening to this component, you can treat it like a native HTML element in your application.
66
+
67
+ For example, to add a click handler in various templates:
68
+
69
+ ### HTML
70
+ ```html
71
+ <!-- Other attributes omitted for clarity -->
72
+ <pie-button onclick="e => console.log(e)">Click me!</pie-button>
73
+ ```
74
+
75
+ ### Vue templates (using Nuxt 3)
76
+ ```html
77
+ <!-- Other attributes omitted for clarity -->
78
+ <pie-button @click="handleClick">Click me!</pie-button>
79
+ ```
80
+
81
+ ### React templates (using Next 13)
82
+ ```html
83
+ <!-- Other attributes omitted for clarity -->
84
+ <PieButton onClick={handleClick}>increment</PieButton>
85
+
86
+ ```
87
+
88
+ ## TypeScript Enum Exports
89
+
90
+ For TypeScript projects, we export three enums related to button properties: `BUTTON_SIZE`, `BUTTON_TYPE`, and `BUTTON_VARIANT`. You can import and use these enums to set the corresponding property values for the `pie-button` component. This ensures better type safety and autocompletion in your project.
91
+
92
+ Here's an example of how to import and use the enums in a TypeScript project:
93
+
94
+ ```typescript
95
+ import { BUTTON_SIZE, BUTTON_TYPE, BUTTON_VARIANT } from '@justeattakeaway/pie-button';
96
+
97
+ // Using the enums to set property values
98
+ const myButtonSize = BUTTON_SIZE.LARGE;
99
+ const myButtonType = BUTTON_TYPE.RESET;
100
+ const myButtonVariant = BUTTON_VARIANT.SECONDARY;
101
+ ```
102
+
103
+ In your markup or JSX, you can then use these variables to set the properties for the pie-button component:
104
+
105
+ ```html
106
+ <PieButton size={myButtonSize} type={myButtonType} variant={myButtonVariant}>Click me!</PieButton>
107
+ ```
108
+
109
+
110
+ ## Testing
111
+
112
+ ### Browser tests
113
+
114
+ Run at the root of the monorepo:
115
+ ```
116
+ yarn test:browsers --filter=pie-button
117
+ ```
118
+
119
+ ### Visual tests
120
+
121
+ Run at the root of the monorepo:
122
+ ```
123
+ yarn test:visual --filter=pie-button
124
+ ```
package/dist/index.js CHANGED
@@ -1,80 +1,81 @@
1
1
  import { unsafeCSS as f, LitElement as h, html as g } from "lit";
2
2
  import { classMap as m } from "lit/directives/class-map.js";
3
- import { property as b, customElement as v } from "lit/decorators.js";
4
- const x = `.o-btn{border-radius:50rem;border:none;font-family:JetSansDigital;font-weight:700;cursor:pointer;user-select:none;outline:none}.o-btn:focus-visible{outline:2px solid #4996fd}.o-btn--xsmall{padding:6px 8px;min-height:32px;font-size:14px;line-height:20px}.o-btn--small{padding:8px 16px;min-height:40px;font-size:19px;line-height:28px}.o-btn--medium{padding:10px 24px;min-height:48px;font-size:20px;line-height:28px}.o-btn--large{padding:14px 24px;min-height:56px;font-size:20px;line-height:28px}.o-btn--primary{background-color:#f36805;color:#fff}.o-btn--primary:hover{background-color:#df5f05}.o-btn--primary:active{background-color:#b74e04}.o-btn--secondary{background-color:#f5f3f1;color:#242e30}.o-btn--secondary:hover{background-color:#ede9e5}.o-btn--secondary:active{background-color:#dcd4cd}.o-btn--outline{outline:1px solid #dbd9d7;background-color:#fff;color:#303d3f}.o-btn--outline:hover{background-color:#f5f5f5}.o-btn--outline:active{background-color:#e0e0e0}.o-btn--ghost{background-color:#fff;color:#242e30}.o-btn--ghost:hover{background-color:#f5f5f5}.o-btn--ghost:active{background-color:#e0e0e0}.o-btn.o-btn--is-disabled{background-color:#efedea;color:#8c999b;outline:1px solid #efedea;cursor:default}.o-btn.o-btn--is-disabled.o-btn--ghost{background-color:#fff;outline:none}
5
- `, p = (e, n) => function(i, t) {
6
- const o = `#${t}`;
7
- Object.defineProperty(i, t, {
3
+ import { property as a, customElement as x } from "lit/decorators.js";
4
+ const v = `.o-btn{border-radius:50rem;border:none;font-family:JetSansDigital;cursor:pointer;user-select:none;outline:none}.o-btn:focus-visible{outline:2px solid #4996fd}.o-btn--xsmall{min-block-size:32px;padding:6px 8px;font-size:14px;font-weight:700;line-height:20px}.o-btn--small-productive{min-block-size:40px;padding:8px 16px;font-size:16px;font-weight:700;line-height:24px}.o-btn--small-expressive{min-block-size:40px;padding:6px 16px;font-size:20px;font-weight:700;line-height:28px}.o-btn--medium{min-block-size:48px;padding:10px 24px;font-size:20px;font-weight:700;line-height:28px}.o-btn--large{min-block-size:56px;padding:14px 24px;font-size:20px;font-weight:700;line-height:28px}.o-btn--primary{background-color:#f36805;color:#fff}.o-btn--primary:hover:not(:disabled){background-color:#df5f05}.o-btn--primary:active:not(:disabled){background-color:#b74e04}.o-btn--secondary{background-color:#f5f3f1;color:#242e30}.o-btn--secondary:hover:not(:disabled){background-color:#ede9e5}.o-btn--secondary:active:not(:disabled){background-color:#dcd4cd}.o-btn--outline{background-color:#fff;color:#303d3f;border:1px solid #dbd9d7}.o-btn--outline:hover:not(:disabled){background-color:#f5f5f5}.o-btn--outline:active:not(:disabled){background-color:#e0e0e0}.o-btn--ghost{background-color:#fff;color:#242e30}.o-btn--ghost:hover:not(:disabled){background-color:#f5f5f5}.o-btn--ghost:active:not(:disabled){background-color:#e0e0e0}.o-btn.o-btn--is-disabled{background-color:#efedea;color:#8c999b;border:1px solid #efedea;cursor:not-allowed}.o-btn.o-btn--is-disabled:hover:not(:disabled){background-color:#e6e3de}.o-btn.o-btn--is-disabled:active:not(:disabled){background-color:#d5cfc7}.o-btn.o-btn--is-disabled.o-btn--ghost{background-color:#fff;outline:none}.o-btn--fullWidth{inline-size:100%}
5
+ `, u = (o, n) => function(r, t) {
6
+ const e = `#${t}`;
7
+ Object.defineProperty(r, t, {
8
8
  get() {
9
- return this[o];
9
+ return this[e];
10
10
  },
11
- set(r) {
12
- const s = this[o];
13
- e.includes(r) ? this[o] = r : (console.error(
14
- `[pie-button] Invalid value "${r}" provided for property "${t}".`,
15
- `Must be one of: ${e.join(" | ")}.`,
11
+ set(i) {
12
+ const d = this[e];
13
+ o.includes(i) ? this[e] = i : (console.error(
14
+ `[pie-button] Invalid value "${i}" provided for property "${t}".`,
15
+ `Must be one of: ${o.join(" | ")}.`,
16
16
  `Falling back to default value: "${n}"`
17
- ), this[o] = n), this.requestUpdate(t, s);
17
+ ), this[e] = n), this.requestUpdate(t, d);
18
18
  }
19
19
  });
20
20
  };
21
- var a = /* @__PURE__ */ ((e) => (e.XSMALL = "xsmall", e.SMALL = "small", e.MEDIUM = "medium", e.LARGE = "large", e))(a || {}), d = /* @__PURE__ */ ((e) => (e.SUBMIT = "submit", e.BUTTON = "button", e.RESET = "reset", e.MENU = "menu", e))(d || {}), u = /* @__PURE__ */ ((e) => (e.PRIMARY = "primary", e.SECONDARY = "secondary", e.OUTLINE = "outline", e.GHOST = "ghost", e))(u || {}), y = Object.defineProperty, M = Object.getOwnPropertyDescriptor, c = (e, n, i, t) => {
22
- for (var o = t > 1 ? void 0 : t ? M(n, i) : n, r = e.length - 1, s; r >= 0; r--)
23
- (s = e[r]) && (o = (t ? s(n, i, o) : s(o)) || o);
24
- return t && o && y(n, i, o), o;
21
+ var b = /* @__PURE__ */ ((o) => (o.XSMALL = "xsmall", o.SMALL_EXPRESSIVE = "small-expressive", o.SMALL_PRODUCTIVE = "small-productive", o.MEDIUM = "medium", o.LARGE = "large", o))(b || {}), c = /* @__PURE__ */ ((o) => (o.SUBMIT = "submit", o.BUTTON = "button", o.RESET = "reset", o.MENU = "menu", o))(c || {}), p = /* @__PURE__ */ ((o) => (o.PRIMARY = "primary", o.SECONDARY = "secondary", o.OUTLINE = "outline", o.GHOST = "ghost", o))(p || {}), y = Object.defineProperty, M = Object.getOwnPropertyDescriptor, l = (o, n, r, t) => {
22
+ for (var e = t > 1 ? void 0 : t ? M(n, r) : n, i = o.length - 1, d; i >= 0; i--)
23
+ (d = o[i]) && (e = (t ? d(n, r, e) : d(e)) || e);
24
+ return t && e && y(n, r, e), e;
25
25
  };
26
- function k(e) {
27
- return (n) => {
28
- if (!customElements.get(e))
29
- return v(e)(n);
30
- };
31
- }
32
- let l = class extends h {
26
+ let s = class extends h {
33
27
  constructor() {
34
- super(...arguments), this.size = a.MEDIUM, this.type = d.SUBMIT, this.variant = u.PRIMARY, this.disabled = !1;
28
+ super(...arguments), this.size = b.MEDIUM, this.type = c.SUBMIT, this.variant = p.PRIMARY, this.disabled = !1, this.isFullWidth = !1;
35
29
  }
36
30
  render() {
37
- const { size: e, type: n, variant: i, disabled: t } = this, o = {
31
+ const {
32
+ size: o,
33
+ type: n,
34
+ variant: r,
35
+ disabled: t,
36
+ isFullWidth: e
37
+ } = this, i = {
38
38
  "o-btn": !0,
39
- [`o-btn--${e}`]: e,
40
- [`o-btn--${i}`]: i,
41
- "o-btn--is-disabled": t
42
- }, r = () => {
43
- const s = new CustomEvent("CustomEvent", { detail: "WC event dispatched" });
44
- console.info("WC event dispatched"), this.dispatchEvent(s);
39
+ [`o-btn--${o}`]: o,
40
+ [`o-btn--${r}`]: r,
41
+ "o-btn--is-disabled": t,
42
+ "o-btn--fullWidth": e
45
43
  };
46
44
  return g`
47
45
  <button
48
- class=${m(o)}
46
+ class=${m(i)}
49
47
  type=${n}
50
48
  ?disabled=${t}
51
- @click="${r}">
49
+ ?isFullWidth=${e}>
52
50
  <slot></slot>
53
51
  </button>`;
54
52
  }
55
53
  };
56
- l.styles = f(x);
57
- c([
58
- b(),
59
- p(Object.values(a), a.MEDIUM)
60
- ], l.prototype, "size", 2);
61
- c([
62
- b(),
63
- p(Object.values(d), d.SUBMIT)
64
- ], l.prototype, "type", 2);
65
- c([
66
- b(),
67
- p(Object.values(u), u.PRIMARY)
68
- ], l.prototype, "variant", 2);
69
- c([
70
- b({ type: Boolean, reflect: !0 })
71
- ], l.prototype, "disabled", 2);
72
- l = c([
73
- k("pie-button")
74
- ], l);
54
+ s.styles = f(v);
55
+ l([
56
+ a(),
57
+ u(Object.values(b), b.MEDIUM)
58
+ ], s.prototype, "size", 2);
59
+ l([
60
+ a(),
61
+ u(Object.values(c), c.SUBMIT)
62
+ ], s.prototype, "type", 2);
63
+ l([
64
+ a(),
65
+ u(Object.values(p), p.PRIMARY)
66
+ ], s.prototype, "variant", 2);
67
+ l([
68
+ a({ type: Boolean, reflect: !0 })
69
+ ], s.prototype, "disabled", 2);
70
+ l([
71
+ a({ type: Boolean, reflect: !0 })
72
+ ], s.prototype, "isFullWidth", 2);
73
+ s = l([
74
+ x("pie-button")
75
+ ], s);
75
76
  export {
76
- a as BUTTON_SIZE,
77
- d as BUTTON_TYPE,
78
- u as BUTTON_VARIANT,
79
- l as PieButton
77
+ b as BUTTON_SIZE,
78
+ c as BUTTON_TYPE,
79
+ p as BUTTON_VARIANT,
80
+ s as PieButton
80
81
  };
@@ -3,7 +3,8 @@
3
3
  */
4
4
  export declare enum BUTTON_SIZE {
5
5
  XSMALL = "xsmall",
6
- SMALL = "small",
6
+ SMALL_EXPRESSIVE = "small-expressive",
7
+ SMALL_PRODUCTIVE = "small-productive",
7
8
  MEDIUM = "medium",
8
9
  LARGE = "large"
9
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,WAAW;IACnB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,KAAK,UAAU;CAClB;AAED;;GAEG;AACH,oBAAY,WAAW;IACnB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;CAChB;AAED;;GAEG;AACH,oBAAY,cAAc;IACtB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,KAAK,UAAU;CAClB"}
1
+ {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,WAAW;IACnB,MAAM,WAAW;IACjB,gBAAgB,qBAAqB;IACrC,gBAAgB,qBAAqB;IACrC,MAAM,WAAW;IACjB,KAAK,UAAU;CAClB;AAED;;GAEG;AACH,oBAAY,WAAW;IACnB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;CAChB;AAED;;GAEG;AACH,oBAAY,cAAc;IACtB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,KAAK,UAAU;CAClB"}
@@ -6,6 +6,7 @@ export declare class PieButton extends LitElement {
6
6
  type: BUTTON_TYPE;
7
7
  variant: BUTTON_VARIANT;
8
8
  disabled: boolean;
9
+ isFullWidth: boolean;
9
10
  render(): import("lit-html").TemplateResult<1>;
10
11
  static styles: import("lit").CSSResult;
11
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,KAAK,CAAC;AAMlD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGlE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAWpD,qBACa,SAAU,SAAQ,UAAU;IAGrC,IAAI,EAAG,WAAW,CAAsB;IAIxC,IAAI,EAAG,WAAW,CAAsB;IAIxC,OAAO,EAAG,cAAc,CAA0B;IAGlD,QAAQ,EAAG,OAAO,CAAS;IAE3B,MAAM;IA2BN,MAAM,CAAC,MAAM,0BAAqB;CACrC;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,YAAY,EAAE,SAAS,CAAC;KAC3B;CACJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,KAAK,CAAC;AAMlD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGlE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAEpD,qBACa,SAAU,SAAQ,UAAU;IAGjC,IAAI,EAAG,WAAW,CAAsB;IAIxC,IAAI,EAAG,WAAW,CAAsB;IAIxC,OAAO,EAAG,cAAc,CAA0B;IAGlD,QAAQ,UAAS;IAGjB,WAAW,UAAS;IAExB,MAAM;IAwBN,MAAM,CAAC,MAAM,0BAAqB;CACrC;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,YAAY,EAAE,SAAS,CAAC;KAC3B;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-button",
3
- "version": "0.10.2",
3
+ "version": "0.12.0",
4
4
  "description": "PIE design system button built using web components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,11 +31,24 @@
31
31
  body {
32
32
  font-feature-settings: "tnum"; /* Enable tabular numbers */
33
33
  }
34
+ /* basic styles to center align components and give them some spacing */
35
+ #root {
36
+ padding: 1em;
37
+ }
38
+
39
+ #root > * {
40
+ display: block;
41
+ margin-inline: auto;
42
+ }
43
+
44
+ #root > * + * {
45
+ margin-top: 1em;
46
+ }
34
47
  </style>
35
48
  <title>Testing Page</title>
36
- </head>
37
- <body>
49
+ </head>
50
+ <body>
38
51
  <div id="root"></div>
39
52
  <script type="module" src="./index.ts"></script>
40
- </body>
41
- </html>
53
+ </body>
54
+ </html>
@@ -33,11 +33,20 @@ export default defineConfig({
33
33
  /* Configure projects for major browsers */
34
34
  projects: [
35
35
  {
36
- name: 'component:chrome',
36
+ name: 'visual:desktop',
37
+ grepInvert: /@mobile/,
37
38
  use: {
38
39
  ...devices['Desktop Chrome'],
39
40
  },
40
41
  testMatch: ['**/test/visual/*.spec.ts']
41
- }
42
+ },
43
+ {
44
+ name: 'visual:mobile',
45
+ grep: /@mobile/,
46
+ use: {
47
+ ...devices['Pixel 5'],
48
+ },
49
+ testMatch: ['**/test/visual/*.spec.ts']
50
+ },
42
51
  ],
43
- });
52
+ });
package/src/button.scss CHANGED
@@ -1,110 +1,160 @@
1
1
  @use '@justeat/pie-design-tokens/dist/jet.scss' as dt;
2
2
 
3
+ $button-height-xs: 32px;
4
+ $button-height-s: 40px;
5
+ $button-height-m: 48px;
6
+ $button-height-l: 56px;
7
+
8
+ $button-border-radius: dt.$radius-rounded-e;
9
+ $button-font: dt.$font-interactive-m-family;
10
+ $button-focus: dt.$color-focus;
11
+
12
+ $button-bg-color-primary: dt.$color-interactive-brand;
13
+ $button-text-color-primary: dt.$color-content-interactive-primary;
14
+
15
+ $button-bg-color-secondary: dt.$color-interactive-secondary;
16
+ $button-text-color-secondary: dt.$color-content-interactive-secondary;
17
+
18
+ $button-bg-color-outline: dt.$color-container-default;
19
+ $button-text-color-outline: dt.$color-content-interactive-tertiary;
20
+ $button-outline-color-outline: dt.$color-border-strong;
21
+
22
+ $button-bg-color-ghost: dt.$color-container-default;
23
+ $button-text-color-ghost: dt.$color-content-link;
24
+ $button-disabled-bg-color-ghost: dt.$color-container-default;
25
+
26
+ $button-bg-color-disabled: dt.$color-disabled-01;
27
+ $button-text-color-disabled: dt.$color-content-disabled;
28
+ $button-outline-color-disabled: dt.$color-disabled-01;
29
+
30
+ // Sets up the size of a button such as xsmall or large
31
+ @mixin button-sizing($min-block-size, $padding-block, $padding-inline, $font-size, $font-weight, $line-height) {
32
+ min-block-size: $min-block-size;
33
+ padding: $padding-block $padding-inline;
34
+ font-size: $font-size * 1px;
35
+ font-weight: $font-weight;
36
+ line-height: $line-height * 1px;
37
+ }
38
+
39
+ // 'Themes' a button for it's particular variant, such as primary, secondary, outline, ghost
40
+ @mixin button-variant($bg-color, $text-color, $outline-color: null) {
41
+ background-color: $bg-color;
42
+ color: $text-color;
43
+
44
+ &:hover:not(:disabled) {
45
+ background-color: darken($bg-color, dt.$color-hover-01);
46
+ }
47
+
48
+ &:active:not(:disabled) {
49
+ background-color: darken($bg-color, dt.$color-active-01);
50
+ }
51
+
52
+ @if $outline-color != null {
53
+ border: 1px solid $outline-color;
54
+ }
55
+ }
56
+
57
+ // Base button styles
3
58
  .o-btn {
4
- border-radius: dt.$radius-rounded-e;
59
+ border-radius: $button-border-radius;
5
60
  border: none;
6
- font-family: dt.$font-interactive-m-family;
7
- font-weight: dt.$font-interactive-m-weight;
61
+ font-family: $button-font;
8
62
 
9
63
  cursor: pointer;
10
64
  user-select: none;
11
65
  outline: none;
12
66
 
13
67
  &:focus-visible {
14
- outline: 2px solid dt.$color-focus;
68
+ outline: 2px solid $button-focus;
15
69
  }
16
70
  }
17
71
 
18
- .o-btn--xsmall {
19
- padding: 6px dt.$spacing-b;
20
- min-height: 32px;
21
- font-size: dt.$font-size-14 * 1px;
22
- line-height: dt.$font-size-14-line-height * 1px;
23
- }
24
-
25
- .o-btn--small {
26
- padding: 8px dt.$spacing-d;
27
- min-height: 40px;
28
- font-size: dt.$font-size-19 * 1px;
29
- line-height: dt.$font-size-19-line-height * 1px;
30
- }
31
-
32
- .o-btn--medium {
33
- padding: 10px dt.$spacing-e;
34
- min-height: 48px;
35
- font-size: dt.$font-size-20 * 1px;
36
- line-height: dt.$font-size-20-line-height * 1px;
37
- }
38
-
39
- .o-btn--large {
40
- padding: 14px dt.$spacing-e;
41
- min-height: 56px;
42
- font-size: dt.$font-size-20 * 1px;
43
- line-height: dt.$font-size-20-line-height * 1px;
72
+ // Sizes
73
+ $button-sizes: (
74
+ 'xsmall': (
75
+ 'min-block-size': $button-height-xs,
76
+ 'padding-block': 6px,
77
+ 'padding-inline': dt.$spacing-b,
78
+ 'font-size': dt.$font-interactive-xs-size,
79
+ 'font-weight': dt.$font-interactive-xs-weight,
80
+ 'line-height': dt.$font-interactive-xs-line-height
81
+ ),
82
+ 'small-productive': (
83
+ 'min-block-size': $button-height-s,
84
+ 'padding-block': dt.$spacing-b,
85
+ 'padding-inline': dt.$spacing-d,
86
+ 'font-size': dt.$font-interactive-s-size,
87
+ 'font-weight': dt.$font-interactive-s-weight,
88
+ 'line-height': dt.$font-interactive-s-line-height
89
+ ),
90
+ 'small-expressive': (
91
+ 'min-block-size': $button-height-s,
92
+ 'padding-block': 6px,
93
+ 'padding-inline': dt.$spacing-d,
94
+ 'font-size': dt.$font-interactive-l-size,
95
+ 'font-weight': dt.$font-interactive-l-weight,
96
+ 'line-height': dt.$font-interactive-l-line-height
97
+ ),
98
+ 'medium': (
99
+ 'min-block-size': $button-height-m,
100
+ 'padding-block': 10px,
101
+ 'padding-inline': dt.$spacing-e,
102
+ 'font-size': dt.$font-interactive-l-size,
103
+ 'font-weight': dt.$font-interactive-l-weight,
104
+ 'line-height': dt.$font-interactive-l-line-height
105
+ ),
106
+ 'large': (
107
+ 'min-block-size': $button-height-l,
108
+ 'padding-block': 14px,
109
+ 'padding-inline': dt.$spacing-e,
110
+ 'font-size': dt.$font-interactive-l-size,
111
+ 'font-weight': dt.$font-interactive-l-weight,
112
+ 'line-height': dt.$font-interactive-l-line-height
113
+ )
114
+ );
115
+
116
+ @each $size, $values in $button-sizes {
117
+ .o-btn--#{$size} {
118
+ @include button-sizing(
119
+ map-get($values, 'min-block-size'),
120
+ map-get($values, 'padding-block'),
121
+ map-get($values, 'padding-inline'),
122
+ map-get($values, 'font-size'),
123
+ map-get($values, 'font-weight'),
124
+ map-get($values, 'line-height')
125
+ );
126
+ }
44
127
  }
45
128
 
129
+ // Variants
46
130
  .o-btn--primary {
47
- background-color: dt.$color-interactive-brand;
48
- color: dt.$color-content-interactive-primary;
49
-
50
- &:hover {
51
- background-color: darken(dt.$color-interactive-brand, dt.$color-hover-01);
52
- }
53
-
54
- &:active {
55
- background-color: darken(dt.$color-interactive-brand, dt.$color-active-01);
56
- }
131
+ @include button-variant($button-bg-color-primary, $button-text-color-primary);
57
132
  }
58
133
 
59
134
  .o-btn--secondary {
60
- background-color: dt.$color-interactive-secondary;
61
- color: dt.$color-content-interactive-secondary;
62
-
63
- &:hover {
64
- background-color: darken(dt.$color-interactive-secondary, dt.$color-hover-01);
65
- }
66
-
67
- &:active {
68
- background-color: darken(dt.$color-interactive-secondary, dt.$color-active-01);
69
- }
135
+ @include button-variant($button-bg-color-secondary, $button-text-color-secondary);
70
136
  }
71
137
 
72
138
  .o-btn--outline {
73
- outline: 1px solid dt.$color-border-strong;
74
- background-color: dt.$color-container-default;
75
- color: dt.$color-content-interactive-tertiary;
76
-
77
- &:hover {
78
- background-color: darken(dt.$color-container-default, dt.$color-hover-01);
79
- }
80
-
81
- &:active {
82
- background-color: darken(dt.$color-container-default, dt.$color-active-01);
83
- }
139
+ @include button-variant($button-bg-color-outline, $button-text-color-outline, $button-outline-color-outline);
84
140
  }
85
141
 
86
142
  .o-btn--ghost {
87
- background-color: dt.$color-container-default;
88
- color: dt.$color-content-link;
89
-
90
- &:hover {
91
- background-color: darken(dt.$color-container-default, dt.$color-hover-01);
92
- }
93
-
94
- &:active {
95
- background-color: darken(dt.$color-container-default, dt.$color-active-01);
96
- }
143
+ @include button-variant($button-bg-color-ghost, $button-text-color-ghost);
97
144
  }
98
145
 
99
146
  .o-btn.o-btn--is-disabled {
100
- background-color: dt.$color-disabled-01;
101
- color: dt.$color-content-disabled;
102
- outline: 1px solid dt.$color-disabled-01;
147
+ @include button-variant($button-bg-color-disabled, $button-text-color-disabled, $button-outline-color-disabled);
103
148
 
104
- cursor: default;
149
+ cursor: not-allowed;
105
150
 
106
151
  &.o-btn--ghost {
107
- background-color: dt.$color-container-default;
152
+ background-color: $button-disabled-bg-color-ghost;
108
153
  outline: none;
109
154
  }
110
- }
155
+ }
156
+
157
+ // Additional modifiers
158
+ .o-btn--fullWidth {
159
+ inline-size: 100%;
160
+ }
package/src/defs.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  */
4
4
  export enum BUTTON_SIZE {
5
5
  XSMALL = 'xsmall',
6
- SMALL = 'small',
6
+ SMALL_EXPRESSIVE = 'small-expressive',
7
+ SMALL_PRODUCTIVE = 'small-productive',
7
8
  MEDIUM = 'medium',
8
9
  LARGE = 'large'
9
10
  }
package/src/index.ts CHANGED
@@ -9,54 +9,45 @@ import { BUTTON_SIZE, BUTTON_TYPE, BUTTON_VARIANT } from './defs';
9
9
  // Valid values available to consumers
10
10
  export { BUTTON_SIZE, BUTTON_TYPE, BUTTON_VARIANT };
11
11
 
12
- // TODO: Extract as a utility function in a shared package
13
- function defineCustomElement(elementName:string) {
14
- return (elementClass:typeof LitElement) => {
15
- if(customElements.get(elementName)) return;
16
-
17
- return customElement(elementName)(elementClass);
18
- }
19
- }
20
-
21
- @defineCustomElement('pie-button')
12
+ @customElement('pie-button')
22
13
  export class PieButton extends LitElement {
23
14
  @property()
24
15
  @validPropertyValues(Object.values(BUTTON_SIZE), BUTTON_SIZE.MEDIUM)
25
- size : BUTTON_SIZE = BUTTON_SIZE.MEDIUM;
16
+ size : BUTTON_SIZE = BUTTON_SIZE.MEDIUM;
26
17
 
27
18
  @property()
28
19
  @validPropertyValues(Object.values(BUTTON_TYPE), BUTTON_TYPE.SUBMIT)
29
- type : BUTTON_TYPE = BUTTON_TYPE.SUBMIT;
20
+ type : BUTTON_TYPE = BUTTON_TYPE.SUBMIT;
30
21
 
31
22
  @property()
32
23
  @validPropertyValues(Object.values(BUTTON_VARIANT), BUTTON_VARIANT.PRIMARY)
33
- variant : BUTTON_VARIANT = BUTTON_VARIANT.PRIMARY;
24
+ variant : BUTTON_VARIANT = BUTTON_VARIANT.PRIMARY;
25
+
26
+ @property({ type: Boolean, reflect: true })
27
+ disabled = false;
34
28
 
35
- @property({type: Boolean, reflect: true})
36
- disabled : boolean = false;
29
+ @property({ type: Boolean, reflect: true })
30
+ isFullWidth = false;
37
31
 
38
32
  render () {
39
- const { size, type, variant, disabled } = this;
33
+ const {
34
+ size, type, variant, disabled, isFullWidth,
35
+ } = this;
40
36
 
41
37
  const classes = {
42
38
  'o-btn': true,
43
39
  [`o-btn--${size}`]: size,
44
40
  [`o-btn--${variant}`]: variant,
45
41
  'o-btn--is-disabled': disabled,
42
+ 'o-btn--fullWidth': isFullWidth,
46
43
  };
47
44
 
48
- const raiseWCEvent = () => {
49
- const event = new CustomEvent('CustomEvent', { detail: 'WC event dispatched' })
50
- console.info('WC event dispatched')
51
- this.dispatchEvent(event)
52
- }
53
-
54
45
  return html`
55
46
  <button
56
47
  class=${classMap(classes)}
57
48
  type=${type}
58
49
  ?disabled=${disabled}
59
- @click="${raiseWCEvent}">
50
+ ?isFullWidth=${isFullWidth}>
60
51
  <slot></slot>
61
52
  </button>`;
62
53
  }
@@ -6,46 +6,52 @@ const sizes = Object.values(BUTTON_SIZE);
6
6
  const variants = Object.values(BUTTON_VARIANT);
7
7
  const disabledStates = [true, false];
8
8
 
9
- variants.forEach(variant => {
10
- test(`should render - ${variant}`, async ({ mount }) => {
9
+ variants.forEach((variant) => {
10
+ test(`should render - ${variant}`, async ({ mount }) => {
11
+ for (const size of sizes) {
12
+ for (const isDisabled of disabledStates) {
13
+ const component = await mount(
14
+ PieButton,
15
+ {
16
+ props: {
17
+ size,
18
+ variant,
19
+ disabled: isDisabled,
20
+ },
21
+ slots: {
22
+ default: `Hello, ${size} ${variant} Button!`,
23
+ },
24
+ },
25
+ );
11
26
 
12
- for (const size of sizes) {
13
- for (const isDisabled of disabledStates) {
14
- const component = await mount(PieButton,
15
- {
27
+ await expect(component).toContainText(`Hello, ${size} ${variant} Button!`);
28
+ }
29
+ }
30
+ });
31
+ });
32
+
33
+ test('should correctly work with native click events', async ({ mount }) => {
34
+ const messages: string[] = [];
35
+ const expectedEventMessage = 'Native event dispatched';
36
+ const component = await mount(
37
+ PieButton,
38
+ {
16
39
  props: {
17
- size,
18
- variant,
19
- disabled: isDisabled
40
+ size: BUTTON_SIZE.LARGE,
41
+ variant: BUTTON_VARIANT.PRIMARY,
20
42
  },
21
43
  slots: {
22
- default: `Hello, ${size} ${variant} Button!`
44
+ default: 'Click me!',
23
45
  },
24
- });
25
-
26
- await expect(component).toContainText(`Hello, ${size} ${variant} Button!`);
27
- }
28
- }
29
- });
30
- })
31
-
32
- test('should emit an event when clicked', async ({ mount }) => {
33
- const messages: string[] = [];
34
- const component = await mount(PieButton,
35
- {
36
- props: {
37
- size: BUTTON_SIZE.LARGE,
38
- variant: BUTTON_VARIANT.PRIMARY
39
- },
40
- slots: {
41
- default: 'Click me!'
42
- },
43
- on: {
44
- CustomEvent: (data: string) => messages.push(data),
45
- },
46
- });
46
+ on: {
47
+ click: () => {
48
+ messages.push(expectedEventMessage);
49
+ },
50
+ },
51
+ },
52
+ );
47
53
 
48
- await component.click();
54
+ await component.click();
49
55
 
50
- expect(messages).toEqual(['WC event dispatched'])
51
- });
56
+ expect(messages).toEqual([expectedEventMessage]);
57
+ });
@@ -1,31 +1,52 @@
1
1
  import { test } from '@sand4rt/experimental-ct-web';
2
+ import percySnapshot from '@percy/playwright';
2
3
  import { PieButton } from '@/index';
3
- import percySnapshot from '@percy/playwright'
4
4
  import { BUTTON_SIZE, BUTTON_TYPE, BUTTON_VARIANT } from '@/defs';
5
5
 
6
6
  const sizes = Object.values(BUTTON_SIZE);
7
7
  const variants = Object.values(BUTTON_VARIANT);
8
8
  const disabledStates = [true, false];
9
9
 
10
- variants.forEach(variant => {
11
- test(`should render - ${variant}`, async ({ page, mount }) => {
10
+ variants.forEach((variant) => {
11
+ test(`should render - ${variant}`, async ({ page, mount }) => {
12
+ for (const size of sizes) {
13
+ for (const disabledState of disabledStates) {
14
+ await mount(
15
+ PieButton,
16
+ {
17
+ props: {
18
+ type: BUTTON_TYPE.BUTTON,
19
+ size,
20
+ variant,
21
+ disabled: disabledState,
22
+ isFullWidth: false,
23
+ },
24
+ slots: {
25
+ default: `Hello, ${size} ${variant} Button!`,
26
+ },
27
+ },
28
+ );
29
+ }
12
30
 
13
- for (const size of sizes) {
14
- for (const isDisabled of disabledStates) {
15
- await mount(PieButton,
16
- {
17
- props: {
18
- type: BUTTON_TYPE.BUTTON,
19
- size,
20
- variant,
21
- disabled: isDisabled
22
- },
23
- slots: {
24
- default: `Hello, ${size} ${variant} Button!`
25
- },
26
- });
27
- }
28
- }
29
- await percySnapshot(page, `PIE Button - ${variant}`);
30
- });
31
- })
31
+ for (const disabledState of disabledStates) {
32
+ await mount(
33
+ PieButton,
34
+ {
35
+ props: {
36
+ type: BUTTON_TYPE.BUTTON,
37
+ size,
38
+ variant,
39
+ disabled: disabledState,
40
+ isFullWidth: true,
41
+ },
42
+ slots: {
43
+ default: `Hello, ${size} ${variant} Button!`,
44
+ },
45
+ },
46
+ );
47
+ }
48
+ }
49
+
50
+ await percySnapshot(page, `PIE Button - ${variant}`);
51
+ });
52
+ });