@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.
- package/.eslintignore +5 -0
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +45 -0
- package/README.md +31 -17
- package/dist/index.js +1442 -204
- package/dist/types/packages/components/pie-modal/src/defs.d.ts +46 -25
- package/dist/types/packages/components/pie-modal/src/defs.d.ts.map +1 -1
- package/dist/types/packages/components/pie-modal/src/index.d.ts +26 -10
- package/dist/types/packages/components/pie-modal/src/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/playwright/index.ts +1 -1
- package/playwright-lit-visual.config.ts +0 -1
- package/playwright-lit.config.ts +1 -2
- package/src/defs.ts +57 -30
- package/src/index.ts +132 -57
- package/src/modal.scss +45 -2
- package/test/component/pie-modal.spec.ts +163 -0
- package/test/visual/pie-modal.spec.ts +200 -26
- package/tsconfig.json +1 -1
|
@@ -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
|
|
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
|
|
63
|
+
* When false, the modal footer will scroll with the content inside the modal body.
|
|
33
64
|
*/
|
|
34
|
-
|
|
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
|
|
88
|
+
* The supporting action configuration for the modal.
|
|
53
89
|
*/
|
|
54
|
-
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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
|
}
|
package/playwright/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
//Import common styles here
|
|
1
|
+
// Import common styles here
|
package/playwright-lit.config.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
91
|
+
* The leading action configuration for the modal.
|
|
44
92
|
*/
|
|
45
|
-
|
|
93
|
+
leadingAction: ActionProps;
|
|
46
94
|
|
|
47
|
-
|
|
48
|
-
*
|
|
95
|
+
/*
|
|
96
|
+
* The position of the modal; this controls where it will appear on the page.
|
|
49
97
|
*/
|
|
50
|
-
|
|
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
|
|
111
|
+
* The supporting action configuration for the modal.
|
|
64
112
|
*/
|
|
65
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
59
|
-
public
|
|
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
|
|
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
|
|
107
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
403
|
+
event.clientY > bottom ||
|
|
404
|
+
event.clientX < left ||
|
|
405
|
+
event.clientX > right;
|
|
331
406
|
|
|
332
407
|
if (isClickOutsideDialog) {
|
|
333
408
|
this.isOpen = false;
|