@purpurds/modal 3.0.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.
@@ -0,0 +1 @@
1
+ ._purpur-modal-content_1fnes_1{position:fixed;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;overflow:auto;background-color:var(--purpur-color-background-primary);animation:_fadeIn_1fnes_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}._purpur-modal-content_1fnes_1:focus{outline:0}@media (min-width: 600px){._purpur-modal-content_1fnes_1{inset:unset;top:50%;left:50%;width:720px;max-width:calc(100% - var(--purpur-spacing-300) * 2);min-height:320px;max-height:80%;overflow:hidden;border-radius:var(--purpur-border-radius-lg);box-sizing:border-box;transform:translate(-50%,-50%);box-shadow:var(--purpur-shadow-lg)}}@media (min-width: 600px){._purpur-modal-content__overlay_1fnes_30{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--purpur-color-overlay-default);animation:_fadeIn_1fnes_1 var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out)}}._purpur-modal-content_1fnes_1 ._purpur-modal-content__close-button_1fnes_37{position:absolute;top:var(--purpur-spacing-100);right:var(--purpur-spacing-100);z-index:2}@media (min-width: 600px){._purpur-modal-content_1fnes_1 ._purpur-modal-content__close-button_1fnes_37{top:var(--purpur-spacing-150)}}._purpur-modal-content__wrapper_1fnes_48{height:100%;overflow:auto}@media (min-width: 600px){._purpur-modal-content__wrapper_1fnes_48{display:flex;flex-direction:column;overflow:hidden}}._purpur-modal-content__wrapper-inner_1fnes_59{display:flex;flex-direction:column}@media (min-width: 600px){._purpur-modal-content__wrapper-inner_1fnes_59{overflow:hidden}}._purpur-modal-content__image-wrapper_1fnes_68{position:relative;flex-shrink:0;order:-1;width:100%;aspect-ratio:2/1;overflow:hidden}@media (((min-width: 600px) and (max-width: 649px) and (max-height: 720px)) or ((min-width: 650px) and (max-height: 820px))){._purpur-modal-content__image-wrapper_1fnes_68{aspect-ratio:3/1}}._purpur-modal-content__image-wrapper_1fnes_68 img{position:absolute;top:50%;left:50%;display:block;width:100%;height:auto;transform:translate(-50%,-50%)}._purpur-modal-content__header_1fnes_91{position:sticky;top:0;flex-grow:0;padding:var(--purpur-spacing-200) calc(var(--purpur-spacing-600) + var(--purpur-spacing-150)) 0 var(--purpur-spacing-200);background:var(--purpur-color-background-primary)}@media (min-width: 600px){._purpur-modal-content__header_1fnes_91{position:static;padding:var(--purpur-spacing-250) calc(var(--purpur-spacing-800) + var(--purpur-spacing-50)) 0 var(--purpur-spacing-300)}}._purpur-modal-content__title_1fnes_104,._purpur-modal-content__description_1fnes_107{margin:0}._purpur-modal-content__body_1fnes_110{padding:var(--purpur-spacing-200) var(--purpur-spacing-200) 0}@media (min-width: 600px){._purpur-modal-content__body_1fnes_110{height:100%;overflow:auto;padding:var(--purpur-spacing-250) var(--purpur-spacing-300) 0}}._purpur-modal-content__body-inner_1fnes_120{position:relative;z-index:-2;display:flex;flex-direction:column;gap:var(--purpur-spacing-400);padding-bottom:var(--purpur-spacing-250)}@media (min-width: 600px){._purpur-modal-content__body-inner_1fnes_120{padding-bottom:var(--purpur-spacing-300)}}._purpur-modal-content__actions_1fnes_133{display:flex;flex-direction:column;gap:var(--purpur-spacing-200);margin-top:auto}@media (min-width: 600px){._purpur-modal-content__actions_1fnes_133{flex-direction:row-reverse;flex-grow:0}}._purpur-modal-content__button_1fnes_145{width:100%}@media (min-width: 600px){._purpur-modal-content__button_1fnes_145{width:auto}._purpur-modal-content__button_1fnes_145:nth-child(3){margin-right:auto}}._purpur-modal-content--with-image_1fnes_156:not(._purpur-modal-content--overflow_1fnes_156) ._purpur-modal-content__header_1fnes_91{padding-top:var(--purpur-spacing-250)}@media (min-width: 600px){._purpur-modal-content--overflow_1fnes_156:not(._purpur-modal-content--with-image_1fnes_156) ._purpur-modal-content__header_1fnes_91{position:relative}._purpur-modal-content--overflow_1fnes_156:not(._purpur-modal-content--with-image_1fnes_156) ._purpur-modal-content__close-button_1fnes_37{top:50%;transform:translateY(-50%)}}._purpur-modal-content--overflow_1fnes_156 ._purpur-modal-content__header_1fnes_91{padding-bottom:var(--purpur-spacing-200);border-bottom:1px solid var(--purpur-color-border-weak)}._purpur-modal-content--overflow_1fnes_156 ._purpur-modal-content__body_1fnes_110{padding-top:var(--purpur-spacing-300)}@media (min-width: 600px){._purpur-modal-content--overflow_1fnes_156 ._purpur-modal-content__body_1fnes_110{padding-top:var(--purpur-spacing-400)}}._purpur-modal-content--overflow_1fnes_156 ._purpur-modal-content__actions_1fnes_133{position:relative}._purpur-modal-content--overflow_1fnes_156 ._purpur-modal-content__actions_1fnes_133:after{content:"";position:absolute;top:-40px;left:0;z-index:-1;width:100%;height:40px;background:linear-gradient(180deg,rgba(255,255,255,0) 0%,var(--purpur-color-background-primary) 100%);pointer-events:none}._purpur-modal-content--overflow_1fnes_156._purpur-modal-content--sticky-footer_1fnes_194 ._purpur-modal-content__actions_1fnes_133{border-top:1px solid var(--purpur-color-border-weak)}._purpur-modal-content--sticky-footer_1fnes_194 ._purpur-modal-content__body-inner_1fnes_120{padding-bottom:var(--purpur-spacing-300)}._purpur-modal-content--sticky-footer_1fnes_194 ._purpur-modal-content__actions_1fnes_133{padding:var(--purpur-spacing-200)}@media (min-width: 600px){._purpur-modal-content--sticky-footer_1fnes_194 ._purpur-modal-content__actions_1fnes_133{padding-left:var(--purpur-spacing-300);padding-right:var(--purpur-spacing-300)}}@keyframes _fadeIn_1fnes_1{0%{opacity:0}to{opacity:1}}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@purpurds/modal",
3
+ "version": "3.0.0",
4
+ "license": "AGPL-3.0-only",
5
+ "main": "./dist/modal.cjs.js",
6
+ "types": "./dist/modal.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/modal.cjs.js",
10
+ "systemjs": "./dist/modal.system.js",
11
+ "types": "./dist/modal.d.ts",
12
+ "default": "./dist/modal.es.js"
13
+ },
14
+ "./styles": "./dist/styles.css"
15
+ },
16
+ "source": "src/modal.tsx",
17
+ "dependencies": {
18
+ "@radix-ui/react-dialog": "~1.0.5",
19
+ "classnames": "~2.5.0",
20
+ "@purpurds/button": "3.0.0",
21
+ "@purpurds/icon": "3.0.0",
22
+ "@purpurds/heading": "3.0.0",
23
+ "@purpurds/text-spacing": "3.0.0",
24
+ "@purpurds/paragraph": "3.0.0",
25
+ "@purpurds/tokens": "3.0.0",
26
+ "@purpurds/visually-hidden": "3.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@rushstack/eslint-patch": "~1.7.0",
30
+ "@storybook/blocks": "~7.6.0",
31
+ "@storybook/react": "~7.6.0",
32
+ "@telia/base-rig": "~8.2.0",
33
+ "@telia/react-rig": "~3.2.0",
34
+ "@testing-library/dom": "~9.3.3",
35
+ "@testing-library/jest-dom": "~6.3.0",
36
+ "@testing-library/react": "~14.1.2",
37
+ "@types/node": "18",
38
+ "@types/react-dom": "~18.2.17",
39
+ "@types/react": "~18.2.42",
40
+ "eslint-plugin-testing-library": "~6.2.0",
41
+ "eslint": "~8.56.0",
42
+ "jsdom": "~22.1.0",
43
+ "lint-staged": "~10.5.3",
44
+ "prettier": "~2.8.8",
45
+ "react-dom": "~18.2.0",
46
+ "react": "~18.2.0",
47
+ "typescript": "~5.2.2",
48
+ "vite": "~5.0.6",
49
+ "vitest": "~1.2.0",
50
+ "@purpurds/component-rig": "1.0.0"
51
+ },
52
+ "scripts": {
53
+ "build:dev": "vite",
54
+ "build:watch": "vite build --watch",
55
+ "build": "rm -rf dist && vite build && vite build --mode systemjs",
56
+ "ci:build": "rushx build",
57
+ "coverage": "vitest run --coverage",
58
+ "lint:fix": "eslint . --fix",
59
+ "lint": "lint-staged --no-stash 2>&1",
60
+ "sbdev": "rush sbdev",
61
+ "test:unit": "vitest run --passWithNoTests",
62
+ "test:watch": "vitest --watch",
63
+ "test": "rushx test:unit",
64
+ "typecheck": "tsc -p ./tsconfig.json"
65
+ }
66
+ }
package/readme.mdx ADDED
@@ -0,0 +1,81 @@
1
+ import { Meta, Stories, ArgTypes, Primary, Subtitle } from "@storybook/blocks";
2
+
3
+ import * as ModalStories from "./src/modal.stories";
4
+ import * as ModalContentStories from "./src/modal-content.stories";
5
+ import packageInfo from "./package.json";
6
+
7
+ <Meta name="Docs" title="Components/Modal" of={ModalStories} />
8
+
9
+ # Modal
10
+
11
+ <Subtitle>Version {packageInfo.version}</Subtitle>
12
+
13
+ ### Showcase
14
+
15
+ <Primary />
16
+
17
+ ### Properties
18
+
19
+ #### Modal
20
+
21
+ <ArgTypes of={ModalStories} />
22
+
23
+ #### ModalContent
24
+
25
+ <ArgTypes of={ModalContentStories} />
26
+
27
+ ### Installation
28
+
29
+ #### Via NPM
30
+
31
+ Add the dependency to your consumer app like `"@purpurds/modal": "x.y.z"`
32
+
33
+ #### From outside the monorepo (build-time)
34
+
35
+ To install this package, you need to setup access to the artifactory. [Click here to go to the guide on how to do that](https://github.com/telia-company/jfrog-documentation/blob/main/doc/JFrog/JFrog_Onboarding.md#getting-access-to-artifactory-and-other-jfrog-applications).
36
+
37
+ ---
38
+
39
+ In MyApp.tsx
40
+
41
+ ```tsx
42
+ import "@purpurds/tokens/index.css";
43
+ ```
44
+
45
+ and
46
+
47
+ ```tsx
48
+ import "@purpurds/modal/styles";
49
+ ```
50
+
51
+ In MyComponent.tsx
52
+
53
+ ```tsx
54
+ import { Modal } from "@purpurds/modal";
55
+
56
+ export const MyComponent = () => {
57
+ const actions = [
58
+ {
59
+ label: "Primary action",
60
+ onClick: () => {
61
+ // click event handler
62
+ },
63
+ },
64
+ ];
65
+
66
+ return (
67
+ <Modal>
68
+ <Modal.Trigger>Open modal</Modal.Trigger>
69
+ <Modal.Content
70
+ title="A title"
71
+ description="A short optional description of the modal"
72
+ actions={actions}
73
+ showCloseButton
74
+ closeButtonAllyLabel="Close"
75
+ >
76
+ Some content
77
+ </Modal.Content>
78
+ </Modal>
79
+ );
80
+ };
81
+ ```
@@ -0,0 +1,4 @@
1
+ declare module "*.scss" {
2
+ const styles: { [className: string]: string };
3
+ export default styles;
4
+ }
@@ -0,0 +1,251 @@
1
+ @import "@purpurds/tokens/breakpoint/variables";
2
+
3
+ $fadeIn: fadeIn var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);
4
+
5
+ .purpur-modal-content {
6
+ $root: &;
7
+
8
+ position: fixed;
9
+ inset: 0;
10
+ display: flex;
11
+ flex-direction: column;
12
+ overflow: auto;
13
+ background-color: var(--purpur-color-background-primary);
14
+ animation: $fadeIn;
15
+
16
+ &:focus {
17
+ outline: 0;
18
+ }
19
+
20
+ @media (min-width: $purpur-breakpoint-md) {
21
+ inset: unset;
22
+ top: 50%;
23
+ left: 50%;
24
+ width: 720px;
25
+ max-width: calc(100% - (var(--purpur-spacing-300) * 2));
26
+ min-height: 320px;
27
+ max-height: 80%;
28
+ overflow: hidden;
29
+ border-radius: var(--purpur-border-radius-lg);
30
+ box-sizing: border-box;
31
+ transform: translate(-50%, -50%);
32
+ box-shadow: var(--purpur-shadow-lg);
33
+ }
34
+
35
+ &__overlay {
36
+ @media (min-width: $purpur-breakpoint-md) {
37
+ position: fixed;
38
+ inset: 0;
39
+ background: var(--purpur-color-overlay-default);
40
+ animation: $fadeIn;
41
+ }
42
+ }
43
+
44
+ #{$root}__close-button {
45
+ position: absolute;
46
+ top: var(--purpur-spacing-100);
47
+ right: var(--purpur-spacing-100);
48
+ z-index: 2;
49
+
50
+ @media (min-width: $purpur-breakpoint-md) {
51
+ top: var(--purpur-spacing-150);
52
+ }
53
+ }
54
+
55
+ &__wrapper {
56
+ height: 100%;
57
+ overflow: auto;
58
+
59
+ @media (min-width: $purpur-breakpoint-md) {
60
+ display: flex;
61
+ flex-direction: column;
62
+ overflow: hidden;
63
+ }
64
+ }
65
+
66
+ &__wrapper-inner {
67
+ display: flex;
68
+ flex-direction: column;
69
+
70
+ @media (min-width: $purpur-breakpoint-md) {
71
+ overflow: hidden;
72
+ }
73
+ }
74
+
75
+ &__image-wrapper {
76
+ position: relative;
77
+ flex-shrink: 0;
78
+ order: -1;
79
+ width: 100%;
80
+ aspect-ratio: 2 / 1;
81
+ overflow: hidden;
82
+
83
+ /* Shrink image height when it's too large on medium sized and large screens */
84
+ @media (((min-width: $purpur-breakpoint-md) and (max-width: 649px) and (max-height: 720px)) or
85
+ ((min-width: 650px) and (max-height: 820px))) {
86
+ aspect-ratio: 3 / 1;
87
+ }
88
+
89
+ img {
90
+ position: absolute;
91
+ top: 50%;
92
+ left: 50%;
93
+ display: block;
94
+ width: 100%;
95
+ height: auto;
96
+ transform: translate(-50%, -50%);
97
+ }
98
+ }
99
+
100
+ &__header {
101
+ position: sticky;
102
+ top: 0;
103
+ flex-grow: 0;
104
+ padding: var(--purpur-spacing-200) calc(var(--purpur-spacing-600) + var(--purpur-spacing-150)) 0
105
+ var(--purpur-spacing-200);
106
+ background: var(--purpur-color-background-primary);
107
+
108
+ @media (min-width: $purpur-breakpoint-md) {
109
+ position: static;
110
+ padding: var(--purpur-spacing-250) calc(var(--purpur-spacing-800) + var(--purpur-spacing-50))
111
+ 0 var(--purpur-spacing-300);
112
+ }
113
+ }
114
+
115
+ &__title {
116
+ margin: 0;
117
+ }
118
+
119
+ &__description {
120
+ margin: 0;
121
+ }
122
+
123
+ &__body {
124
+ padding: var(--purpur-spacing-200) var(--purpur-spacing-200) 0;
125
+
126
+ @media (min-width: $purpur-breakpoint-md) {
127
+ height: 100%;
128
+ overflow: auto;
129
+ padding: var(--purpur-spacing-250) var(--purpur-spacing-300) 0;
130
+ }
131
+ }
132
+
133
+ &__body-inner {
134
+ position: relative;
135
+ z-index: -2;
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: var(--purpur-spacing-400);
139
+ padding-bottom: var(--purpur-spacing-250);
140
+
141
+ @media (min-width: $purpur-breakpoint-md) {
142
+ padding-bottom: var(--purpur-spacing-300);
143
+ }
144
+ }
145
+
146
+ &__actions {
147
+ display: flex;
148
+ flex-direction: column;
149
+ gap: var(--purpur-spacing-200);
150
+ margin-top: auto;
151
+
152
+ @media (min-width: $purpur-breakpoint-md) {
153
+ flex-direction: row-reverse;
154
+ flex-grow: 0;
155
+ }
156
+ }
157
+
158
+ &__button {
159
+ width: 100%;
160
+
161
+ @media (min-width: $purpur-breakpoint-md) {
162
+ width: auto;
163
+
164
+ &:nth-child(3) {
165
+ margin-right: auto;
166
+ }
167
+ }
168
+ }
169
+
170
+ &--with-image:not(#{$root}--overflow) #{$root}__header {
171
+ padding-top: var(--purpur-spacing-250);
172
+ }
173
+
174
+ &--overflow {
175
+ &:not(#{$root}--with-image) {
176
+ @media (min-width: $purpur-breakpoint-md) {
177
+ #{$root}__header {
178
+ position: relative;
179
+ }
180
+
181
+ #{$root}__close-button {
182
+ top: 50%;
183
+ transform: translateY(-50%);
184
+ }
185
+ }
186
+ }
187
+
188
+ #{$root}__header {
189
+ padding-bottom: var(--purpur-spacing-200);
190
+ border-bottom: 1px solid var(--purpur-color-border-weak);
191
+ }
192
+
193
+ #{$root}__body {
194
+ padding-top: var(--purpur-spacing-300);
195
+
196
+ @media (min-width: $purpur-breakpoint-md) {
197
+ padding-top: var(--purpur-spacing-400);
198
+ }
199
+ }
200
+
201
+ #{$root}__actions {
202
+ $overflowFaderHeight: 40px;
203
+
204
+ position: relative;
205
+
206
+ &::after {
207
+ content: "";
208
+ position: absolute;
209
+ top: -$overflowFaderHeight;
210
+ left: 0;
211
+ z-index: -1;
212
+ width: 100%;
213
+ height: $overflowFaderHeight;
214
+ background: linear-gradient(
215
+ 180deg,
216
+ rgba(255, 255, 255, 0) 0%,
217
+ var(--purpur-color-background-primary) 100%
218
+ );
219
+ pointer-events: none;
220
+ }
221
+ }
222
+
223
+ &#{$root}--sticky-footer #{$root}__actions {
224
+ border-top: 1px solid var(--purpur-color-border-weak);
225
+ }
226
+ }
227
+
228
+ &--sticky-footer {
229
+ #{$root}__body-inner {
230
+ padding-bottom: var(--purpur-spacing-300);
231
+ }
232
+
233
+ #{$root}__actions {
234
+ padding: var(--purpur-spacing-200);
235
+
236
+ @media (min-width: $purpur-breakpoint-md) {
237
+ padding-left: var(--purpur-spacing-300);
238
+ padding-right: var(--purpur-spacing-300);
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ @keyframes fadeIn {
245
+ from {
246
+ opacity: 0;
247
+ }
248
+ to {
249
+ opacity: 1;
250
+ }
251
+ }
@@ -0,0 +1,120 @@
1
+ import React from "react";
2
+ import * as RadixDialog from "@radix-ui/react-dialog";
3
+ import type { Meta, StoryObj } from "@storybook/react";
4
+ import { Paragraph } from "@purpurds/paragraph";
5
+
6
+ import { ModalContent as ModalContentCmp, primaryActionVariants } from "./modal-content";
7
+ import { TextSpacing } from "@purpurds/text-spacing";
8
+
9
+ import "@purpurds/button/styles";
10
+ import "@purpurds/icon/styles";
11
+ import "@purpurds/text-spacing/styles";
12
+
13
+ const meta: Meta<typeof ModalContentCmp> = {
14
+ title: "Components/Modal",
15
+ component: ModalContentCmp,
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof ModalContentCmp>;
20
+
21
+ const defaultArgs = {
22
+ actions: [
23
+ {
24
+ label: "Primary button",
25
+ onClick: () => {},
26
+ },
27
+ {
28
+ label: "Secondary button",
29
+ onClick: () => {},
30
+ },
31
+ { label: "Tertiary text button", onClick: () => {} },
32
+ ],
33
+ description: "This is an optional description",
34
+ primaryActionVariant: primaryActionVariants[0],
35
+ stickyButtons: true,
36
+ title: "Title",
37
+ };
38
+
39
+ const argTypes = {
40
+ primaryActionVariant: { control: "select", options: primaryActionVariants },
41
+ };
42
+
43
+ export const ModalContent: Story = {
44
+ args: defaultArgs,
45
+ parameters: {
46
+ design: [
47
+ {
48
+ name: "Modal",
49
+ type: "figma",
50
+ url: "https://www.figma.com/file/TggtRkYyKpwgKTU0LxuYFN/Modal-redesign?type=design&node-id=104-731&mode=design&t=sm44NPLlG3tjpFQb-0",
51
+ },
52
+ ],
53
+ },
54
+ argTypes,
55
+ render: (args) => {
56
+ return (
57
+ <RadixDialog.Root open>
58
+ <ModalContentCmp {...args}>
59
+ <TextSpacing>
60
+ <Paragraph variant="paragraph-100">
61
+ Lorem ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut faucibus diam
62
+ faucibus eu. Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh
63
+ placerat.Lorem ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut
64
+ faucibus diam faucibus eu.
65
+ </Paragraph>
66
+ <Paragraph variant="paragraph-100">
67
+ Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh placerat.Lorem
68
+ ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut faucibus diam faucibus
69
+ eu. Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh placerat.
70
+ </Paragraph>
71
+ </TextSpacing>
72
+ </ModalContentCmp>
73
+ </RadixDialog.Root>
74
+ );
75
+ },
76
+ };
77
+
78
+ export const ModalContentWithImage: Story = {
79
+ args: {
80
+ ...defaultArgs,
81
+ description: undefined,
82
+ image: (
83
+ <img
84
+ src="https://www.telia.se/images/i15skfqwpurk/5YYelnwdIJGush05RYsE6A/04d4eeb571bca6d5c72b557f6da92c92/Telia_Company_Reinvention_69.jpg"
85
+ alt="Familjens unga ser på tv tillsammans mysig stämning men spännande"
86
+ />
87
+ ),
88
+ },
89
+ parameters: {
90
+ design: [
91
+ {
92
+ name: "Modal",
93
+ type: "figma",
94
+ url: "https://www.figma.com/file/TggtRkYyKpwgKTU0LxuYFN/Modal-redesign?type=design&node-id=104-731&mode=design&t=sm44NPLlG3tjpFQb-0",
95
+ },
96
+ ],
97
+ },
98
+ argTypes,
99
+ render: (args) => {
100
+ return (
101
+ <RadixDialog.Root open>
102
+ <ModalContentCmp {...args}>
103
+ <TextSpacing>
104
+ <Paragraph variant="paragraph-100">
105
+ Lorem ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut faucibus diam
106
+ faucibus eu. Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh
107
+ placerat.Lorem ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut
108
+ faucibus diam faucibus eu.
109
+ </Paragraph>
110
+ <Paragraph variant="paragraph-100">
111
+ Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh placerat.Lorem
112
+ ipsum dolor sit amet consectetur. Diam vitae leo amet tortor ut faucibus diam faucibus
113
+ eu. Pellentesque quis pellentesque sit fermentum. Mi id aenean aliquam nibh placerat.
114
+ </Paragraph>
115
+ </TextSpacing>
116
+ </ModalContentCmp>
117
+ </RadixDialog.Root>
118
+ );
119
+ },
120
+ };
@@ -0,0 +1,141 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+
6
+ import { Modal } from "./modal";
7
+ import { ModalActions, MAX_NUMBER_OF_ACTIONS } from "./modal-content";
8
+
9
+ expect.extend(matchers);
10
+
11
+ describe("ModalContent", () => {
12
+ afterEach(() => {
13
+ cleanup();
14
+ });
15
+
16
+ it("should render title", () => {
17
+ render(
18
+ <Modal open>
19
+ <Modal.Trigger data-testid="modal trigger">
20
+ <button type="button">Open</button>
21
+ </Modal.Trigger>
22
+ <Modal.Content data-testid="modal content" title="Title">
23
+ Some content
24
+ </Modal.Content>
25
+ </Modal>
26
+ );
27
+
28
+ expect(screen.getByTestId("modal content title")).toBeInTheDocument();
29
+ });
30
+
31
+ it("should render description", () => {
32
+ render(
33
+ <Modal open>
34
+ <Modal.Trigger data-testid="modal trigger">
35
+ <button type="button">Open</button>
36
+ </Modal.Trigger>
37
+ <Modal.Content
38
+ data-testid="modal content"
39
+ title="Title"
40
+ description="This is a description"
41
+ >
42
+ Some content
43
+ </Modal.Content>
44
+ </Modal>
45
+ );
46
+
47
+ expect(screen.getByTestId("modal content description")).toBeInTheDocument();
48
+ });
49
+
50
+ it("should render image", () => {
51
+ render(
52
+ <Modal open>
53
+ <Modal.Trigger data-testid="modal trigger">
54
+ <button type="button">Open</button>
55
+ </Modal.Trigger>
56
+ <Modal.Content
57
+ data-testid="modal content"
58
+ title="Title"
59
+ image={<img src="image.jpg" alt="alt text" />}
60
+ >
61
+ Some content
62
+ </Modal.Content>
63
+ </Modal>
64
+ );
65
+
66
+ expect(screen.getByTestId("modal content image")).toBeInTheDocument();
67
+ });
68
+
69
+ it("should render close button if showCloseButton is set to true", () => {
70
+ render(
71
+ <Modal open>
72
+ <Modal.Trigger data-testid="modal trigger">
73
+ <button type="button">Open</button>
74
+ </Modal.Trigger>
75
+ <Modal.Content
76
+ data-testid="modal content"
77
+ title="Title"
78
+ showCloseButton
79
+ closeButtonAllyLabel="Close"
80
+ >
81
+ Some content
82
+ </Modal.Content>
83
+ </Modal>
84
+ );
85
+
86
+ expect(screen.getByTestId("modal content close-button")).toBeInTheDocument();
87
+ });
88
+ });
89
+
90
+ const testActions = [
91
+ {
92
+ label: "Primary",
93
+ onClick: vi.fn(),
94
+ },
95
+ {
96
+ label: "Secondary",
97
+ onClick: vi.fn(),
98
+ },
99
+ {
100
+ label: "Tertiary",
101
+ onClick: vi.fn(),
102
+ },
103
+ ];
104
+
105
+ describe("ModalActions", () => {
106
+ afterEach(() => {
107
+ cleanup();
108
+ });
109
+
110
+ it("should render a button for each action", () => {
111
+ render(<ModalActions actions={testActions} />);
112
+
113
+ expect(screen.getAllByTestId("modal actions button").length).toBe(3);
114
+ });
115
+
116
+ it(`should render a maximum of ${MAX_NUMBER_OF_ACTIONS} buttons`, () => {
117
+ render(
118
+ <ModalActions
119
+ actions={Array(MAX_NUMBER_OF_ACTIONS + 1)
120
+ .fill(null)
121
+ .map((_, i) => ({ label: i.toString(), onClick: vi.fn() }))}
122
+ />
123
+ );
124
+
125
+ expect(screen.getAllByTestId("modal actions button").length).toBe(MAX_NUMBER_OF_ACTIONS);
126
+ });
127
+
128
+ it("should trigger `onClick` callback function for each button", () => {
129
+ render(<ModalActions actions={testActions} primaryActionVariant="expressive" />);
130
+
131
+ const buttons = screen.getAllByTestId("modal actions button");
132
+
133
+ fireEvent.click(buttons[0]);
134
+ fireEvent.click(buttons[1]);
135
+ fireEvent.click(buttons[2]);
136
+
137
+ expect(testActions[0].onClick).toHaveBeenCalledTimes(1);
138
+ expect(testActions[1].onClick).toHaveBeenCalledTimes(1);
139
+ expect(testActions[2].onClick).toHaveBeenCalledTimes(1);
140
+ });
141
+ });