@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.
Files changed (145) hide show
  1. package/.storybook/main.ts +16 -0
  2. package/.storybook/preview.ts +6 -0
  3. package/README.md +11 -2
  4. package/dist/cjs/action-components/index.js +6 -0
  5. package/dist/cjs/action-components/index.js.map +1 -0
  6. package/dist/cjs/action-components/share-site-modal/index.js +44 -0
  7. package/dist/cjs/action-components/share-site-modal/index.js.map +1 -0
  8. package/dist/cjs/action-components/share-site-modal/style.scss +159 -0
  9. package/dist/cjs/badge/index.js +2 -2
  10. package/dist/cjs/badge/index.js.map +1 -1
  11. package/dist/cjs/checklist/index.js +16 -8
  12. package/dist/cjs/checklist/index.js.map +1 -1
  13. package/dist/cjs/checklist/index.stories.js +29 -0
  14. package/dist/cjs/checklist/index.stories.js.map +1 -0
  15. package/dist/cjs/checklist-item/index.js +30 -13
  16. package/dist/cjs/checklist-item/index.js.map +1 -1
  17. package/dist/cjs/checklist-item/index.stories.js +79 -0
  18. package/dist/cjs/checklist-item/index.stories.js.map +1 -0
  19. package/dist/cjs/checklist-item/style.scss +142 -22
  20. package/dist/cjs/index.js +6 -1
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/launchpad-container/index.js +11 -0
  23. package/dist/cjs/launchpad-container/index.js.map +1 -0
  24. package/dist/cjs/launchpad-container/style.scss +59 -0
  25. package/dist/cjs/launchpad-internal.js +40 -0
  26. package/dist/cjs/launchpad-internal.js.map +1 -0
  27. package/dist/cjs/launchpad-internal.stories.js +45 -0
  28. package/dist/cjs/launchpad-internal.stories.js.map +1 -0
  29. package/dist/cjs/launchpad.js +39 -10
  30. package/dist/cjs/launchpad.js.map +1 -1
  31. package/dist/cjs/msw/link-in-bio.js +68 -0
  32. package/dist/cjs/msw/link-in-bio.js.map +1 -0
  33. package/dist/cjs/msw/resolver.js +19 -0
  34. package/dist/cjs/msw/resolver.js.map +1 -0
  35. package/dist/cjs/msw/setup-free.js +88 -0
  36. package/dist/cjs/msw/setup-free.js.map +1 -0
  37. package/dist/cjs/setup-actions.js +51 -54
  38. package/dist/cjs/setup-actions.js.map +1 -1
  39. package/dist/cjs/use-tracking/index.js +72 -0
  40. package/dist/cjs/use-tracking/index.js.map +1 -0
  41. package/dist/esm/action-components/index.js +2 -0
  42. package/dist/esm/action-components/index.js.map +1 -0
  43. package/dist/esm/action-components/share-site-modal/index.js +42 -0
  44. package/dist/esm/action-components/share-site-modal/index.js.map +1 -0
  45. package/dist/esm/action-components/share-site-modal/style.scss +159 -0
  46. package/dist/esm/badge/index.js +2 -2
  47. package/dist/esm/badge/index.js.map +1 -1
  48. package/dist/esm/checklist/index.js +14 -8
  49. package/dist/esm/checklist/index.js.map +1 -1
  50. package/dist/esm/checklist/index.stories.js +25 -0
  51. package/dist/esm/checklist/index.stories.js.map +1 -0
  52. package/dist/esm/checklist-item/index.js +29 -14
  53. package/dist/esm/checklist-item/index.js.map +1 -1
  54. package/dist/esm/checklist-item/index.stories.js +75 -0
  55. package/dist/esm/checklist-item/index.stories.js.map +1 -0
  56. package/dist/esm/checklist-item/style.scss +142 -22
  57. package/dist/esm/index.js +3 -0
  58. package/dist/esm/index.js.map +1 -1
  59. package/dist/esm/launchpad-container/index.js +8 -0
  60. package/dist/esm/launchpad-container/index.js.map +1 -0
  61. package/dist/esm/launchpad-container/style.scss +59 -0
  62. package/dist/esm/launchpad-internal.js +37 -0
  63. package/dist/esm/launchpad-internal.js.map +1 -0
  64. package/dist/esm/launchpad-internal.stories.js +41 -0
  65. package/dist/esm/launchpad-internal.stories.js.map +1 -0
  66. package/dist/esm/launchpad.js +41 -13
  67. package/dist/esm/launchpad.js.map +1 -1
  68. package/dist/esm/msw/link-in-bio.js +66 -0
  69. package/dist/esm/msw/link-in-bio.js.map +1 -0
  70. package/dist/esm/msw/resolver.js +14 -0
  71. package/dist/esm/msw/resolver.js.map +1 -0
  72. package/dist/esm/msw/setup-free.js +86 -0
  73. package/dist/esm/msw/setup-free.js.map +1 -0
  74. package/dist/esm/setup-actions.js +51 -54
  75. package/dist/esm/setup-actions.js.map +1 -1
  76. package/dist/esm/use-tracking/index.js +68 -0
  77. package/dist/esm/use-tracking/index.js.map +1 -0
  78. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/dist/types/action-components/index.d.ts +2 -0
  81. package/dist/types/action-components/index.d.ts.map +1 -0
  82. package/dist/types/action-components/share-site-modal/index.d.ts +9 -0
  83. package/dist/types/action-components/share-site-modal/index.d.ts.map +1 -0
  84. package/dist/types/badge/index.d.ts.map +1 -1
  85. package/dist/types/checklist/index.d.ts +6 -7
  86. package/dist/types/checklist/index.d.ts.map +1 -1
  87. package/dist/types/checklist/index.stories.d.ts +8 -0
  88. package/dist/types/checklist/index.stories.d.ts.map +1 -0
  89. package/dist/types/checklist-item/index.d.ts +11 -8
  90. package/dist/types/checklist-item/index.d.ts.map +1 -1
  91. package/dist/types/checklist-item/index.stories.d.ts +14 -0
  92. package/dist/types/checklist-item/index.stories.d.ts.map +1 -0
  93. package/dist/types/index.d.ts +3 -0
  94. package/dist/types/index.d.ts.map +1 -1
  95. package/dist/types/launchpad-container/index.d.ts +15 -0
  96. package/dist/types/launchpad-container/index.d.ts.map +1 -0
  97. package/dist/types/launchpad-internal.d.ts +20 -0
  98. package/dist/types/launchpad-internal.d.ts.map +1 -0
  99. package/dist/types/launchpad-internal.stories.d.ts +8 -0
  100. package/dist/types/launchpad-internal.stories.d.ts.map +1 -0
  101. package/dist/types/launchpad.d.ts +10 -9
  102. package/dist/types/launchpad.d.ts.map +1 -1
  103. package/dist/types/msw/link-in-bio.d.ts +35 -0
  104. package/dist/types/msw/link-in-bio.d.ts.map +1 -0
  105. package/dist/types/msw/resolver.d.ts +3 -0
  106. package/dist/types/msw/resolver.d.ts.map +1 -0
  107. package/dist/types/msw/setup-free.d.ts +35 -0
  108. package/dist/types/msw/setup-free.d.ts.map +1 -0
  109. package/dist/types/setup-actions.d.ts +1 -1
  110. package/dist/types/setup-actions.d.ts.map +1 -1
  111. package/dist/types/types.d.ts +19 -9
  112. package/dist/types/types.d.ts.map +1 -1
  113. package/dist/types/use-tracking/index.d.ts +14 -0
  114. package/dist/types/use-tracking/index.d.ts.map +1 -0
  115. package/package.json +24 -16
  116. package/src/action-components/index.ts +1 -0
  117. package/src/action-components/share-site-modal/index.tsx +100 -0
  118. package/src/action-components/share-site-modal/style.scss +159 -0
  119. package/src/badge/index.tsx +2 -4
  120. package/src/checklist/index.stories.tsx +45 -0
  121. package/src/checklist/index.tsx +22 -21
  122. package/src/checklist/test/index.tsx +26 -0
  123. package/src/checklist-item/index.stories.tsx +92 -0
  124. package/src/checklist-item/index.tsx +62 -22
  125. package/src/checklist-item/style.scss +142 -22
  126. package/src/checklist-item/test/index.tsx +232 -0
  127. package/src/index.ts +3 -0
  128. package/src/launchpad-container/index.tsx +45 -0
  129. package/src/launchpad-container/style.scss +59 -0
  130. package/src/launchpad-container/test/index.tsx +72 -0
  131. package/src/launchpad-internal.stories.tsx +49 -0
  132. package/src/launchpad-internal.tsx +85 -0
  133. package/src/launchpad.tsx +80 -30
  134. package/src/msw/link-in-bio.ts +66 -0
  135. package/src/msw/resolver.ts +18 -0
  136. package/src/msw/setup-free.ts +85 -0
  137. package/src/setup-actions.ts +67 -54
  138. package/src/test/launchpad.tsx +51 -24
  139. package/src/test/lib/fixtures.ts +7 -2
  140. package/src/types.ts +24 -9
  141. package/src/use-tracking/index.tsx +87 -0
  142. package/src/use-tracking/test/index.tsx +116 -0
  143. package/static/mockServiceWorker.js +287 -0
  144. package/src/test/checklist-item.tsx +0 -96
  145. package/src/test/checklist.tsx +0 -25
@@ -0,0 +1,159 @@
1
+ @import "@automattic/typography/styles/variables";
2
+
3
+ .share-site-modal__modal {
4
+ max-width: 640px;
5
+
6
+ .components-modal__header {
7
+ padding: 48px;
8
+ }
9
+
10
+ .components-modal__content {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+
15
+ .share-site-modal__modal-content {
16
+ padding: 48px;
17
+ padding-bottom: 40px;
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: 32px;
21
+ }
22
+
23
+ .share-site-modal__modal-heading {
24
+ color: var(--studio-gray-100);
25
+ font-family: Recoleta, "Noto Serif", Georgia, "Times New Roman", Times, serif;
26
+ font-size: 2rem;
27
+ line-height: 40px;
28
+ font-weight: 400;
29
+ letter-spacing: 0.2px;
30
+ margin: 0;
31
+ padding-bottom: 8px;
32
+ }
33
+
34
+ .share-site-modal__modal-domain {
35
+ display: flex;
36
+ justify-content: center;
37
+ align-items: center;
38
+ }
39
+
40
+ .share-site-modal__modal-body,
41
+ .share-site-modal__modal-domain-text {
42
+ margin: 0;
43
+ color: var(--studio-gray-80);
44
+ font-size: 1rem;
45
+ line-height: 24px;
46
+ }
47
+
48
+ .share-site-modal__modal-domain-text {
49
+ padding: 0 8px;
50
+
51
+ // prevent text overflow
52
+ text-overflow: ellipsis;
53
+ white-space: nowrap;
54
+ overflow: hidden;
55
+ }
56
+
57
+ .share-site-modal__modal-buttons {
58
+ display: flex;
59
+ flex-direction: row;
60
+ justify-content: end;
61
+ gap: 16px;
62
+ }
63
+
64
+ .share-site-modal__modal-site {
65
+ padding: 8px;
66
+ background: var(--studio-gray-0);
67
+ display: flex;
68
+ flex-direction: row;
69
+ justify-content: space-between;
70
+ align-items: center;
71
+ }
72
+
73
+ .launchpad__clipboard-button {
74
+ opacity: 0;
75
+ }
76
+
77
+ .launchpad__clipboard-button:focus {
78
+ opacity: 1;
79
+ }
80
+
81
+ .share-site-modal__modal-site:hover {
82
+ .launchpad__clipboard-button {
83
+ opacity: 1;
84
+ }
85
+ }
86
+
87
+ .share-site-modal__modal-customize {
88
+ color: var(--studio-blue-50);
89
+ font-size: 0.875rem;
90
+ display: inline-flex;
91
+ flex-direction: row;
92
+ justify-content: start;
93
+ gap: 6px;
94
+ padding: 0;
95
+ margin: 0;
96
+ margin-top: 16px;
97
+ }
98
+
99
+ .share-site-modal__modal-upsell {
100
+ border-top: 1px solid var(--studio-gray-5);
101
+ background: var(--studio-gray-0);
102
+ display: flex;
103
+ justify-content: center;
104
+ align-items: center;
105
+ padding: 32px 48px;
106
+ gap: 32px;
107
+
108
+ .components-button {
109
+ height: 42px;
110
+ }
111
+ }
112
+
113
+ .share-site-modal__modal-upsell-content p {
114
+ margin-bottom: 0;
115
+ }
116
+
117
+ .share-site-modal__modal-upsell-content-highlight {
118
+ font-weight: bold;
119
+ }
120
+
121
+ .share-site-modal__modal-view-site {
122
+ display: flex;
123
+ gap: 4px;
124
+
125
+ font-weight: 500;
126
+ /* stylelint-disable-next-line */
127
+ font-size: 14px;
128
+ line-height: 20px;
129
+ letter-spacing: -0.154px;
130
+ color: #101517;
131
+ }
132
+
133
+ .share-site-modal__modal-view-site:visited {
134
+ color: #101517;
135
+ }
136
+
137
+ .share-site-modal__modal-view-site:hover:not(:disabled) {
138
+ text-decoration: underline;
139
+ }
140
+ }
141
+
142
+ .share-site-modal__popover {
143
+ outline: none;
144
+ z-index: 100000;
145
+ .popover__inner {
146
+ border: 0;
147
+ box-shadow: none;
148
+ border-radius: 2px;
149
+ color: var(--color-text-inverted);
150
+ background: var(--color-neutral-60);
151
+ font-size: $font-body-extra-small;
152
+ padding: 6px 10px;
153
+ text-align: left;
154
+ }
155
+
156
+ .popover__arrow {
157
+ display: none;
158
+ }
159
+ }
@@ -1,4 +1,4 @@
1
- import classNames from 'classnames';
1
+ import clsx from 'clsx';
2
2
 
3
3
  import './style.scss';
4
4
 
@@ -19,9 +19,7 @@ export interface BadgeProps {
19
19
  const Badge: React.FunctionComponent< BadgeProps > = ( props ) => {
20
20
  const className = props.className;
21
21
  const type = props.type || 'warning';
22
- return (
23
- <div className={ classNames( `badge badge--${ type }`, className ) }>{ props.children }</div>
24
- );
22
+ return <div className={ clsx( `badge badge--${ type }`, className ) }>{ props.children }</div>;
25
23
  };
26
24
 
27
25
  export default Badge;
@@ -0,0 +1,45 @@
1
+ import ChecklistItem from '../checklist-item';
2
+ import Checklist from './';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+
5
+ const meta: Meta< typeof Checklist > = {
6
+ title: 'Launchpad/Checklist',
7
+ component: Checklist,
8
+ decorators: [
9
+ ( Story ) => (
10
+ <div style={ { width: 300 } }>
11
+ <Story />
12
+ </div>
13
+ ),
14
+ ],
15
+ args: {
16
+ children: [
17
+ <ChecklistItem
18
+ key="1"
19
+ task={ { id: '1', title: 'Task 1', completed: true, disabled: true } }
20
+ onClick={ () => {} }
21
+ />,
22
+ <ChecklistItem
23
+ key="2"
24
+ task={ { id: '2', title: 'Task 2', disabled: false, completed: false } }
25
+ onClick={ () => {} }
26
+ />,
27
+ <ChecklistItem
28
+ key="3"
29
+ task={ { id: '3', title: 'Task 3', disabled: false, completed: false } }
30
+ onClick={ () => {} }
31
+ />,
32
+ ],
33
+ },
34
+ };
35
+
36
+ type Story = StoryObj< typeof meta >;
37
+ export const Default: Story = {};
38
+
39
+ export const WithMakeLastTaskPrimaryAction: Story = {
40
+ args: {
41
+ makeLastTaskPrimaryAction: true,
42
+ },
43
+ };
44
+
45
+ export default meta;
@@ -1,41 +1,42 @@
1
- import classNames from 'classnames';
2
- import ChecklistItem from '../checklist-item';
3
- import { Task } from '../types';
4
-
1
+ import clsx from 'clsx';
2
+ import { Children, cloneElement, type FC, type ReactElement, type ComponentProps } from 'react';
3
+ import ChecklistItem, { Placeholder as ChecklistItemPlaceholder } from '../checklist-item';
5
4
  import './style.scss';
6
5
 
7
- interface ChecklistProps {
8
- tasks: Task[] | null;
6
+ interface Props {
9
7
  makeLastTaskPrimaryAction?: boolean;
8
+ children?:
9
+ | ReactElement< ComponentProps< typeof ChecklistItem > >
10
+ | ReactElement< ComponentProps< typeof ChecklistItem > >[];
10
11
  }
11
12
 
12
- const Checklist = ( { tasks, makeLastTaskPrimaryAction }: ChecklistProps ) => {
13
+ const Checklist: FC< Props > = ( { children, makeLastTaskPrimaryAction } ) => {
14
+ const lastChildIndex = Children.count( children ) - 1;
15
+
13
16
  return (
14
17
  <ul
15
- className={ classNames( 'checklist__tasks', {
18
+ className={ clsx( 'checklist__tasks', {
16
19
  'checklist__has-primary-action': makeLastTaskPrimaryAction,
17
20
  } ) }
18
21
  aria-label="Launchpad Checklist"
19
22
  >
20
- { tasks &&
21
- tasks.map( ( task: Task, index: number ) => (
22
- <ChecklistItem
23
- key={ task.id }
24
- task={ task }
25
- isPrimaryAction={ makeLastTaskPrimaryAction && index === tasks.length - 1 }
26
- />
27
- ) ) }
23
+ { Children.map( children || [], ( child, index ) => {
24
+ if ( index === lastChildIndex ) {
25
+ return cloneElement( child, { isPrimaryAction: makeLastTaskPrimaryAction } );
26
+ }
27
+ return child;
28
+ } ) }
28
29
  </ul>
29
30
  );
30
31
  };
31
32
 
32
- Checklist.Placeholder = () => {
33
+ export const Placeholder = () => {
33
34
  return (
34
35
  <ul className="checklist__tasks" aria-label="Launchpad Checklist">
35
- <ChecklistItem.Placeholder />
36
- <ChecklistItem.Placeholder />
37
- <ChecklistItem.Placeholder />
38
- <ChecklistItem.Placeholder />
36
+ <ChecklistItemPlaceholder />
37
+ <ChecklistItemPlaceholder />
38
+ <ChecklistItemPlaceholder />
39
+ <ChecklistItemPlaceholder />
39
40
  </ul>
40
41
  );
41
42
  };
@@ -0,0 +1,26 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import React from 'react';
3
+ import Checklist from '../';
4
+ import ChecklistItem from '../../checklist-item';
5
+ import { buildTask } from '../../test/lib/fixtures';
6
+ import '@testing-library/jest-dom';
7
+
8
+ describe( 'Checklist', () => {
9
+ it( 'renders the list of items', () => {
10
+ render(
11
+ <Checklist>
12
+ <ChecklistItem key={ 0 } task={ buildTask( {} ) } />
13
+ <ChecklistItem key={ 1 } task={ buildTask( {} ) } />
14
+ </Checklist>
15
+ );
16
+ const checklistItems = screen.getAllByRole( 'listitem' );
17
+ expect( checklistItems.length ).toBe( 2 );
18
+ } );
19
+
20
+ it( 'renders a empty list when provided no tasks', () => {
21
+ render( <Checklist /> );
22
+ const checklistItems = screen.queryByRole( 'listitem' );
23
+
24
+ expect( checklistItems ).not.toBeInTheDocument();
25
+ } );
26
+ } );
@@ -0,0 +1,92 @@
1
+ import ChecklistItem from './';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ const meta: Meta< typeof ChecklistItem > = {
5
+ title: 'Launchpad/ChecklistItem',
6
+ component: ChecklistItem,
7
+ decorators: [
8
+ ( Story ) => (
9
+ <div style={ { width: 300 } }>
10
+ <ul className="checklist__tasks">
11
+ <Story />
12
+ </ul>
13
+ </div>
14
+ ),
15
+ ],
16
+ };
17
+
18
+ type Story = StoryObj< typeof meta >;
19
+ const baseTask = {
20
+ id: '1',
21
+ title: 'Task 1',
22
+ completed: false,
23
+ disabled: false,
24
+ };
25
+
26
+ export const Default: Story = {
27
+ args: {
28
+ onClick: () => {},
29
+ task: baseTask,
30
+ },
31
+ };
32
+
33
+ export const Completed: Story = {
34
+ args: {
35
+ onClick: () => {},
36
+ task: { ...baseTask, completed: true },
37
+ },
38
+ };
39
+
40
+ export const Disabled: Story = {
41
+ args: {
42
+ onClick: () => {},
43
+ task: { ...baseTask, disabled: true },
44
+ },
45
+ };
46
+
47
+ export const WithBadgetText: Story = {
48
+ args: {
49
+ onClick: () => {},
50
+ task: { ...baseTask, badge_text: 'Upgrade plan' },
51
+ },
52
+ };
53
+
54
+ export const WithTaskCounter: Story = {
55
+ args: {
56
+ onClick: () => {},
57
+ task: { ...baseTask, repetition_count: 1, target_repetitions: 3 },
58
+ },
59
+ };
60
+
61
+ export const WithSubtitle: Story = {
62
+ args: {
63
+ onClick: () => {},
64
+ task: { ...baseTask, subtitle: 'This is a subtitle' },
65
+ },
66
+ };
67
+
68
+ export const Expanded: Story = {
69
+ args: {
70
+ task: baseTask,
71
+ expandable: {
72
+ content: <p>This is the expanded content.</p>,
73
+ isOpen: true,
74
+ },
75
+ },
76
+ };
77
+
78
+ export const ExpandedWithAction: Story = {
79
+ args: {
80
+ task: baseTask,
81
+ expandable: {
82
+ content: <p>This is the expanded content.</p>,
83
+ isOpen: true,
84
+ action: {
85
+ label: 'Next',
86
+ onClick: () => {},
87
+ },
88
+ },
89
+ },
90
+ };
91
+
92
+ export default meta;
@@ -1,52 +1,79 @@
1
- import { Badge, Button, Gridicon } from '@automattic/components';
2
- import classnames from 'classnames';
1
+ import { Badge, Gridicon } from '@automattic/components';
2
+ import { Button } from '@wordpress/components';
3
+ import clsx from 'clsx';
3
4
  import { translate, useRtl } from 'i18n-calypso';
4
- import { Task } from '../types';
5
+ import type { Task, Expandable } from '../types';
6
+ import type { FC, Key } from 'react';
5
7
 
6
8
  import './style.scss';
7
9
 
8
- const ChecklistItem = ( { task, isPrimaryAction }: { task: Task; isPrimaryAction?: boolean } ) => {
10
+ interface Props {
11
+ key?: Key;
12
+ task: Task;
13
+ isPrimaryAction?: boolean;
14
+ expandable?: Expandable;
15
+ onClick?: () => void;
16
+ }
17
+
18
+ const ChecklistItem: FC< Props > = ( { task, isPrimaryAction, expandable, onClick } ) => {
9
19
  const isRtl = useRtl();
10
- const { id, completed, disabled, title, subtitle, actionDispatch } = task;
20
+ const { id, completed, disabled = false, title, subtitle, actionDispatch } = task;
11
21
 
12
- // Display chevron if task is incomplete. Don't display chevron and badge at the same time.
13
- const shouldDisplayChevron = ! completed && ! disabled && ! task.badge_text;
22
+ // If the task says we should use the Calypso path, ensure we use that link for the button's href.
23
+ // This allows the UI routing code to hook into the URL changes and should reduce full-page (re)loads
24
+ // when clicking on the task list items.
25
+ const buttonHref = task.useCalypsoPath && task.calypso_path ? task.calypso_path : undefined;
14
26
 
15
- const handlePrimaryAction = () => {
16
- localStorage.removeItem( 'launchpad_siteSlug' );
17
- actionDispatch && actionDispatch();
27
+ // The Button component does not accept the `disabled` and `href` props together.
28
+ // This code will only add href property if the disabled variable is false.
29
+ const buttonProps = {
30
+ disabled,
31
+ ...( disabled ? {} : { href: buttonHref } ),
18
32
  };
19
33
 
34
+ const onClickHandler = onClick || actionDispatch;
35
+ const isClickable = !! ( buttonProps.href || onClickHandler );
36
+
37
+ // Display chevron if task is incomplete.
38
+ // Don't display chevron and badge at the same time.
39
+ // Don't display chevron if item is not clickable.
40
+ const shouldDisplayChevron = ! completed && ! disabled && ! task.badge_text && isClickable;
41
+
20
42
  // Display task counter if task is incomplete and has the count properties;
21
43
  const shouldDisplayTaskCounter =
22
44
  task.target_repetitions &&
23
45
  null !== task.repetition_count &&
24
46
  undefined !== task.repetition_count;
25
47
 
48
+ // Make sure the button keeps the button styles if it is not clickable.
49
+ const buttonClassName = isClickable ? '' : 'components-button';
50
+ const ButtonElement = isClickable ? Button : 'div';
51
+
26
52
  return (
27
53
  <li
28
- className={ classnames( 'checklist-item__task', {
54
+ className={ clsx( 'checklist-item__task', {
29
55
  completed: completed,
30
56
  pending: ! completed,
31
57
  enabled: ! disabled,
32
58
  disabled: disabled,
59
+ expanded: expandable && expandable.isOpen,
33
60
  } ) }
34
61
  >
35
62
  { isPrimaryAction ? (
36
- <Button
37
- className="checklist-item__checklist-primary-button"
38
- disabled={ disabled }
63
+ <ButtonElement
64
+ className={ clsx( 'checklist-item__checklist-primary-button', buttonClassName ) }
39
65
  data-task={ id }
40
- onClick={ handlePrimaryAction }
66
+ onClick={ onClickHandler }
67
+ { ...buttonProps }
41
68
  >
42
69
  { title }
43
- </Button>
70
+ </ButtonElement>
44
71
  ) : (
45
- <Button
46
- className="checklist-item__task-content"
47
- disabled={ disabled }
72
+ <ButtonElement
73
+ className={ clsx( 'checklist-item__task-content', buttonClassName ) }
48
74
  data-task={ id }
49
- onClick={ actionDispatch }
75
+ onClick={ onClickHandler }
76
+ { ...buttonProps }
50
77
  >
51
78
  { completed && (
52
79
  // show checkmark for completed tasks regardless if they are disabled or kept active
@@ -75,13 +102,26 @@ const ChecklistItem = ( { task, isPrimaryAction }: { task: Task; isPrimaryAction
75
102
  />
76
103
  ) }
77
104
  { subtitle && <p className="checklist-item__subtext">{ subtitle }</p> }
78
- </Button>
105
+ </ButtonElement>
106
+ ) }
107
+ { expandable && expandable.isOpen && (
108
+ <div className="checklist-item__expanded-content">
109
+ { expandable.content }
110
+ { expandable.action && (
111
+ <Button
112
+ className="checklist-item__checklist-expanded-action-button"
113
+ onClick={ expandable.action.onClick }
114
+ >
115
+ { expandable.action.label }
116
+ </Button>
117
+ ) }
118
+ </div>
79
119
  ) }
80
120
  </li>
81
121
  );
82
122
  };
83
123
 
84
- ChecklistItem.Placeholder = () => {
124
+ export const Placeholder: FC = () => {
85
125
  return (
86
126
  <div className="checklist-item__task-content is-placeholder">
87
127
  <div className="checklist-item__content">