@justeattakeaway/pie-modal 0.14.0 → 0.17.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,7 +3,38 @@ import { Variant } from '@justeattakeaway/pie-button/src/defs.ts';
3
3
  export declare const headingLevels: readonly ["h1", "h2", "h3", "h4", "h5", "h6"];
4
4
  export declare const sizes: readonly ["small", "medium", "large"];
5
5
  export declare const positions: readonly ["top", "center"];
6
- export interface ModalProps extends RTLComponentProps {
6
+ export type AriaProps = {
7
+ close?: string;
8
+ back?: string;
9
+ loading?: string;
10
+ };
11
+ export type ActionProps = {
12
+ /**
13
+ * The text to display inside the button.
14
+ */
15
+ text: string;
16
+ /**
17
+ * The button variant.
18
+ */
19
+ variant?: Variant;
20
+ /**
21
+ * The ARIA label for the button.
22
+ */
23
+ ariaLabel?: string;
24
+ };
25
+ export type ModalProps = RTLComponentProps & {
26
+ /**
27
+ * The ARIA labels used for the modal close and back buttons, as well as loading state.
28
+ */
29
+ aria?: AriaProps;
30
+ /**
31
+ * When true, the modal will have a back button. This currently behaves the same as the close button.
32
+ */
33
+ hasBackButton: boolean;
34
+ /**
35
+ * When true, the modal will have a back button. This currently behaves the same as the close button.
36
+ */
37
+ hasStackedActions: boolean;
7
38
  /**
8
39
  * The text to display in the modal's heading.
9
40
  */
@@ -29,17 +60,22 @@ export interface ModalProps extends RTLComponentProps {
29
60
  */
30
61
  isDismissible: boolean;
31
62
  /**
32
- * When true, displays a loading spinner in the modal.
63
+ * When false, the modal footer will scroll with the content inside the modal body.
33
64
  */
34
- isLoading: boolean;
35
- /**
36
- * When true, the modal will have a back button. This currently behaves the same as the close button.
37
- */
38
- hasBackButton: boolean;
65
+ isFooterPinned: boolean;
39
66
  /**
40
67
  * This controls whether a *medium-sized* modal will cover the full width of the page when below the mid breakpoint.
41
68
  */
42
69
  isFullWidthBelowMid: boolean;
70
+ /**
71
+ * When true, displays a loading spinner in the modal.
72
+ */
73
+ isLoading: boolean;
74
+ /**
75
+ * The leading action configuration for the modal.
76
+ */
77
+ leadingAction: ActionProps;
78
+ position: typeof positions[number];
43
79
  /**
44
80
  * The selector for the element that you would like focus to be returned to when the modal is closed, e.g., #skipToMain
45
81
  */
@@ -49,25 +85,10 @@ export interface ModalProps extends RTLComponentProps {
49
85
  */
50
86
  size: typeof sizes[number];
51
87
  /**
52
- * The leading action configuration for the modal.
88
+ * The supporting action configuration for the modal.
53
89
  */
54
- leadingAction: {
55
- /**
56
- * The text to display for the leading action button.
57
- */
58
- text: string;
59
- /**
60
- * The variant of the leading action button.
61
- * Default: 'primary'
62
- */
63
- variant?: Variant;
64
- /**
65
- * The ARIA label for the leading action button.
66
- */
67
- ariaLabel?: string;
68
- };
69
- position: typeof positions[number];
70
- }
90
+ supportingAction: ActionProps;
91
+ };
71
92
  /**
72
93
  * Event name for when the modal is closed.
73
94
  *
@@ -1 +1 @@
1
- {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAElE,eAAO,MAAM,aAAa,+CAAgD,CAAC;AAC3E,eAAO,MAAM,KAAK,uCAAwC,CAAC;AAC3D,eAAO,MAAM,SAAS,4BAA6B,CAAC;AAEpD,MAAM,WAAW,UAAW,SAAQ,iBAAiB;IACjD;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,YAAY,EAAE,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAE3C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;;;;;OAUG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAEvC;;OAEG;IACH,IAAI,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;IAE3B;;OAEG;IACH,aAAa,EAAE;QACX;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QAEb;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;QAElB;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAKF,QAAQ,EAAE,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAC"}
1
+ {"version":3,"file":"defs.d.ts","sourceRoot":"","sources":["../../../src/defs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAElE,eAAO,MAAM,aAAa,+CAAgD,CAAC;AAC3E,eAAO,MAAM,KAAK,uCAAwC,CAAC;AAC3D,eAAO,MAAM,SAAS,4BAA6B,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IAClB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG;IACzC;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,YAAY,EAAE,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAE3C;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;;;;;OAUG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,aAAa,EAAE,WAAW,CAAC;IAK3B,QAAQ,EAAE,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;IAEnC;;OAEG;IACH,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAEvC;;OAEG;IACH,IAAI,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;IAE3B;;OAEG;IACH,gBAAgB,EAAE,WAAW,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAC"}
@@ -1,7 +1,6 @@
1
1
  import { LitElement, TemplateResult } from 'lit';
2
2
  import type { DependentMap } from '@justeattakeaway/pie-webc-core';
3
- import { Variant } from '@justeattakeaway/pie-button/src/defs.ts';
4
- import { ModalProps, headingLevels, sizes } from './defs';
3
+ import { type AriaProps, type ActionProps, type ModalProps, headingLevels, sizes } from './defs';
5
4
  export { type ModalProps, headingLevels, sizes };
6
5
  declare const componentSelector = "pie-modal";
7
6
  declare const PieModal_base: (new (...args: any[]) => {
@@ -14,21 +13,21 @@ declare const PieModal_base: (new (...args: any[]) => {
14
13
  * @event {CustomEvent} pie-modal-back - when the modal back button is clicked.
15
14
  */
16
15
  export declare class PieModal extends PieModal_base implements ModalProps {
16
+ aria: AriaProps;
17
17
  heading: string;
18
18
  headingLevel: ModalProps['headingLevel'];
19
- isDismissible: boolean;
20
19
  hasBackButton: boolean;
20
+ hasStackedActions: boolean;
21
+ isDismissible: boolean;
22
+ isFooterPinned: boolean;
21
23
  isFullWidthBelowMid: boolean;
22
- isOpen: boolean;
23
24
  isLoading: boolean;
25
+ isOpen: boolean;
26
+ leadingAction: ActionProps;
27
+ position: ModalProps['position'];
24
28
  returnFocusAfterCloseSelector?: string;
25
29
  size: ModalProps['size'];
26
- leadingAction: {
27
- text: string;
28
- variant?: Variant;
29
- ariaLabel?: string;
30
- };
31
- position: ModalProps['position'];
30
+ supportingAction: ActionProps;
32
31
  private _dialog?;
33
32
  private _backButtonClicked;
34
33
  static styles: import("lit").CSSResult;
@@ -83,6 +82,23 @@ export declare class PieModal extends PieModal_base implements ModalProps {
83
82
  * @private
84
83
  */
85
84
  private renderLeadingAction;
85
+ /**
86
+ * Render supportingAction button depending on prop availability.
87
+ *
88
+ * 1. If the prop `supportingAction` is not provided, the button is not rendered.
89
+ * 2. If the prop `supportingAction` is provided but any of the optional properties
90
+ * are not provided, they fall back to their default values.
91
+ * 3. If `supportingAction` is provided but not `leadingAction`, log a warning and do
92
+ * not render `supportingAction`.
93
+ *
94
+ * @private
95
+ */
96
+ private renderSupportingAction;
97
+ /**
98
+ * Renders the modal inner content and footer of the modal.
99
+ * @private
100
+ */
101
+ private renderModalContentAndFooter;
86
102
  render(): TemplateResult;
87
103
  /**
88
104
  * Dismisses the modal on backdrop click if `isDismissible` is `true`.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EAAW,cAAc,EACtC,MAAM,KAAK,CAAC;AAMb,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,iDAAiD,CAAC;AACzD,OAAO,uDAAuD,CAAC;AAC/D,OAAO,wDAAwD,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAGlE,OAAO,EACH,UAAU,EACV,aAAa,EAIb,KAAK,EAER,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAEjD,QAAA,MAAM,iBAAiB,cAAc,CAAC;;;;;AAEtC;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,aAAqB,YAAW,UAAU;IAG7D,OAAO,EAAG,MAAM,CAAC;IAIjB,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,CAAQ;IAGhD,aAAa,UAAS;IAGtB,aAAa,UAAS;IAGtB,mBAAmB,UAAS;IAG5B,MAAM,UAAS;IAGf,SAAS,UAAS;IAGlB,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAIvC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAY;IAGpC,aAAa,EAAG;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAGK,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,CAAY;IAGnD,OAAO,CAAC,OAAO,CAAC,CAAoB;IAEpC,OAAO,CAAC,kBAAkB,CAAS;IAGnC,MAAM,CAAC,MAAM,0BAAqB;;IAOlC,iBAAiB,IAAM,IAAI;IAO3B,oBAAoB,IAAM,IAAI;IAO9B,YAAY,CAAE,iBAAiB,EAAE,YAAY,CAAC,UAAU,CAAC,GAAI,IAAI;IASjE,OAAO,CAAE,iBAAiB,EAAE,YAAY,CAAC,UAAU,CAAC,GAAI,IAAI;IAI5D;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAI9B;IAGF,OAAO,CAAC,kCAAkC;IAU1C,OAAO,CAAC,4BAA4B;IAkBpC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IAmBpB,MAAM;IAoDb;;;OAGG;IACH,OAAO,CAAC,yBAAyB,CAyB/B;IAEF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,yBAAyB,CAO/B;CACL;AAID,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC;KACjC;CACJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EAAW,cAAc,EACtC,MAAM,KAAK,CAAC;AAMb,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,yDAAyD,CAAC;AACjE,OAAO,+DAA+D,CAAC;AACvE,OAAO,gEAAgE,CAAC;AAKxE,OAAO,EACH,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,aAAa,EAEb,KAAK,EAIR,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAEjD,QAAA,MAAM,iBAAiB,cAAc,CAAC;;;;;AAEtC;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,aAAqB,YAAW,UAAU;IAE7D,IAAI,EAAG,SAAS,CAAC;IAIjB,OAAO,EAAG,MAAM,CAAC;IAIjB,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,CAAQ;IAGhD,aAAa,UAAS;IAGtB,iBAAiB,UAAS;IAG1B,aAAa,UAAS;IAGtB,cAAc,UAAQ;IAGtB,mBAAmB,UAAS;IAG5B,SAAS,UAAS;IAGlB,MAAM,UAAS;IAGf,aAAa,EAAG,WAAW,CAAC;IAI5B,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,CAAY;IAG5C,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAIvC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAY;IAGpC,gBAAgB,EAAG,WAAW,CAAC;IAGtC,OAAO,CAAC,OAAO,CAAC,CAAoB;IAEpC,OAAO,CAAC,kBAAkB,CAAS;IAGnC,MAAM,CAAC,MAAM,0BAAqB;;IAOlC,iBAAiB,IAAM,IAAI;IAO3B,oBAAoB,IAAM,IAAI;IAO9B,YAAY,CAAE,iBAAiB,EAAE,YAAY,CAAC,UAAU,CAAC,GAAI,IAAI;IAYjE,OAAO,CAAE,iBAAiB,EAAE,YAAY,CAAC,UAAU,CAAC,GAAI,IAAI;IAI5D;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAI9B;IAGF,OAAO,CAAC,kCAAkC;IAU1C,OAAO,CAAC,4BAA4B;IAkBpC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IAoB3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,sBAAsB;IAyB9B;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAa5B,MAAM;IAuDb;;;OAGG;IACH,OAAO,CAAC,yBAAyB,CAyB/B;IAEF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,yBAAyB,CAO/B;CACL;AAID,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC;KACjC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-modal",
3
- "version": "0.14.0",
3
+ "version": "0.17.0",
4
4
  "description": "PIE design system modal built using web components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,6 +8,8 @@
8
8
  "types": "dist/types/index.d.ts",
9
9
  "scripts": {
10
10
  "build": "yarn build:wrapper pie-modal && run -T vite build",
11
+ "lint:scripts": "run -T eslint .",
12
+ "lint:scripts:fix": "run -T eslint . --fix",
11
13
  "lint:style": "run -T stylelint ./src/**/*.{css,scss}",
12
14
  "lint:style:fix": "yarn lint:style --fix",
13
15
  "watch": "run -T vite build --watch",
@@ -33,5 +35,8 @@
33
35
  },
34
36
  "volta": {
35
37
  "extends": "../../../package.json"
38
+ },
39
+ "dependencies": {
40
+ "dialog-polyfill": "0.5.6"
36
41
  }
37
42
  }
@@ -1 +1 @@
1
- //Import common styles here
1
+ // Import common styles here
@@ -1,5 +1,4 @@
1
1
  import { defineConfig } from '@sand4rt/experimental-ct-web';
2
2
  import { getPlaywrightVisualConfig } from '@justeattakeaway/pie-components-config';
3
3
 
4
- // @ts-ignore
5
4
  export default defineConfig(getPlaywrightVisualConfig());
@@ -1,5 +1,4 @@
1
1
  import { defineConfig } from '@sand4rt/experimental-ct-web';
2
- import { getPlaywrightConfig } from '@justeattakeaway/pie-components-config'
2
+ import { getPlaywrightConfig } from '@justeattakeaway/pie-components-config';
3
3
 
4
- // @ts-ignore
5
4
  export default defineConfig(getPlaywrightConfig());
package/src/defs.ts CHANGED
@@ -5,7 +5,45 @@ export const headingLevels = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
5
5
  export const sizes = ['small', 'medium', 'large'] as const;
6
6
  export const positions = ['top', 'center'] as const;
7
7
 
8
- export interface ModalProps extends RTLComponentProps {
8
+ export type AriaProps = {
9
+ close?: string;
10
+ back?: string;
11
+ loading?: string;
12
+ };
13
+
14
+ export type ActionProps = {
15
+ /**
16
+ * The text to display inside the button.
17
+ */
18
+ text: string;
19
+
20
+ /**
21
+ * The button variant.
22
+ */
23
+ variant?: Variant;
24
+
25
+ /**
26
+ * The ARIA label for the button.
27
+ */
28
+ ariaLabel?: string;
29
+ };
30
+
31
+ export type ModalProps = RTLComponentProps & {
32
+ /**
33
+ * The ARIA labels used for the modal close and back buttons, as well as loading state.
34
+ */
35
+ aria?: AriaProps;
36
+
37
+ /**
38
+ * When true, the modal will have a back button. This currently behaves the same as the close button.
39
+ */
40
+ hasBackButton: boolean;
41
+
42
+ /**
43
+ * When true, the modal will have a back button. This currently behaves the same as the close button.
44
+ */
45
+ hasStackedActions: boolean;
46
+
9
47
  /**
10
48
  * The text to display in the modal's heading.
11
49
  */
@@ -34,20 +72,30 @@ export interface ModalProps extends RTLComponentProps {
34
72
  */
35
73
  isDismissible: boolean;
36
74
 
75
+ /**
76
+ * When false, the modal footer will scroll with the content inside the modal body.
77
+ */
78
+ isFooterPinned: boolean;
79
+
80
+ /**
81
+ * This controls whether a *medium-sized* modal will cover the full width of the page when below the mid breakpoint.
82
+ */
83
+ isFullWidthBelowMid: boolean;
84
+
37
85
  /**
38
86
  * When true, displays a loading spinner in the modal.
39
87
  */
40
88
  isLoading: boolean;
41
89
 
42
90
  /**
43
- * When true, the modal will have a back button. This currently behaves the same as the close button.
91
+ * The leading action configuration for the modal.
44
92
  */
45
- hasBackButton: boolean;
93
+ leadingAction: ActionProps;
46
94
 
47
- /**
48
- * This controls whether a *medium-sized* modal will cover the full width of the page when below the mid breakpoint.
95
+ /*
96
+ * The position of the modal; this controls where it will appear on the page.
49
97
  */
50
- isFullWidthBelowMid: boolean;
98
+ position: typeof positions[number];
51
99
 
52
100
  /**
53
101
  * The selector for the element that you would like focus to be returned to when the modal is closed, e.g., #skipToMain
@@ -60,31 +108,10 @@ export interface ModalProps extends RTLComponentProps {
60
108
  size: typeof sizes[number];
61
109
 
62
110
  /**
63
- * The leading action configuration for the modal.
111
+ * The supporting action configuration for the modal.
64
112
  */
65
- leadingAction: {
66
- /**
67
- * The text to display for the leading action button.
68
- */
69
- text: string;
70
-
71
- /**
72
- * The variant of the leading action button.
73
- * Default: 'primary'
74
- */
75
- variant?: Variant;
76
-
77
- /**
78
- * The ARIA label for the leading action button.
79
- */
80
- ariaLabel?: string;
81
- };
82
-
83
- /*
84
- * The position of the modal; this controls where it will appear on the page.
85
- */
86
- position: typeof positions[number];
87
- }
113
+ supportingAction: ActionProps;
114
+ };
88
115
 
89
116
  /**
90
117
  * Event name for when the modal is closed.
package/src/index.ts CHANGED
@@ -4,24 +4,27 @@ import {
4
4
  import { html, unsafeStatic } from 'lit/static-html.js';
5
5
  import { property, query } from 'lit/decorators.js';
6
6
  import {
7
- RtlMixin, validPropertyValues, requiredProperty,
7
+ requiredProperty, RtlMixin, validPropertyValues,
8
8
  } from '@justeattakeaway/pie-webc-core';
9
9
  import type { DependentMap } from '@justeattakeaway/pie-webc-core';
10
10
  import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
11
- import '@justeattakeaway/pie-icons-webc/icons/IconClose';
12
- import '@justeattakeaway/pie-icons-webc/icons/IconChevronLeft';
13
- import '@justeattakeaway/pie-icons-webc/icons/IconChevronRight';
14
- import { Variant } from '@justeattakeaway/pie-button/src/defs.ts';
11
+ import '@justeattakeaway/pie-icons-webc/dist/icons/IconClose.js';
12
+ import '@justeattakeaway/pie-icons-webc/dist/icons/IconChevronLeft.js';
13
+ import '@justeattakeaway/pie-icons-webc/dist/icons/IconChevronRight.js';
14
+
15
+ import dialogPolyfill from 'dialog-polyfill';
15
16
 
16
17
  import styles from './modal.scss?inline';
17
18
  import {
18
- ModalProps,
19
+ type AriaProps,
20
+ type ActionProps,
21
+ type ModalProps,
19
22
  headingLevels,
23
+ positions,
24
+ sizes,
25
+ ON_MODAL_BACK_EVENT,
20
26
  ON_MODAL_CLOSE_EVENT,
21
27
  ON_MODAL_OPEN_EVENT,
22
- ON_MODAL_BACK_EVENT,
23
- sizes,
24
- positions,
25
28
  } from './defs';
26
29
 
27
30
  // Valid values available to consumers
@@ -35,6 +38,9 @@ const componentSelector = 'pie-modal';
35
38
  * @event {CustomEvent} pie-modal-back - when the modal back button is clicked.
36
39
  */
37
40
  export class PieModal extends RtlMixin(LitElement) implements ModalProps {
41
+ @property({ type: Object })
42
+ public aria!: AriaProps;
43
+
38
44
  @property({ type: String })
39
45
  @requiredProperty(componentSelector)
40
46
  public heading!: string;
@@ -43,20 +49,33 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
43
49
  @validPropertyValues(componentSelector, headingLevels, 'h2')
44
50
  public headingLevel: ModalProps['headingLevel'] = 'h2';
45
51
 
52
+ @property({ type: Boolean })
53
+ public hasBackButton = false;
54
+
55
+ @property({ type: Boolean })
56
+ public hasStackedActions = false;
57
+
46
58
  @property({ type: Boolean, reflect: true })
47
59
  public isDismissible = false;
48
60
 
49
61
  @property({ type: Boolean })
50
- public hasBackButton = false;
62
+ public isFooterPinned = true;
51
63
 
52
64
  @property({ type: Boolean })
53
65
  public isFullWidthBelowMid = false;
54
66
 
67
+ @property({ type: Boolean, reflect: true })
68
+ public isLoading = false;
69
+
55
70
  @property({ type: Boolean })
56
71
  public isOpen = false;
57
72
 
58
- @property({ type: Boolean, reflect: true })
59
- public isLoading = false;
73
+ @property({ type: Object })
74
+ public leadingAction!: ActionProps;
75
+
76
+ @property()
77
+ @validPropertyValues(componentSelector, positions, 'center')
78
+ public position: ModalProps['position'] = 'center';
60
79
 
61
80
  @property()
62
81
  public returnFocusAfterCloseSelector?: string;
@@ -65,15 +84,8 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
65
84
  @validPropertyValues(componentSelector, sizes, 'medium')
66
85
  public size: ModalProps['size'] = 'medium';
67
86
 
68
- @property()
69
- public leadingAction!: {
70
- text: string;
71
- variant?: Variant;
72
- ariaLabel?: string;
73
- };
74
-
75
- @validPropertyValues(componentSelector, positions, 'center')
76
- public position: ModalProps['position'] = 'center';
87
+ @property({ type: Object })
88
+ public supportingAction!: ActionProps;
77
89
 
78
90
  @query('dialog')
79
91
  private _dialog?: HTMLDialogElement;
@@ -103,12 +115,15 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
103
115
  }
104
116
 
105
117
  firstUpdated (changedProperties: DependentMap<ModalProps>) : void {
106
- this._dialog?.addEventListener('cancel', (event) => this._handleDialogCancelEvent(event));
107
- this._handleModalOpenStateOnFirstRender(changedProperties);
118
+ if (this._dialog) {
119
+ dialogPolyfill.registerDialog(this._dialog);
120
+ this._dialog.addEventListener('cancel', (event) => this._handleDialogCancelEvent(event));
121
+ this._dialog.addEventListener('close', () => {
122
+ this.isOpen = false;
123
+ });
124
+ }
108
125
 
109
- this._dialog?.addEventListener('close', () => {
110
- this.isOpen = false;
111
- });
126
+ this._handleModalOpenStateOnFirstRender(changedProperties);
112
127
  }
113
128
 
114
129
  updated (changedProperties: DependentMap<ModalProps>) : void {
@@ -201,8 +216,10 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
201
216
  @click="${() => { this.isOpen = false; }}"
202
217
  variant="ghost-secondary"
203
218
  class="c-modal-closeBtn"
204
- data-test-id="modal-close-button"><icon-close /></pie-icon-button>
205
- `;
219
+ aria-label="${this.aria?.close || nothing}"
220
+ data-test-id="modal-close-button">
221
+ <icon-close></icon-close>
222
+ </pie-icon-button>`;
206
223
  }
207
224
 
208
225
  /**
@@ -217,8 +234,9 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
217
234
  @click="${() => { this._backButtonClicked = true; this.isOpen = false; }}"
218
235
  variant="ghost-secondary"
219
236
  class="c-modal-backBtn"
237
+ aria-label="${this.aria?.back || nothing}"
220
238
  data-test-id="modal-back-button">
221
- ${this.isRTL ? html`<icon-chevron-right />` : html`<icon-chevron-left />`}
239
+ ${this.isRTL ? html`<icon-chevron-right></icon-chevron-right>` : html`<icon-chevron-left></icon-chevron-left>`}
222
240
  </pie-icon-button>
223
241
  `;
224
242
  }
@@ -232,8 +250,8 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
232
250
  *
233
251
  * @private
234
252
  */
235
- private renderLeadingAction (): TemplateResult | typeof nothing {
236
- const { text = 'Confirm', variant = 'primary', ariaLabel } = this.leadingAction;
253
+ private renderLeadingAction () : TemplateResult | typeof nothing {
254
+ const { text, variant = 'primary', ariaLabel } = this.leadingAction;
237
255
 
238
256
  if (!text) {
239
257
  return nothing;
@@ -241,27 +259,85 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
241
259
 
242
260
  return html`
243
261
  <pie-button
244
- variant="${variant}"
245
- aria-label="${ariaLabel || nothing}"
246
- type="submit"
247
- @click="${() => this._dialog?.close('leading')}"
248
- data-test-id="modal-leading-action">
262
+ variant="${variant}"
263
+ aria-label="${ariaLabel || nothing}"
264
+ type="submit"
265
+ ?isFullWidth="${this.hasStackedActions}"
266
+ @click="${() => this._dialog?.close('leading')}"
267
+ data-test-id="modal-leading-action">
249
268
  ${text}
250
- </pie-button>
269
+ </pie-button>
251
270
  `;
252
271
  }
253
272
 
273
+ /**
274
+ * Render supportingAction button depending on prop availability.
275
+ *
276
+ * 1. If the prop `supportingAction` is not provided, the button is not rendered.
277
+ * 2. If the prop `supportingAction` is provided but any of the optional properties
278
+ * are not provided, they fall back to their default values.
279
+ * 3. If `supportingAction` is provided but not `leadingAction`, log a warning and do
280
+ * not render `supportingAction`.
281
+ *
282
+ * @private
283
+ */
284
+ private renderSupportingAction (): TemplateResult | typeof nothing {
285
+ const { text, variant = 'ghost', ariaLabel } = this.supportingAction;
286
+
287
+ if (!text) {
288
+ return nothing;
289
+ }
290
+
291
+ if (!this.leadingAction) {
292
+ console.warn('Use `leadingAction` instead of `supportingAction`. `supportingAction` is being ignored.');
293
+ return nothing;
294
+ }
295
+
296
+ return html`
297
+ <pie-button
298
+ variant="${variant}"
299
+ aria-label="${ariaLabel || nothing}"
300
+ type="reset"
301
+ ?isFullWidth="${this.hasStackedActions}"
302
+ @click="${() => this._dialog?.close('supporting')}"
303
+ data-test-id="modal-supporting-action">
304
+ ${text}
305
+ </pie-button>
306
+ `;
307
+ }
308
+
309
+ /**
310
+ * Renders the modal inner content and footer of the modal.
311
+ * @private
312
+ */
313
+ private renderModalContentAndFooter (): TemplateResult {
314
+ return html`
315
+ <article class="c-modal-content c-modal-content--scrollable">
316
+ <div class="c-modal-contentInner">
317
+ <slot></slot>
318
+ </div>
319
+ </article>
320
+ <footer class="c-modal-footer">
321
+ ${this.leadingAction ? this.renderLeadingAction() : nothing}
322
+ ${this.supportingAction ? this.renderSupportingAction() : nothing}
323
+ </footer>`;
324
+ }
325
+
254
326
  public render () {
255
327
  const {
328
+ aria,
256
329
  hasBackButton,
330
+ hasStackedActions,
257
331
  heading,
258
332
  headingLevel = 'h2',
259
333
  isDismissible,
334
+ isFooterPinned,
260
335
  isFullWidthBelowMid,
261
336
  isLoading,
262
- size,
263
337
  leadingAction,
264
338
  position,
339
+ size,
340
+ supportingAction,
265
341
  } = this;
266
342
 
267
343
  const headingTag = unsafeStatic(headingLevel);
@@ -270,13 +346,17 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
270
346
  <dialog
271
347
  id="dialog"
272
348
  class="c-modal"
273
- data-test-id="pie-modal"
274
349
  size="${size}"
275
350
  position="${position}"
351
+ ?hasActions=${leadingAction || supportingAction}
276
352
  ?hasBackButton=${hasBackButton}
353
+ ?hasStackedActions=${hasStackedActions}
277
354
  ?isDismissible=${isDismissible}
355
+ ?isFooterPinned=${isFooterPinned}
278
356
  ?isFullWidthBelowMid=${isFullWidthBelowMid}
279
357
  ?isLoading=${isLoading}
358
+ aria-busy="${isLoading ? 'true' : 'false'}"
359
+ aria-label="${(isLoading && aria?.loading) || nothing}"
280
360
  data-test-id="pie-modal">
281
361
  <header class="c-modal-header">
282
362
  ${hasBackButton ? this.renderBackButton() : nothing}
@@ -285,21 +365,16 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
285
365
  </${headingTag}>
286
366
  ${isDismissible ? this.renderCloseButton() : nothing}
287
367
  </header>
288
- <article class="c-modal-content c-modal-content--scrollable">
289
- <div class="c-modal-contentInner">
290
- <slot></slot>
291
- </div>
292
- </article>
293
- <footer class="c-modal-footer">
294
- ${leadingAction ? this.renderLeadingAction() : nothing}
295
- <pie-button
296
- variant="ghost"
297
- type="reset"
298
- @click="${() => this._dialog?.close('supporting')}"
299
- data-test-id="modal-supporting-action">
300
- Cancel
301
- </pie-button>
302
- </footer>
368
+ ${
369
+ // We need to wrap the remaining content in a shared scrollable container if the footer is not pinned
370
+ isFooterPinned
371
+ ? this.renderModalContentAndFooter()
372
+ : html`
373
+ <div class="c-modal-scrollContainer">
374
+ ${this.renderModalContentAndFooter()}
375
+ </div>
376
+ `
377
+ }
303
378
  </dialog>`;
304
379
  }
305
380
 
@@ -325,9 +400,9 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
325
400
  }
326
401
 
327
402
  const isClickOutsideDialog = event.clientY < top ||
328
- event.clientY > bottom ||
329
- event.clientX < left ||
330
- event.clientX > right;
403
+ event.clientY > bottom ||
404
+ event.clientX < left ||
405
+ event.clientX > right;
331
406
 
332
407
  if (isClickOutsideDialog) {
333
408
  this.isOpen = false;