@justeattakeaway/pie-modal 0.13.0 → 0.16.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.js CHANGED
File without changes
File without changes
@@ -1,7 +1,40 @@
1
1
  import { RTLComponentProps } from '@justeattakeaway/pie-webc-core';
2
+ import { Variant } from '@justeattakeaway/pie-button/src/defs.ts';
2
3
  export declare const headingLevels: readonly ["h1", "h2", "h3", "h4", "h5", "h6"];
3
4
  export declare const sizes: readonly ["small", "medium", "large"];
4
- export interface ModalProps extends RTLComponentProps {
5
+ export declare const positions: readonly ["top", "center"];
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;
5
38
  /**
6
39
  * The text to display in the modal's heading.
7
40
  */
@@ -27,17 +60,22 @@ export interface ModalProps extends RTLComponentProps {
27
60
  */
28
61
  isDismissible: boolean;
29
62
  /**
30
- * When true, displays a loading spinner in the modal.
63
+ * When false, the modal footer will scroll with the content inside the modal body.
31
64
  */
32
- isLoading: boolean;
33
- /**
34
- * When true, the modal will have a back button. This currently behaves the same as the close button.
35
- */
36
- hasBackButton: boolean;
65
+ isFooterPinned: boolean;
37
66
  /**
38
67
  * This controls whether a *medium-sized* modal will cover the full width of the page when below the mid breakpoint.
39
68
  */
40
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];
41
79
  /**
42
80
  * The selector for the element that you would like focus to be returned to when the modal is closed, e.g., #skipToMain
43
81
  */
@@ -46,7 +84,11 @@ export interface ModalProps extends RTLComponentProps {
46
84
  * The size of the modal; this controls how wide it will appear on the page.
47
85
  */
48
86
  size: typeof sizes[number];
49
- }
87
+ /**
88
+ * The supporting action configuration for the modal.
89
+ */
90
+ supportingAction: ActionProps;
91
+ };
50
92
  /**
51
93
  * Event name for when the modal is closed.
52
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;AAEnE,eAAO,MAAM,aAAa,+CAAgD,CAAC;AAC3E,eAAO,MAAM,KAAK,uCAAwC,CAAC;AAE3D,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;CAC9B;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,6 +1,6 @@
1
1
  import { LitElement, TemplateResult } from 'lit';
2
2
  import type { DependentMap } from '@justeattakeaway/pie-webc-core';
3
- import { ModalProps, headingLevels, sizes } from './defs';
3
+ import { type AriaProps, type ActionProps, type ModalProps, headingLevels, sizes } from './defs';
4
4
  export { type ModalProps, headingLevels, sizes };
5
5
  declare const componentSelector = "pie-modal";
6
6
  declare const PieModal_base: (new (...args: any[]) => {
@@ -13,15 +13,21 @@ declare const PieModal_base: (new (...args: any[]) => {
13
13
  * @event {CustomEvent} pie-modal-back - when the modal back button is clicked.
14
14
  */
15
15
  export declare class PieModal extends PieModal_base implements ModalProps {
16
+ aria: AriaProps;
16
17
  heading: string;
17
18
  headingLevel: ModalProps['headingLevel'];
18
- isDismissible: boolean;
19
19
  hasBackButton: boolean;
20
+ hasStackedActions: boolean;
21
+ isDismissible: boolean;
22
+ isFooterPinned: boolean;
20
23
  isFullWidthBelowMid: boolean;
21
- isOpen: boolean;
22
24
  isLoading: boolean;
25
+ isOpen: boolean;
26
+ leadingAction: ActionProps;
27
+ position: ModalProps['position'];
23
28
  returnFocusAfterCloseSelector?: string;
24
29
  size: ModalProps['size'];
30
+ supportingAction: ActionProps;
25
31
  private _dialog?;
26
32
  private _backButtonClicked;
27
33
  static styles: import("lit").CSSResult;
@@ -66,6 +72,33 @@ export declare class PieModal extends PieModal_base implements ModalProps {
66
72
  * @private
67
73
  */
68
74
  private renderBackButton;
75
+ /**
76
+ * Render leadingAction button depending on prop availability.
77
+ *
78
+ * 1. If the prop `leadingAction` is not provided, the button is not rendered.
79
+ * 2. If the prop `leadingAction` is provided but any of the optional properties
80
+ * are not provided, they fall back to their default values.
81
+ *
82
+ * @private
83
+ */
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;
69
102
  render(): TemplateResult;
70
103
  /**
71
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;AAGhE,OAAO,EACH,UAAU,EACV,aAAa,EAIb,KAAK,EACR,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;IAG3C,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;IAYjB,MAAM;IAsDb;;;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;AAGxE,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;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;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"}
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-modal",
3
- "version": "0.13.0",
3
+ "version": "0.16.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",
@@ -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
@@ -1,9 +1,49 @@
1
1
  import { RTLComponentProps } from '@justeattakeaway/pie-webc-core';
2
+ import { Variant } from '@justeattakeaway/pie-button/src/defs.ts';
2
3
 
3
4
  export const headingLevels = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const;
4
5
  export const sizes = ['small', 'medium', 'large'] as const;
6
+ export const positions = ['top', 'center'] as const;
7
+
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;
5
46
 
6
- export interface ModalProps extends RTLComponentProps {
7
47
  /**
8
48
  * The text to display in the modal's heading.
9
49
  */
@@ -32,20 +72,30 @@ export interface ModalProps extends RTLComponentProps {
32
72
  */
33
73
  isDismissible: boolean;
34
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
+
35
85
  /**
36
86
  * When true, displays a loading spinner in the modal.
37
87
  */
38
88
  isLoading: boolean;
39
89
 
40
90
  /**
41
- * 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.
42
92
  */
43
- hasBackButton: boolean;
93
+ leadingAction: ActionProps;
44
94
 
45
- /**
46
- * 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.
47
97
  */
48
- isFullWidthBelowMid: boolean;
98
+ position: typeof positions[number];
49
99
 
50
100
  /**
51
101
  * The selector for the element that you would like focus to be returned to when the modal is closed, e.g., #skipToMain
@@ -56,7 +106,12 @@ export interface ModalProps extends RTLComponentProps {
56
106
  * The size of the modal; this controls how wide it will appear on the page.
57
107
  */
58
108
  size: typeof sizes[number];
59
- }
109
+
110
+ /**
111
+ * The supporting action configuration for the modal.
112
+ */
113
+ supportingAction: ActionProps;
114
+ };
60
115
 
61
116
  /**
62
117
  * Event name for when the modal is closed.
package/src/index.ts CHANGED
@@ -4,22 +4,25 @@ 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';
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
14
 
15
15
  import styles from './modal.scss?inline';
16
16
  import {
17
- ModalProps,
17
+ type AriaProps,
18
+ type ActionProps,
19
+ type ModalProps,
18
20
  headingLevels,
21
+ positions,
22
+ sizes,
23
+ ON_MODAL_BACK_EVENT,
19
24
  ON_MODAL_CLOSE_EVENT,
20
25
  ON_MODAL_OPEN_EVENT,
21
- ON_MODAL_BACK_EVENT,
22
- sizes,
23
26
  } from './defs';
24
27
 
25
28
  // Valid values available to consumers
@@ -33,6 +36,9 @@ const componentSelector = 'pie-modal';
33
36
  * @event {CustomEvent} pie-modal-back - when the modal back button is clicked.
34
37
  */
35
38
  export class PieModal extends RtlMixin(LitElement) implements ModalProps {
39
+ @property({ type: Object })
40
+ public aria!: AriaProps;
41
+
36
42
  @property({ type: String })
37
43
  @requiredProperty(componentSelector)
38
44
  public heading!: string;
@@ -41,20 +47,33 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
41
47
  @validPropertyValues(componentSelector, headingLevels, 'h2')
42
48
  public headingLevel: ModalProps['headingLevel'] = 'h2';
43
49
 
50
+ @property({ type: Boolean })
51
+ public hasBackButton = false;
52
+
53
+ @property({ type: Boolean })
54
+ public hasStackedActions = false;
55
+
44
56
  @property({ type: Boolean, reflect: true })
45
57
  public isDismissible = false;
46
58
 
47
59
  @property({ type: Boolean })
48
- public hasBackButton = false;
60
+ public isFooterPinned = true;
49
61
 
50
62
  @property({ type: Boolean })
51
63
  public isFullWidthBelowMid = false;
52
64
 
65
+ @property({ type: Boolean, reflect: true })
66
+ public isLoading = false;
67
+
53
68
  @property({ type: Boolean })
54
69
  public isOpen = false;
55
70
 
56
- @property({ type: Boolean, reflect: true })
57
- public isLoading = false;
71
+ @property({ type: Object })
72
+ public leadingAction!: ActionProps;
73
+
74
+ @property()
75
+ @validPropertyValues(componentSelector, positions, 'center')
76
+ public position: ModalProps['position'] = 'center';
58
77
 
59
78
  @property()
60
79
  public returnFocusAfterCloseSelector?: string;
@@ -63,6 +82,9 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
63
82
  @validPropertyValues(componentSelector, sizes, 'medium')
64
83
  public size: ModalProps['size'] = 'medium';
65
84
 
85
+ @property({ type: Object })
86
+ public supportingAction!: ActionProps;
87
+
66
88
  @query('dialog')
67
89
  private _dialog?: HTMLDialogElement;
68
90
 
@@ -189,8 +211,10 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
189
211
  @click="${() => { this.isOpen = false; }}"
190
212
  variant="ghost-secondary"
191
213
  class="c-modal-closeBtn"
192
- data-test-id="modal-close-button"><icon-close /></pie-icon-button>
193
- `;
214
+ aria-label="${this.aria?.close || nothing}"
215
+ data-test-id="modal-close-button">
216
+ <icon-close></icon-close>
217
+ </pie-icon-button>`;
194
218
  }
195
219
 
196
220
  /**
@@ -205,21 +229,110 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
205
229
  @click="${() => { this._backButtonClicked = true; this.isOpen = false; }}"
206
230
  variant="ghost-secondary"
207
231
  class="c-modal-backBtn"
232
+ aria-label="${this.aria?.back || nothing}"
208
233
  data-test-id="modal-back-button">
209
- ${this.isRTL ? html`<icon-chevron-right />` : html`<icon-chevron-left />`}
234
+ ${this.isRTL ? html`<icon-chevron-right></icon-chevron-right>` : html`<icon-chevron-left></icon-chevron-left>`}
210
235
  </pie-icon-button>
211
236
  `;
212
237
  }
213
238
 
239
+ /**
240
+ * Render leadingAction button depending on prop availability.
241
+ *
242
+ * 1. If the prop `leadingAction` is not provided, the button is not rendered.
243
+ * 2. If the prop `leadingAction` is provided but any of the optional properties
244
+ * are not provided, they fall back to their default values.
245
+ *
246
+ * @private
247
+ */
248
+ private renderLeadingAction () : TemplateResult | typeof nothing {
249
+ const { text, variant = 'primary', ariaLabel } = this.leadingAction;
250
+
251
+ if (!text) {
252
+ return nothing;
253
+ }
254
+
255
+ return html`
256
+ <pie-button
257
+ variant="${variant}"
258
+ aria-label="${ariaLabel || nothing}"
259
+ type="submit"
260
+ ?isFullWidth="${this.hasStackedActions}"
261
+ @click="${() => this._dialog?.close('leading')}"
262
+ data-test-id="modal-leading-action">
263
+ ${text}
264
+ </pie-button>
265
+ `;
266
+ }
267
+
268
+ /**
269
+ * Render supportingAction button depending on prop availability.
270
+ *
271
+ * 1. If the prop `supportingAction` is not provided, the button is not rendered.
272
+ * 2. If the prop `supportingAction` is provided but any of the optional properties
273
+ * are not provided, they fall back to their default values.
274
+ * 3. If `supportingAction` is provided but not `leadingAction`, log a warning and do
275
+ * not render `supportingAction`.
276
+ *
277
+ * @private
278
+ */
279
+ private renderSupportingAction (): TemplateResult | typeof nothing {
280
+ const { text, variant = 'ghost', ariaLabel } = this.supportingAction;
281
+
282
+ if (!text) {
283
+ return nothing;
284
+ }
285
+
286
+ if (!this.leadingAction) {
287
+ console.warn('Use `leadingAction` instead of `supportingAction`. `supportingAction` is being ignored.');
288
+ return nothing;
289
+ }
290
+
291
+ return html`
292
+ <pie-button
293
+ variant="${variant}"
294
+ aria-label="${ariaLabel || nothing}"
295
+ type="reset"
296
+ ?isFullWidth="${this.hasStackedActions}"
297
+ @click="${() => this._dialog?.close('supporting')}"
298
+ data-test-id="modal-supporting-action">
299
+ ${text}
300
+ </pie-button>
301
+ `;
302
+ }
303
+
304
+ /**
305
+ * Renders the modal inner content and footer of the modal.
306
+ * @private
307
+ */
308
+ private renderModalContentAndFooter (): TemplateResult {
309
+ return html`
310
+ <article class="c-modal-content c-modal-content--scrollable">
311
+ <div class="c-modal-contentInner">
312
+ <slot></slot>
313
+ </div>
314
+ </article>
315
+ <footer class="c-modal-footer">
316
+ ${this.leadingAction ? this.renderLeadingAction() : nothing}
317
+ ${this.supportingAction ? this.renderSupportingAction() : nothing}
318
+ </footer>`;
319
+ }
320
+
214
321
  public render () {
215
322
  const {
323
+ aria,
216
324
  hasBackButton,
325
+ hasStackedActions,
217
326
  heading,
218
327
  headingLevel = 'h2',
219
328
  isDismissible,
329
+ isFooterPinned,
220
330
  isFullWidthBelowMid,
221
331
  isLoading,
332
+ leadingAction,
333
+ position,
222
334
  size,
335
+ supportingAction,
223
336
  } = this;
224
337
 
225
338
  const headingTag = unsafeStatic(headingLevel);
@@ -229,10 +342,16 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
229
342
  id="dialog"
230
343
  class="c-modal"
231
344
  size="${size}"
345
+ position="${position}"
346
+ ?hasActions=${leadingAction || supportingAction}
232
347
  ?hasBackButton=${hasBackButton}
348
+ ?hasStackedActions=${hasStackedActions}
233
349
  ?isDismissible=${isDismissible}
350
+ ?isFooterPinned=${isFooterPinned}
234
351
  ?isFullWidthBelowMid=${isFullWidthBelowMid}
235
352
  ?isLoading=${isLoading}
353
+ aria-busy="${isLoading ? 'true' : 'false'}"
354
+ aria-label="${(isLoading && aria?.loading) || nothing}"
236
355
  data-test-id="pie-modal">
237
356
  <header class="c-modal-header">
238
357
  ${hasBackButton ? this.renderBackButton() : nothing}
@@ -241,27 +360,16 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
241
360
  </${headingTag}>
242
361
  ${isDismissible ? this.renderCloseButton() : nothing}
243
362
  </header>
244
- <article class="c-modal-content c-modal-content--scrollable">
245
- <div class="c-modal-contentInner">
246
- <slot></slot>
247
- </div>
248
- </article>
249
- <footer class="c-modal-footer">
250
- <pie-button
251
- variant="primary"
252
- type="submit"
253
- @click="${() => this._dialog?.close('leading')}"
254
- data-test-id="modal-leading-action">
255
- Confirm
256
- </pie-button>
257
- <pie-button
258
- variant="ghost"
259
- type="reset"
260
- @click="${() => this._dialog?.close('supporting')}"
261
- data-test-id="modal-supporting-action">
262
- Cancel
263
- </pie-button>
264
- </footer>
363
+ ${
364
+ // We need to wrap the remaining content in a shared scrollable container if the footer is not pinned
365
+ isFooterPinned
366
+ ? this.renderModalContentAndFooter()
367
+ : html`
368
+ <div class="c-modal-scrollContainer">
369
+ ${this.renderModalContentAndFooter()}
370
+ </div>
371
+ `
372
+ }
265
373
  </dialog>`;
266
374
  }
267
375
 
@@ -287,9 +395,9 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
287
395
  }
288
396
 
289
397
  const isClickOutsideDialog = event.clientY < top ||
290
- event.clientY > bottom ||
291
- event.clientX < left ||
292
- event.clientX > right;
398
+ event.clientY > bottom ||
399
+ event.clientX < left ||
400
+ event.clientX > right;
293
401
 
294
402
  if (isClickOutsideDialog) {
295
403
  this.isOpen = false;