@automattic/launchpad 1.0.0 → 1.0.1
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/.storybook/main.ts +16 -0
- package/.storybook/preview.ts +6 -0
- package/README.md +11 -2
- package/dist/cjs/action-components/index.js +6 -0
- package/dist/cjs/action-components/index.js.map +1 -0
- package/dist/cjs/action-components/share-site-modal/index.js +44 -0
- package/dist/cjs/action-components/share-site-modal/index.js.map +1 -0
- package/dist/cjs/action-components/share-site-modal/style.scss +159 -0
- package/dist/cjs/badge/index.js +2 -2
- package/dist/cjs/badge/index.js.map +1 -1
- package/dist/cjs/checklist/index.js +16 -8
- package/dist/cjs/checklist/index.js.map +1 -1
- package/dist/cjs/checklist/index.stories.js +29 -0
- package/dist/cjs/checklist/index.stories.js.map +1 -0
- package/dist/cjs/checklist-item/index.js +30 -13
- package/dist/cjs/checklist-item/index.js.map +1 -1
- package/dist/cjs/checklist-item/index.stories.js +79 -0
- package/dist/cjs/checklist-item/index.stories.js.map +1 -0
- package/dist/cjs/checklist-item/style.scss +142 -22
- package/dist/cjs/index.js +6 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/launchpad-container/index.js +11 -0
- package/dist/cjs/launchpad-container/index.js.map +1 -0
- package/dist/cjs/launchpad-container/style.scss +59 -0
- package/dist/cjs/launchpad-internal.js +40 -0
- package/dist/cjs/launchpad-internal.js.map +1 -0
- package/dist/cjs/launchpad-internal.stories.js +45 -0
- package/dist/cjs/launchpad-internal.stories.js.map +1 -0
- package/dist/cjs/launchpad.js +39 -10
- package/dist/cjs/launchpad.js.map +1 -1
- package/dist/cjs/msw/link-in-bio.js +68 -0
- package/dist/cjs/msw/link-in-bio.js.map +1 -0
- package/dist/cjs/msw/resolver.js +19 -0
- package/dist/cjs/msw/resolver.js.map +1 -0
- package/dist/cjs/msw/setup-free.js +88 -0
- package/dist/cjs/msw/setup-free.js.map +1 -0
- package/dist/cjs/setup-actions.js +51 -54
- package/dist/cjs/setup-actions.js.map +1 -1
- package/dist/cjs/use-tracking/index.js +72 -0
- package/dist/cjs/use-tracking/index.js.map +1 -0
- package/dist/esm/action-components/index.js +2 -0
- package/dist/esm/action-components/index.js.map +1 -0
- package/dist/esm/action-components/share-site-modal/index.js +42 -0
- package/dist/esm/action-components/share-site-modal/index.js.map +1 -0
- package/dist/esm/action-components/share-site-modal/style.scss +159 -0
- package/dist/esm/badge/index.js +2 -2
- package/dist/esm/badge/index.js.map +1 -1
- package/dist/esm/checklist/index.js +14 -8
- package/dist/esm/checklist/index.js.map +1 -1
- package/dist/esm/checklist/index.stories.js +25 -0
- package/dist/esm/checklist/index.stories.js.map +1 -0
- package/dist/esm/checklist-item/index.js +29 -14
- package/dist/esm/checklist-item/index.js.map +1 -1
- package/dist/esm/checklist-item/index.stories.js +75 -0
- package/dist/esm/checklist-item/index.stories.js.map +1 -0
- package/dist/esm/checklist-item/style.scss +142 -22
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/launchpad-container/index.js +8 -0
- package/dist/esm/launchpad-container/index.js.map +1 -0
- package/dist/esm/launchpad-container/style.scss +59 -0
- package/dist/esm/launchpad-internal.js +37 -0
- package/dist/esm/launchpad-internal.js.map +1 -0
- package/dist/esm/launchpad-internal.stories.js +41 -0
- package/dist/esm/launchpad-internal.stories.js.map +1 -0
- package/dist/esm/launchpad.js +41 -13
- package/dist/esm/launchpad.js.map +1 -1
- package/dist/esm/msw/link-in-bio.js +66 -0
- package/dist/esm/msw/link-in-bio.js.map +1 -0
- package/dist/esm/msw/resolver.js +14 -0
- package/dist/esm/msw/resolver.js.map +1 -0
- package/dist/esm/msw/setup-free.js +86 -0
- package/dist/esm/msw/setup-free.js.map +1 -0
- package/dist/esm/setup-actions.js +51 -54
- package/dist/esm/setup-actions.js.map +1 -1
- package/dist/esm/use-tracking/index.js +68 -0
- package/dist/esm/use-tracking/index.js.map +1 -0
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/action-components/index.d.ts +2 -0
- package/dist/types/action-components/index.d.ts.map +1 -0
- package/dist/types/action-components/share-site-modal/index.d.ts +9 -0
- package/dist/types/action-components/share-site-modal/index.d.ts.map +1 -0
- package/dist/types/badge/index.d.ts.map +1 -1
- package/dist/types/checklist/index.d.ts +6 -7
- package/dist/types/checklist/index.d.ts.map +1 -1
- package/dist/types/checklist/index.stories.d.ts +8 -0
- package/dist/types/checklist/index.stories.d.ts.map +1 -0
- package/dist/types/checklist-item/index.d.ts +11 -8
- package/dist/types/checklist-item/index.d.ts.map +1 -1
- package/dist/types/checklist-item/index.stories.d.ts +14 -0
- package/dist/types/checklist-item/index.stories.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/launchpad-container/index.d.ts +15 -0
- package/dist/types/launchpad-container/index.d.ts.map +1 -0
- package/dist/types/launchpad-internal.d.ts +20 -0
- package/dist/types/launchpad-internal.d.ts.map +1 -0
- package/dist/types/launchpad-internal.stories.d.ts +8 -0
- package/dist/types/launchpad-internal.stories.d.ts.map +1 -0
- package/dist/types/launchpad.d.ts +10 -9
- package/dist/types/launchpad.d.ts.map +1 -1
- package/dist/types/msw/link-in-bio.d.ts +35 -0
- package/dist/types/msw/link-in-bio.d.ts.map +1 -0
- package/dist/types/msw/resolver.d.ts +3 -0
- package/dist/types/msw/resolver.d.ts.map +1 -0
- package/dist/types/msw/setup-free.d.ts +35 -0
- package/dist/types/msw/setup-free.d.ts.map +1 -0
- package/dist/types/setup-actions.d.ts +1 -1
- package/dist/types/setup-actions.d.ts.map +1 -1
- package/dist/types/types.d.ts +19 -9
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/use-tracking/index.d.ts +14 -0
- package/dist/types/use-tracking/index.d.ts.map +1 -0
- package/package.json +24 -16
- package/src/action-components/index.ts +1 -0
- package/src/action-components/share-site-modal/index.tsx +100 -0
- package/src/action-components/share-site-modal/style.scss +159 -0
- package/src/badge/index.tsx +2 -4
- package/src/checklist/index.stories.tsx +45 -0
- package/src/checklist/index.tsx +22 -21
- package/src/checklist/test/index.tsx +26 -0
- package/src/checklist-item/index.stories.tsx +92 -0
- package/src/checklist-item/index.tsx +62 -22
- package/src/checklist-item/style.scss +142 -22
- package/src/checklist-item/test/index.tsx +232 -0
- package/src/index.ts +3 -0
- package/src/launchpad-container/index.tsx +45 -0
- package/src/launchpad-container/style.scss +59 -0
- package/src/launchpad-container/test/index.tsx +72 -0
- package/src/launchpad-internal.stories.tsx +49 -0
- package/src/launchpad-internal.tsx +85 -0
- package/src/launchpad.tsx +80 -30
- package/src/msw/link-in-bio.ts +66 -0
- package/src/msw/resolver.ts +18 -0
- package/src/msw/setup-free.ts +85 -0
- package/src/setup-actions.ts +67 -54
- package/src/test/launchpad.tsx +51 -24
- package/src/test/lib/fixtures.ts +7 -2
- package/src/types.ts +24 -9
- package/src/use-tracking/index.tsx +87 -0
- package/src/use-tracking/test/index.tsx +116 -0
- package/static/mockServiceWorker.js +287 -0
- package/src/test/checklist-item.tsx +0 -96
- package/src/test/checklist.tsx +0 -25
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
@import "@wordpress/base-styles/breakpoints";
|
|
3
3
|
@import "@automattic/typography/styles/variables";
|
|
4
4
|
|
|
5
|
-
.checklist-item__task-content {
|
|
6
|
-
align-items:
|
|
5
|
+
.checklist-item__task-content.components-button {
|
|
6
|
+
align-items: center;
|
|
7
7
|
background-color: transparent;
|
|
8
8
|
border-bottom: 1px solid var(--studio-gray-5);
|
|
9
9
|
border-left: none;
|
|
@@ -15,10 +15,51 @@
|
|
|
15
15
|
width: 100%;
|
|
16
16
|
margin: 0;
|
|
17
17
|
color: var(--color-neutral-100);
|
|
18
|
+
height: auto;
|
|
19
|
+
font-family: $sans;
|
|
20
|
+
font-size: $font-body-small;
|
|
21
|
+
line-height: 22px;
|
|
22
|
+
outline: 0;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
text-decoration: none;
|
|
25
|
+
padding-left: 2px;
|
|
26
|
+
|
|
27
|
+
&:is(button, a) {
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&:is(div) {
|
|
32
|
+
cursor: default;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:visited {
|
|
36
|
+
color: var(--color-neutral-100);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:disabled {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
color: var(--color-neutral-20);
|
|
42
|
+
}
|
|
18
43
|
|
|
19
44
|
&.is-placeholder {
|
|
20
45
|
padding: 16px 0 16px 0;
|
|
21
46
|
}
|
|
47
|
+
|
|
48
|
+
&:focus-visible {
|
|
49
|
+
outline: var(--color-primary);
|
|
50
|
+
box-shadow: 0 0 0 2px var(--color-primary-light);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.badge {
|
|
54
|
+
margin-left: auto;
|
|
55
|
+
background-color: var(--studio-blue-5);
|
|
56
|
+
color: var(--studio-blue-80);
|
|
57
|
+
font-size: $font-body-extra-small;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.checklist-item__chevron {
|
|
61
|
+
width: auto;
|
|
62
|
+
}
|
|
22
63
|
}
|
|
23
64
|
|
|
24
65
|
.checklist-item__content {
|
|
@@ -38,27 +79,28 @@
|
|
|
38
79
|
}
|
|
39
80
|
}
|
|
40
81
|
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
border-bottom: 1px solid var(--studio-gray-5);
|
|
82
|
+
.checklist-item__task-content {
|
|
83
|
+
&.is-placeholder {
|
|
84
|
+
padding: 5px 0 5px 0;
|
|
85
|
+
}
|
|
46
86
|
}
|
|
47
87
|
|
|
48
|
-
.checklist-item__task-content
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
88
|
+
.checklist-item__task-content.components-button:hover:not([disabled]),
|
|
89
|
+
.checklist-item__task-content.components-button:focus:not([disabled]) {
|
|
90
|
+
&:is(button, a) {
|
|
91
|
+
fill: var(--studio-blue-50);
|
|
92
|
+
color: var(--studio-blue-50);
|
|
93
|
+
}
|
|
94
|
+
border-bottom: 1px solid var(--studio-gray-5);
|
|
53
95
|
}
|
|
54
96
|
|
|
55
|
-
.checklist-item__task-content[disabled],
|
|
56
|
-
.checklist-item__task-content[disabled]:focus {
|
|
97
|
+
.checklist-item__task-content.components-button[disabled],
|
|
98
|
+
.checklist-item__task-content.components-button[disabled]:focus {
|
|
57
99
|
background-color: transparent;
|
|
58
100
|
pointer-events: none;
|
|
59
101
|
}
|
|
60
102
|
|
|
61
|
-
.checklist-item__task-content[data-task="verify_email"] {
|
|
103
|
+
.checklist-item__task-content.components-button[data-task="verify_email"] {
|
|
62
104
|
color: var(--color-text);
|
|
63
105
|
}
|
|
64
106
|
|
|
@@ -68,11 +110,13 @@
|
|
|
68
110
|
}
|
|
69
111
|
|
|
70
112
|
.checklist-item__checkmark-container {
|
|
71
|
-
margin-right:
|
|
113
|
+
margin-right: 7px;
|
|
72
114
|
width: 20px;
|
|
115
|
+
display: flex;
|
|
116
|
+
margin-left: 1px;
|
|
117
|
+
margin-bottom: -2px;
|
|
73
118
|
|
|
74
119
|
.checklist-item__checkmark {
|
|
75
|
-
top: 2px;
|
|
76
120
|
vertical-align: text-bottom;
|
|
77
121
|
}
|
|
78
122
|
}
|
|
@@ -103,7 +147,7 @@
|
|
|
103
147
|
}
|
|
104
148
|
}
|
|
105
149
|
|
|
106
|
-
.checklist-item__task-content:hover .checklist-item__subtext {
|
|
150
|
+
.checklist-item__task-content.components-button:hover .checklist-item__subtext {
|
|
107
151
|
color: var(--color-neutral-100);
|
|
108
152
|
}
|
|
109
153
|
|
|
@@ -125,6 +169,18 @@
|
|
|
125
169
|
}
|
|
126
170
|
}
|
|
127
171
|
|
|
172
|
+
.checklist-item__task.expanded {
|
|
173
|
+
.checklist-item__task-content,
|
|
174
|
+
.checklist-item__task-content:hover,
|
|
175
|
+
.checklist-item__task-content:focus {
|
|
176
|
+
border-bottom: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.checklist-item__task-content {
|
|
180
|
+
padding-bottom: 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
128
184
|
// completed tasks (both enabled and disabled)
|
|
129
185
|
.checklist-item__task.completed.disabled,
|
|
130
186
|
.checklist-item__task.completed.enabled {
|
|
@@ -145,8 +201,10 @@
|
|
|
145
201
|
}
|
|
146
202
|
|
|
147
203
|
// pending tasks and completed enabled
|
|
148
|
-
.checklist-item__task.completed.enabled:hover,
|
|
149
|
-
.checklist-item__task.pending:hover,
|
|
204
|
+
.checklist-item__task.completed.enabled > button:hover,
|
|
205
|
+
.checklist-item__task.pending > button:hover,
|
|
206
|
+
.checklist-item__task.completed.enabled > a:hover,
|
|
207
|
+
.checklist-item__task.pending > a:hover,
|
|
150
208
|
.checklist-item__task.completed.enabled > a:focus,
|
|
151
209
|
.checklist-item__task.pending > a:focus {
|
|
152
210
|
.checklist-item__text {
|
|
@@ -171,7 +229,7 @@
|
|
|
171
229
|
// pending disabled tasks
|
|
172
230
|
.checklist-item__task.pending.disabled {
|
|
173
231
|
.checklist-item__text {
|
|
174
|
-
color:
|
|
232
|
+
color: var(--color-neutral-20);
|
|
175
233
|
}
|
|
176
234
|
}
|
|
177
235
|
|
|
@@ -191,14 +249,20 @@
|
|
|
191
249
|
font-weight: 600;
|
|
192
250
|
}
|
|
193
251
|
|
|
194
|
-
.checklist-item__checklist-
|
|
252
|
+
.checklist-item__checklist-expanded-action-button.components-button,
|
|
253
|
+
.checklist-item__checklist-primary-button.components-button {
|
|
195
254
|
width: 100%;
|
|
196
255
|
font-weight: 500;
|
|
197
256
|
padding: 11px;
|
|
198
257
|
margin-top: 12px;
|
|
199
258
|
background-color: var(--color-primary);
|
|
200
259
|
border-color: var(--color-primary);
|
|
260
|
+
border-radius: 4px;
|
|
201
261
|
color: var(--color-text-inverted);
|
|
262
|
+
justify-content: center;
|
|
263
|
+
font-size: $font-body-small;
|
|
264
|
+
height: auto;
|
|
265
|
+
line-height: 22px;
|
|
202
266
|
|
|
203
267
|
&:hover:not([disabled]) {
|
|
204
268
|
background-color: var(--color-primary-60);
|
|
@@ -211,4 +275,60 @@
|
|
|
211
275
|
border-color: var(--color-primary-10);
|
|
212
276
|
color: var(--color-text-inverted);
|
|
213
277
|
}
|
|
278
|
+
|
|
279
|
+
&:disabled {
|
|
280
|
+
opacity: 1;
|
|
281
|
+
&:hover {
|
|
282
|
+
color: var(--color-text-inverted);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.checklist-item__checklist-expanded-action-button.components-button {
|
|
288
|
+
display: block;
|
|
289
|
+
margin: 16px 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.checklist-item__expanded-content {
|
|
293
|
+
padding-left: 2px;
|
|
294
|
+
font-family: $sans;
|
|
295
|
+
font-size: $font-body-small;
|
|
296
|
+
|
|
297
|
+
p {
|
|
298
|
+
margin: 12px 0;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.checklist-item__checklist-secondary-button.components-button {
|
|
303
|
+
width: 100%;
|
|
304
|
+
font-weight: 500;
|
|
305
|
+
padding: 11px;
|
|
306
|
+
margin-top: 12px;
|
|
307
|
+
background-color: var(--studio-white);
|
|
308
|
+
border: 1px solid var(--studio-gray-100);
|
|
309
|
+
border-radius: 4px;
|
|
310
|
+
color: var(--color-text);
|
|
311
|
+
justify-content: center;
|
|
312
|
+
font-size: $font-body-small;
|
|
313
|
+
height: auto;
|
|
314
|
+
line-height: 22px;
|
|
315
|
+
|
|
316
|
+
&:hover:not([disabled]) {
|
|
317
|
+
background-color: var(--studio-gray-0);
|
|
318
|
+
border-color: var(--color-neutral-40);
|
|
319
|
+
color: var(--color-text);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
&[disabled] {
|
|
323
|
+
background-color: var(--studio-gray-0);
|
|
324
|
+
border: none;
|
|
325
|
+
color: var(--color-neutral-40);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
&:disabled {
|
|
329
|
+
opacity: 1;
|
|
330
|
+
&:hover {
|
|
331
|
+
color: var(--color-neutral-40);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
214
334
|
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import React, { ComponentProps } from 'react';
|
|
4
|
+
import ChecklistItem from '..';
|
|
5
|
+
import { buildTask } from '../../test/lib/fixtures';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
|
|
8
|
+
describe( 'ChecklistItem', () => {
|
|
9
|
+
const defaultProps = {
|
|
10
|
+
task: buildTask( {
|
|
11
|
+
completed: false,
|
|
12
|
+
disabled: false,
|
|
13
|
+
actionDispatch: jest.fn(),
|
|
14
|
+
} ),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const renderComponent = ( props: Partial< ComponentProps< typeof ChecklistItem > > ) =>
|
|
18
|
+
render( <ChecklistItem { ...defaultProps } { ...props } /> );
|
|
19
|
+
|
|
20
|
+
it( 'displays a badge', () => {
|
|
21
|
+
const badge_text = 'Badge Text';
|
|
22
|
+
renderComponent( { task: buildTask( { badge_text } ) } );
|
|
23
|
+
|
|
24
|
+
expect( screen.getByText( badge_text ) ).toBeVisible();
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
it( 'hides the task complete icon when the task is not completed', () => {
|
|
28
|
+
renderComponent( { task: buildTask( { completed: false } ) } );
|
|
29
|
+
const taskCompleteIcon = screen.queryByLabelText( 'Task complete' );
|
|
30
|
+
|
|
31
|
+
expect( taskCompleteIcon ).not.toBeInTheDocument();
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'renders a button when it has the onClick event', () => {
|
|
35
|
+
const onClick = jest.fn();
|
|
36
|
+
renderComponent( { onClick } );
|
|
37
|
+
|
|
38
|
+
const taskButton = screen.getByRole( 'button' );
|
|
39
|
+
|
|
40
|
+
expect( taskButton ).toBeInTheDocument();
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'calls onClick when the task is clicked', async () => {
|
|
44
|
+
const onClick = jest.fn();
|
|
45
|
+
renderComponent( { onClick } );
|
|
46
|
+
|
|
47
|
+
const taskButton = screen.getByRole( 'button' );
|
|
48
|
+
await userEvent.click( taskButton );
|
|
49
|
+
|
|
50
|
+
expect( onClick ).toHaveBeenCalledTimes( 1 );
|
|
51
|
+
} );
|
|
52
|
+
|
|
53
|
+
it( 'uses the task action dispatch when the task is clicked and no custom onClick prop is used', async () => {
|
|
54
|
+
const actionDispatch = jest.fn();
|
|
55
|
+
renderComponent( {
|
|
56
|
+
task: buildTask( { actionDispatch, completed: false, disabled: false } ),
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
const taskButton = screen.getByRole( 'button' );
|
|
60
|
+
await userEvent.click( taskButton );
|
|
61
|
+
|
|
62
|
+
expect( actionDispatch ).toHaveBeenCalledTimes( 1 );
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
it( 'calls the onclick when calypso path is available', async () => {
|
|
66
|
+
const onClick = jest.fn();
|
|
67
|
+
renderComponent( {
|
|
68
|
+
onClick,
|
|
69
|
+
isPrimaryAction: true,
|
|
70
|
+
task: buildTask( {
|
|
71
|
+
useCalypsoPath: true,
|
|
72
|
+
disabled: false,
|
|
73
|
+
calypso_path: 'https://wordpress.com',
|
|
74
|
+
} ),
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
const taskButton = screen.getByRole( 'link' );
|
|
78
|
+
await userEvent.click( taskButton );
|
|
79
|
+
|
|
80
|
+
expect( onClick ).toHaveBeenCalledTimes( 1 );
|
|
81
|
+
} );
|
|
82
|
+
|
|
83
|
+
it( 'calls the task action dispatch when calypso path is available', async () => {
|
|
84
|
+
const actionDispatch = jest.fn();
|
|
85
|
+
renderComponent( {
|
|
86
|
+
isPrimaryAction: true,
|
|
87
|
+
task: buildTask( {
|
|
88
|
+
useCalypsoPath: true,
|
|
89
|
+
disabled: false,
|
|
90
|
+
calypso_path: 'https://wordpress.com',
|
|
91
|
+
actionDispatch,
|
|
92
|
+
} ),
|
|
93
|
+
} );
|
|
94
|
+
|
|
95
|
+
const taskButton = screen.getByRole( 'link' );
|
|
96
|
+
await userEvent.click( taskButton );
|
|
97
|
+
|
|
98
|
+
expect( actionDispatch ).toHaveBeenCalledTimes( 1 );
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
it( 'renders a span when it does not have the click or a link', () => {
|
|
102
|
+
const title = 'Task';
|
|
103
|
+
const { container } = renderComponent( { task: buildTask( { title } ) } );
|
|
104
|
+
|
|
105
|
+
const taskButton = container.querySelector( '.components-button' )!;
|
|
106
|
+
|
|
107
|
+
expect( taskButton.tagName ).toEqual( 'DIV' );
|
|
108
|
+
} );
|
|
109
|
+
|
|
110
|
+
describe( 'when the task is disabled', () => {
|
|
111
|
+
it( 'disables the button', () => {
|
|
112
|
+
renderComponent( { task: buildTask( { disabled: true } ), onClick: () => {} } );
|
|
113
|
+
const taskButton = screen.queryByRole( 'button' );
|
|
114
|
+
|
|
115
|
+
expect( taskButton ).toBeDisabled();
|
|
116
|
+
} );
|
|
117
|
+
|
|
118
|
+
it( 'hides the task enabled icon', () => {
|
|
119
|
+
renderComponent( { task: buildTask( { disabled: true } ) } );
|
|
120
|
+
const taskEnabledIcon = screen.queryByLabelText( 'Task enabled' );
|
|
121
|
+
|
|
122
|
+
expect( taskEnabledIcon ).not.toBeInTheDocument();
|
|
123
|
+
} );
|
|
124
|
+
} );
|
|
125
|
+
|
|
126
|
+
describe( 'when the task is completed', () => {
|
|
127
|
+
it( 'shows the task completed icon', () => {
|
|
128
|
+
renderComponent( { task: buildTask( { completed: true } ) } );
|
|
129
|
+
const taskCompleteIcon = screen.queryByLabelText( 'Task complete' );
|
|
130
|
+
|
|
131
|
+
expect( taskCompleteIcon ).toBeVisible();
|
|
132
|
+
} );
|
|
133
|
+
|
|
134
|
+
it( 'hides the task enabled icon', () => {
|
|
135
|
+
renderComponent( { task: buildTask( { completed: true } ) } );
|
|
136
|
+
const taskEnabledIcon = screen.queryByLabelText( 'Task enabled' );
|
|
137
|
+
expect( taskEnabledIcon ).not.toBeInTheDocument();
|
|
138
|
+
} );
|
|
139
|
+
|
|
140
|
+
it( 'disables the task', () => {
|
|
141
|
+
renderComponent( { task: buildTask( { completed: true } ), onClick: () => {} } );
|
|
142
|
+
const taskButton = screen.queryByRole( 'button' );
|
|
143
|
+
|
|
144
|
+
expect( taskButton ).toBeDisabled();
|
|
145
|
+
} );
|
|
146
|
+
|
|
147
|
+
describe( 'and the task is kept enabled', () => {
|
|
148
|
+
it( 'hides the task enabled icon', () => {
|
|
149
|
+
renderComponent( { task: buildTask( { completed: true, disabled: false } ) } );
|
|
150
|
+
const taskEnabledIcon = screen.queryByLabelText( 'Task enabled' );
|
|
151
|
+
|
|
152
|
+
expect( taskEnabledIcon ).not.toBeInTheDocument();
|
|
153
|
+
} );
|
|
154
|
+
|
|
155
|
+
it( 'enables the task', () => {
|
|
156
|
+
renderComponent( {
|
|
157
|
+
task: buildTask( { completed: true, disabled: false } ),
|
|
158
|
+
onClick: () => {},
|
|
159
|
+
} );
|
|
160
|
+
const taskButton = screen.queryByRole( 'button' );
|
|
161
|
+
|
|
162
|
+
expect( taskButton ).toBeEnabled();
|
|
163
|
+
} );
|
|
164
|
+
} );
|
|
165
|
+
} );
|
|
166
|
+
|
|
167
|
+
describe( 'when a task is a primary action', () => {
|
|
168
|
+
it( 'displays a primary button', () => {
|
|
169
|
+
renderComponent( { isPrimaryAction: true } );
|
|
170
|
+
const taskButton = screen.queryByRole( 'button' );
|
|
171
|
+
|
|
172
|
+
expect( taskButton?.className ).toContain( 'checklist-item__checklist-primary-button' );
|
|
173
|
+
} );
|
|
174
|
+
} );
|
|
175
|
+
|
|
176
|
+
describe( 'when it has expandable content', () => {
|
|
177
|
+
describe( 'and when it is open', () => {
|
|
178
|
+
it( 'displays the content', () => {
|
|
179
|
+
renderComponent( { expandable: { isOpen: true, content: <div>Expanded</div> } } );
|
|
180
|
+
|
|
181
|
+
expect( screen.getByText( 'Expanded' ) ).toBeVisible();
|
|
182
|
+
} );
|
|
183
|
+
|
|
184
|
+
it( 'calls the action click', async () => {
|
|
185
|
+
const onClick = jest.fn();
|
|
186
|
+
renderComponent( {
|
|
187
|
+
expandable: {
|
|
188
|
+
isOpen: true,
|
|
189
|
+
content: <div>Expanded</div>,
|
|
190
|
+
action: {
|
|
191
|
+
label: 'Action',
|
|
192
|
+
onClick,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
} );
|
|
196
|
+
|
|
197
|
+
const actionButton = screen.queryByRole( 'button', { name: 'Action' } )!;
|
|
198
|
+
await userEvent.click( actionButton );
|
|
199
|
+
|
|
200
|
+
expect( onClick ).toHaveBeenCalled();
|
|
201
|
+
} );
|
|
202
|
+
|
|
203
|
+
it( 'has the expanded class', () => {
|
|
204
|
+
const { container } = renderComponent( {
|
|
205
|
+
expandable: { isOpen: true, content: <div>Expanded</div> },
|
|
206
|
+
} );
|
|
207
|
+
|
|
208
|
+
expect(
|
|
209
|
+
container.querySelector( '.checklist-item__task' )?.classList.contains( 'expanded' )
|
|
210
|
+
).toBeTruthy();
|
|
211
|
+
} );
|
|
212
|
+
} );
|
|
213
|
+
|
|
214
|
+
describe( 'and when it is not open', () => {
|
|
215
|
+
it( 'does not display the content', () => {
|
|
216
|
+
renderComponent( { expandable: { isOpen: false, content: <div>Expanded</div> } } );
|
|
217
|
+
|
|
218
|
+
expect( screen.queryByText( 'Expanded' ) ).not.toBeInTheDocument();
|
|
219
|
+
} );
|
|
220
|
+
|
|
221
|
+
it( 'does not have the expanded class', () => {
|
|
222
|
+
const { container } = renderComponent( {
|
|
223
|
+
expandable: { isOpen: false, content: <div>Expanded</div> },
|
|
224
|
+
} );
|
|
225
|
+
|
|
226
|
+
expect(
|
|
227
|
+
container.querySelector( '.checklist-item__task' )?.classList.contains( 'expanded' )
|
|
228
|
+
).toBeFalsy();
|
|
229
|
+
} );
|
|
230
|
+
} );
|
|
231
|
+
} );
|
|
232
|
+
} );
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { default as Checklist } from './checklist';
|
|
2
2
|
export { default as ChecklistItem } from './checklist-item';
|
|
3
|
+
export { default as LaunchpadInternal } from './launchpad-internal';
|
|
4
|
+
export { default as LaunchpadContainer } from './launchpad-container';
|
|
3
5
|
export { default as Launchpad } from './launchpad';
|
|
4
6
|
export { setUpActionsForTasks } from './setup-actions';
|
|
7
|
+
export * from './action-components';
|
|
5
8
|
export * from './types';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import './style.scss';
|
|
5
|
+
|
|
6
|
+
export interface ContainerProps {
|
|
7
|
+
className?: string;
|
|
8
|
+
headerClassName?: string;
|
|
9
|
+
contentClassName?: string;
|
|
10
|
+
sidebarClassName?: string;
|
|
11
|
+
mainContentClassName?: string;
|
|
12
|
+
header?: React.ReactNode;
|
|
13
|
+
sidebar: React.ReactNode;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const LaunchpadContainer: React.FunctionComponent< ContainerProps > = ( {
|
|
18
|
+
className,
|
|
19
|
+
headerClassName,
|
|
20
|
+
contentClassName,
|
|
21
|
+
sidebarClassName,
|
|
22
|
+
mainContentClassName,
|
|
23
|
+
header,
|
|
24
|
+
sidebar,
|
|
25
|
+
children,
|
|
26
|
+
} ) => {
|
|
27
|
+
return (
|
|
28
|
+
<main className={ clsx( 'launchpad-container', className ) }>
|
|
29
|
+
{ header && (
|
|
30
|
+
<div className={ clsx( 'launchpad-container__header', headerClassName ) }>{ header }</div>
|
|
31
|
+
) }
|
|
32
|
+
|
|
33
|
+
<div className={ clsx( 'launchpad-container__content', contentClassName ) }>
|
|
34
|
+
<div className={ clsx( 'launchpad-container__sidebar', sidebarClassName ) }>
|
|
35
|
+
{ sidebar }
|
|
36
|
+
</div>
|
|
37
|
+
<div className={ clsx( 'launchpad-container__main-content', mainContentClassName ) }>
|
|
38
|
+
{ children }
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</main>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default LaunchpadContainer;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
@import "@wordpress/base-styles/mixins";
|
|
2
|
+
@import "@wordpress/base-styles/breakpoints";
|
|
3
|
+
|
|
4
|
+
.launchpad-container {
|
|
5
|
+
height: 100%;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Launchpad Content.
|
|
11
|
+
.launchpad-container__content {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
|
|
15
|
+
@include break-large {
|
|
16
|
+
height: 100%;
|
|
17
|
+
min-height: calc(100vh - 60px); // Discount margin-top from `.step-route`.
|
|
18
|
+
flex-direction: row;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Launchpad Sidebar.
|
|
23
|
+
.launchpad-container__sidebar {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
max-width: 100%;
|
|
27
|
+
|
|
28
|
+
@include break-large {
|
|
29
|
+
max-width: 360px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// TODO: Migrate to the checklist components. The checklist could receive a special className as prop that would be responsible for this.
|
|
33
|
+
.launchpad-container .checklist__tasks .checklist-item__task-content.is-placeholder .checklist-item__content {
|
|
34
|
+
/* Override default placeholder colours to align with other elements on the page. */
|
|
35
|
+
background-color: var(--color-neutral-5);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Main content.
|
|
40
|
+
.launchpad-container__main-content {
|
|
41
|
+
flex-grow: 1;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Container spacings.
|
|
47
|
+
.launchpad-container__sidebar,
|
|
48
|
+
.launchpad-container__main-content {
|
|
49
|
+
padding: 16px;
|
|
50
|
+
|
|
51
|
+
@include break-large {
|
|
52
|
+
padding: 40px;
|
|
53
|
+
|
|
54
|
+
// Exception for the sidebar.
|
|
55
|
+
&.launchpad-container__sidebar {
|
|
56
|
+
padding-right: 32px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import LaunchpadContainer from '../';
|
|
4
|
+
|
|
5
|
+
describe( 'LaunchpadContainer', () => {
|
|
6
|
+
it( 'renders the container with sidebar and content', () => {
|
|
7
|
+
const { getByText } = render(
|
|
8
|
+
<LaunchpadContainer sidebar={ <div>Sidebar</div> }>My Content</LaunchpadContainer>
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
expect( getByText( 'Sidebar' ) ).toBeInTheDocument();
|
|
12
|
+
expect( getByText( 'My Content' ) ).toBeInTheDocument();
|
|
13
|
+
} );
|
|
14
|
+
|
|
15
|
+
it( 'renders the container with header', () => {
|
|
16
|
+
const { getByText } = render(
|
|
17
|
+
<LaunchpadContainer sidebar={ <div>Sidebar</div> } header={ <div>Header</div> }>
|
|
18
|
+
My Content
|
|
19
|
+
</LaunchpadContainer>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect( getByText( 'Header' ) ).toBeInTheDocument();
|
|
23
|
+
} );
|
|
24
|
+
|
|
25
|
+
it( 'renders the container without header', () => {
|
|
26
|
+
const { container } = render(
|
|
27
|
+
<LaunchpadContainer sidebar={ <div>Sidebar</div> }>My Content</LaunchpadContainer>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect( container.querySelector( '.launchpad-container__header' ) ).not.toBeInTheDocument();
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'renders the container with custom classes', () => {
|
|
34
|
+
const { container } = render(
|
|
35
|
+
<LaunchpadContainer
|
|
36
|
+
className="classname"
|
|
37
|
+
headerClassName="header-classname"
|
|
38
|
+
contentClassName="content-classname"
|
|
39
|
+
sidebarClassName="sidebar-classname"
|
|
40
|
+
mainContentClassName="main-content-classname"
|
|
41
|
+
sidebar={ <div>Sidebar</div> }
|
|
42
|
+
header={ <div>Header</div> }
|
|
43
|
+
>
|
|
44
|
+
My Content
|
|
45
|
+
</LaunchpadContainer>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(
|
|
49
|
+
container.querySelector( '.launchpad-container' )?.classList.contains( 'classname' )
|
|
50
|
+
).toBeTruthy();
|
|
51
|
+
expect(
|
|
52
|
+
container
|
|
53
|
+
.querySelector( '.launchpad-container__header' )
|
|
54
|
+
?.classList.contains( 'header-classname' )
|
|
55
|
+
).toBeTruthy();
|
|
56
|
+
expect(
|
|
57
|
+
container
|
|
58
|
+
.querySelector( '.launchpad-container__content' )
|
|
59
|
+
?.classList.contains( 'content-classname' )
|
|
60
|
+
).toBeTruthy();
|
|
61
|
+
expect(
|
|
62
|
+
container
|
|
63
|
+
.querySelector( '.launchpad-container__sidebar' )
|
|
64
|
+
?.classList.contains( 'sidebar-classname' )
|
|
65
|
+
).toBeTruthy();
|
|
66
|
+
expect(
|
|
67
|
+
container
|
|
68
|
+
.querySelector( '.launchpad-container__main-content' )
|
|
69
|
+
?.classList.contains( 'main-content-classname' )
|
|
70
|
+
).toBeTruthy();
|
|
71
|
+
} );
|
|
72
|
+
} );
|