@workday/canvas-kit-preview-react 9.0.0-alpha.338-next.5 → 9.0.0-alpha.344-next.3

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 (154) hide show
  1. package/dist/commonjs/form-field/lib/FormField.d.ts +2 -2
  2. package/dist/commonjs/form-field/lib/FormFieldHint.d.ts +1 -1
  3. package/dist/commonjs/form-field/lib/FormFieldInput.d.ts +1 -1
  4. package/dist/commonjs/form-field/lib/hooks/useFormFieldHint.d.ts +1 -1
  5. package/dist/commonjs/form-field/lib/hooks/useFormFieldInput.d.ts +1 -1
  6. package/dist/commonjs/form-field/lib/hooks/useFormFieldLabel.d.ts +1 -1
  7. package/dist/commonjs/form-field/lib/hooks/useFormFieldModel.d.ts +2 -12
  8. package/dist/commonjs/form-field/lib/hooks/useFormFieldModel.d.ts.map +1 -1
  9. package/dist/commonjs/form-field/lib/hooks/useFormFieldModel.js +1 -3
  10. package/dist/commonjs/menu/lib/Menu.d.ts +12 -5
  11. package/dist/commonjs/menu/lib/Menu.d.ts.map +1 -1
  12. package/dist/commonjs/menu/lib/Menu.js +14 -5
  13. package/dist/commonjs/menu/lib/MenuItem.d.ts +15 -5
  14. package/dist/commonjs/menu/lib/MenuItem.d.ts.map +1 -1
  15. package/dist/commonjs/menu/lib/MenuItem.js +18 -6
  16. package/dist/commonjs/pill/lib/Pill.d.ts +103 -5
  17. package/dist/commonjs/pill/lib/Pill.d.ts.map +1 -1
  18. package/dist/commonjs/pill/lib/Pill.js +101 -2
  19. package/dist/commonjs/segmented-control/index.d.ts +2 -0
  20. package/dist/commonjs/segmented-control/index.d.ts.map +1 -1
  21. package/dist/commonjs/segmented-control/index.js +4 -0
  22. package/dist/commonjs/segmented-control/lib/SegmentedControl.d.ts +74 -38
  23. package/dist/commonjs/segmented-control/lib/SegmentedControl.d.ts.map +1 -1
  24. package/dist/commonjs/segmented-control/lib/SegmentedControl.js +36 -0
  25. package/dist/commonjs/segmented-control/lib/SegmentedControlItem.d.ts +1 -1
  26. package/dist/commonjs/segmented-control/lib/SegmentedControlList.d.ts +64 -1
  27. package/dist/commonjs/segmented-control/lib/SegmentedControlList.d.ts.map +1 -1
  28. package/dist/commonjs/segmented-control/lib/SegmentedControlList.js +3 -3
  29. package/dist/commonjs/segmented-control/lib/hooks/useSegmentedControlItem.d.ts +2 -2
  30. package/dist/commonjs/segmented-control/lib/hooks/useSegmentedControlModel.d.ts +39 -39
  31. package/dist/commonjs/segmented-control/lib/hooks/useSegmentedControlModel.js +2 -1
  32. package/dist/commonjs/select/lib/Select.js +2 -0
  33. package/dist/commonjs/side-panel/lib/SidePanel.d.ts +13 -26
  34. package/dist/commonjs/side-panel/lib/SidePanel.d.ts.map +1 -1
  35. package/dist/commonjs/side-panel/lib/SidePanel.js +73 -96
  36. package/dist/commonjs/side-panel/lib/SidePanelToggleButton.d.ts +17 -0
  37. package/dist/commonjs/side-panel/lib/SidePanelToggleButton.d.ts.map +1 -0
  38. package/dist/commonjs/side-panel/lib/SidePanelToggleButton.js +88 -0
  39. package/dist/commonjs/side-panel/lib/hooks.d.ts +20 -0
  40. package/dist/commonjs/side-panel/lib/hooks.d.ts.map +1 -1
  41. package/dist/commonjs/side-panel/lib/hooks.js +20 -1
  42. package/dist/commonjs/status-indicator/index.d.ts +2 -0
  43. package/dist/commonjs/status-indicator/index.d.ts.map +1 -1
  44. package/dist/commonjs/status-indicator/index.js +4 -0
  45. package/dist/commonjs/status-indicator/lib/StatusIndicator.d.ts +28 -2
  46. package/dist/commonjs/status-indicator/lib/StatusIndicator.d.ts.map +1 -1
  47. package/dist/commonjs/status-indicator/lib/StatusIndicator.js +27 -1
  48. package/dist/commonjs/status-indicator/lib/StatusIndicatorIcon.d.ts +13 -0
  49. package/dist/commonjs/status-indicator/lib/StatusIndicatorIcon.d.ts.map +1 -1
  50. package/dist/commonjs/status-indicator/lib/StatusIndicatorIcon.js +3 -3
  51. package/dist/commonjs/text-area/lib/TextArea.d.ts +1 -1
  52. package/dist/commonjs/text-area/lib/TextArea.d.ts.map +1 -1
  53. package/dist/commonjs/text-area/lib/TextArea.js +1 -2
  54. package/dist/commonjs/text-input/lib/TextInput.d.ts +2 -2
  55. package/dist/commonjs/text-input/lib/TextInput.d.ts.map +1 -1
  56. package/dist/commonjs/text-input/lib/TextInput.js +1 -2
  57. package/dist/commonjs/text-input/lib/TextInputField.js +1 -1
  58. package/dist/commonjs/text-input/lib/hooks/useTextInputField.d.ts +1 -1
  59. package/dist/commonjs/text-input/lib/hooks/useTextInputModel.d.ts +1 -0
  60. package/dist/commonjs/text-input/lib/hooks/useTextInputModel.d.ts.map +1 -1
  61. package/dist/commonjs/text-input/lib/hooks/useTextInputModel.js +1 -0
  62. package/dist/es6/form-field/lib/FormField.d.ts +2 -2
  63. package/dist/es6/form-field/lib/FormFieldHint.d.ts +1 -1
  64. package/dist/es6/form-field/lib/FormFieldInput.d.ts +1 -1
  65. package/dist/es6/form-field/lib/hooks/useFormFieldHint.d.ts +1 -1
  66. package/dist/es6/form-field/lib/hooks/useFormFieldInput.d.ts +1 -1
  67. package/dist/es6/form-field/lib/hooks/useFormFieldLabel.d.ts +1 -1
  68. package/dist/es6/form-field/lib/hooks/useFormFieldModel.d.ts +2 -12
  69. package/dist/es6/form-field/lib/hooks/useFormFieldModel.d.ts.map +1 -1
  70. package/dist/es6/form-field/lib/hooks/useFormFieldModel.js +1 -3
  71. package/dist/es6/menu/lib/Menu.d.ts +12 -5
  72. package/dist/es6/menu/lib/Menu.d.ts.map +1 -1
  73. package/dist/es6/menu/lib/Menu.js +14 -5
  74. package/dist/es6/menu/lib/MenuItem.d.ts +15 -5
  75. package/dist/es6/menu/lib/MenuItem.d.ts.map +1 -1
  76. package/dist/es6/menu/lib/MenuItem.js +18 -6
  77. package/dist/es6/pill/lib/Pill.d.ts +103 -5
  78. package/dist/es6/pill/lib/Pill.d.ts.map +1 -1
  79. package/dist/es6/pill/lib/Pill.js +101 -2
  80. package/dist/es6/segmented-control/index.d.ts +2 -0
  81. package/dist/es6/segmented-control/index.d.ts.map +1 -1
  82. package/dist/es6/segmented-control/index.js +2 -0
  83. package/dist/es6/segmented-control/lib/SegmentedControl.d.ts +74 -38
  84. package/dist/es6/segmented-control/lib/SegmentedControl.d.ts.map +1 -1
  85. package/dist/es6/segmented-control/lib/SegmentedControl.js +36 -0
  86. package/dist/es6/segmented-control/lib/SegmentedControlItem.d.ts +1 -1
  87. package/dist/es6/segmented-control/lib/SegmentedControlList.d.ts +64 -1
  88. package/dist/es6/segmented-control/lib/SegmentedControlList.d.ts.map +1 -1
  89. package/dist/es6/segmented-control/lib/SegmentedControlList.js +1 -1
  90. package/dist/es6/segmented-control/lib/hooks/useSegmentedControlItem.d.ts +2 -2
  91. package/dist/es6/segmented-control/lib/hooks/useSegmentedControlModel.d.ts +39 -39
  92. package/dist/es6/segmented-control/lib/hooks/useSegmentedControlModel.js +2 -1
  93. package/dist/es6/select/lib/Select.js +2 -0
  94. package/dist/es6/side-panel/lib/SidePanel.d.ts +13 -26
  95. package/dist/es6/side-panel/lib/SidePanel.d.ts.map +1 -1
  96. package/dist/es6/side-panel/lib/SidePanel.js +75 -97
  97. package/dist/es6/side-panel/lib/SidePanelToggleButton.d.ts +17 -0
  98. package/dist/es6/side-panel/lib/SidePanelToggleButton.d.ts.map +1 -0
  99. package/dist/es6/side-panel/lib/SidePanelToggleButton.js +66 -0
  100. package/dist/es6/side-panel/lib/hooks.d.ts +20 -0
  101. package/dist/es6/side-panel/lib/hooks.d.ts.map +1 -1
  102. package/dist/es6/side-panel/lib/hooks.js +19 -0
  103. package/dist/es6/status-indicator/index.d.ts +2 -0
  104. package/dist/es6/status-indicator/index.d.ts.map +1 -1
  105. package/dist/es6/status-indicator/index.js +2 -0
  106. package/dist/es6/status-indicator/lib/StatusIndicator.d.ts +28 -2
  107. package/dist/es6/status-indicator/lib/StatusIndicator.d.ts.map +1 -1
  108. package/dist/es6/status-indicator/lib/StatusIndicator.js +27 -1
  109. package/dist/es6/status-indicator/lib/StatusIndicatorIcon.d.ts +13 -0
  110. package/dist/es6/status-indicator/lib/StatusIndicatorIcon.d.ts.map +1 -1
  111. package/dist/es6/status-indicator/lib/StatusIndicatorIcon.js +1 -1
  112. package/dist/es6/text-area/lib/TextArea.d.ts +1 -1
  113. package/dist/es6/text-area/lib/TextArea.d.ts.map +1 -1
  114. package/dist/es6/text-area/lib/TextArea.js +2 -3
  115. package/dist/es6/text-input/lib/TextInput.d.ts +2 -2
  116. package/dist/es6/text-input/lib/TextInput.d.ts.map +1 -1
  117. package/dist/es6/text-input/lib/TextInput.js +2 -3
  118. package/dist/es6/text-input/lib/TextInputField.js +3 -3
  119. package/dist/es6/text-input/lib/hooks/useTextInputField.d.ts +1 -1
  120. package/dist/es6/text-input/lib/hooks/useTextInputModel.d.ts +1 -0
  121. package/dist/es6/text-input/lib/hooks/useTextInputModel.d.ts.map +1 -1
  122. package/dist/es6/text-input/lib/hooks/useTextInputModel.js +1 -0
  123. package/form-field/lib/hooks/useFormFieldModel.tsx +1 -3
  124. package/menu/lib/Menu.tsx +12 -5
  125. package/menu/lib/MenuItem.tsx +15 -5
  126. package/package.json +4 -4
  127. package/pill/lib/Pill.tsx +101 -2
  128. package/segmented-control/index.ts +2 -0
  129. package/segmented-control/lib/SegmentedControl.tsx +36 -0
  130. package/segmented-control/lib/SegmentedControlList.tsx +1 -1
  131. package/side-panel/lib/SidePanel.tsx +119 -169
  132. package/side-panel/lib/SidePanelToggleButton.tsx +78 -0
  133. package/side-panel/lib/hooks.ts +20 -0
  134. package/status-indicator/index.ts +2 -0
  135. package/status-indicator/lib/StatusIndicator.tsx +27 -1
  136. package/status-indicator/lib/StatusIndicatorIcon.tsx +1 -1
  137. package/text-area/lib/TextArea.tsx +6 -3
  138. package/text-input/lib/TextInput.tsx +6 -3
  139. package/text-input/lib/TextInputField.tsx +3 -3
  140. package/text-input/lib/hooks/useTextInputModel.ts +1 -0
  141. package/dist/commonjs/text-area/lib/hooks/index.d.ts +0 -2
  142. package/dist/commonjs/text-area/lib/hooks/index.d.ts.map +0 -1
  143. package/dist/commonjs/text-area/lib/hooks/index.js +0 -13
  144. package/dist/commonjs/text-area/lib/hooks/useTextAreaModel.d.ts +0 -28
  145. package/dist/commonjs/text-area/lib/hooks/useTextAreaModel.d.ts.map +0 -1
  146. package/dist/commonjs/text-area/lib/hooks/useTextAreaModel.js +0 -5
  147. package/dist/es6/text-area/lib/hooks/index.d.ts +0 -2
  148. package/dist/es6/text-area/lib/hooks/index.d.ts.map +0 -1
  149. package/dist/es6/text-area/lib/hooks/index.js +0 -1
  150. package/dist/es6/text-area/lib/hooks/useTextAreaModel.d.ts +0 -28
  151. package/dist/es6/text-area/lib/hooks/useTextAreaModel.d.ts.map +0 -1
  152. package/dist/es6/text-area/lib/hooks/useTextAreaModel.js +0 -2
  153. package/text-area/lib/hooks/index.ts +0 -1
  154. package/text-area/lib/hooks/useTextAreaModel.ts +0 -3
package/pill/lib/Pill.tsx CHANGED
@@ -160,14 +160,113 @@ const StyledNonInteractivePill = styled(StyledBasePill)<StyledType>({
160
160
  position: 'relative',
161
161
  });
162
162
 
163
+ /**
164
+ * By default, a `Pill` renders an interactive element that accepts subcomponents. By "interactive"
165
+ * we mean that the Pill container is a focusable element (a `<button>`). All leading elements
166
+ * (icons or avatars) are intended to be descriptive, helping support the label. They should not
167
+ * receive focus.
168
+ *
169
+ * `Pill` is the container component. It also provides a React context model for its subcomponents.
170
+ * Based on the `variant` prop this component will render different styled `Pill`s.
171
+ *
172
+ * Example of read only:
173
+ *
174
+ * ```tsx
175
+ * <Pill variant="readOnly">This is a read only</Pill>
176
+ * ```
177
+ *
178
+ * Example of interactive:
179
+ *
180
+ * ```tsx
181
+ * <Pill onClick={() => console.log('clicked')}>
182
+ * <Pill.Avatar /> Regina Skeltor
183
+ * </Pill>
184
+ * ```
185
+ *
186
+ * Example of removable:
187
+ *
188
+ * ```tsx
189
+ * <Pill variant="removable">
190
+ * <Pill.Avatar /> Regina Skeltor
191
+ * <Pill.IconButton onClick={() => console.log('clicked')} />
192
+ * </Pill>
193
+ * ```
194
+ *
195
+ * If you set the `Pill` `variant` to `removable`, it will render a `<span>` element. You can then
196
+ * provide a `Pill.IconButton` that acts as the focus target. This creates a smaller, more
197
+ * intentional click target that prevents users from accidentally deleting an item.
198
+ *
199
+ * ```tsx
200
+ * <Pill variant="removable">
201
+ * Shoes
202
+ * <Pill.IconButton onClick={() => console.log('handle remove')} />
203
+ * </Pill>
204
+ * ```
205
+ */
163
206
  export const Pill = createContainer('button')({
207
+ displayName: 'Pill',
164
208
  modelHook: usePillModel,
165
209
  subComponents: {
166
- Icon: PillIcon,
210
+ /**
211
+ * This component renders an avatar. It supports all props of the `Avatar` component.
212
+ *
213
+ * ```tsx
214
+ * <Pill variant="removable">
215
+ * <Pill.Avatar url={avatarUrl} />
216
+ * Regina Skeltor
217
+ * <Pill.IconButton onClick={() => console.log('handle remove')} />
218
+ * </Pill>
219
+ * ```
220
+ */
167
221
  Avatar: PillAvatar,
222
+ /**
223
+ * This component renders its `children` as the count.
224
+ *
225
+ * ```tsx
226
+ * <Pill onClick={() => console.warn('clicked')}>
227
+ * Shoes
228
+ * <Pill.Count>30</Pill.Count>
229
+ * </Pill>
230
+ * ```
231
+ */
168
232
  Count: PillCount,
169
- Label: PillLabel,
233
+ /**
234
+ * This component renders an `icon`. It not be used with the `default` styling – not `readOnly`
235
+ * or `removable` variants. By default it renders a `plusIcon` but it can be overridden by
236
+ * providing an icon to the `icon` prop.
237
+ *
238
+ * ```tsx
239
+ * <Pill onClick={() => console.warn('clicked')}>
240
+ * <Pill.Icon />
241
+ * <Pill.Label>Regina Skeltor</Pill.Label>
242
+ * </Pill>
243
+ * ```
244
+ */
245
+ Icon: PillIcon,
246
+ /**
247
+ * This component renders a custom icon button. It is only intended to be used with the
248
+ * `removable` variant. By default, it renders a `xSmallIcon` but can be overridden by providing
249
+ * an icon to the `icon` prop.
250
+ *
251
+ * ```tsx
252
+ * <Pill variant="removable">
253
+ * Pink Shirts
254
+ * <Pill.IconButton onClick={() => console.warn('clicked')} />
255
+ * </Pill>
256
+ * ```
257
+ */
170
258
  IconButton: PillIconButton,
259
+ /**
260
+ * This component renders a `<span>` that automatically handles overflow by rendering a tooltip.
261
+ * There's no need to use this component directly since the overflow is handled for you automatically.
262
+ *
263
+ * ```tsx
264
+ * <Pill variant="readOnly">
265
+ * <Pill.Label>Read-only</Pill.Label>
266
+ * </Pill>
267
+ * ```
268
+ */
269
+ Label: PillLabel,
171
270
  },
172
271
  })<PillProps>(({variant = 'default', maxWidth, ...elemProps}, Element, model) => {
173
272
  return (
@@ -1,2 +1,4 @@
1
1
  export * from './lib/SegmentedControl';
2
2
  export * from './lib/hooks/useSegmentedControlModel';
3
+ export * from './lib/hooks/useSegmentedControlItem';
4
+ export {useSegmentedControlList} from './lib/SegmentedControlList';
@@ -9,11 +9,47 @@ export interface SegmentedControlProps {
9
9
  children: React.ReactNode;
10
10
  }
11
11
 
12
+ /**
13
+ * `SegmentedControl` is a container component that is responsible for creating a
14
+ * {@link SegmentedControlModel} and sharing it with its subcomponents using React context. It does
15
+ * not represent a real element.
16
+ *
17
+ * ```tsx
18
+ * <SegmentedControl items={[]}>{Child components}</SegmentedControl>
19
+ * ```
20
+ *
21
+ * Alternatively, you may pass in a model using the [hoisted model
22
+ * pattern](/getting-started/for-developers/resources/compound-components/#configuring-a-model).
23
+ *
24
+ * ```tsx
25
+ * const model = useSegmentedControlModel({
26
+ * items: [],
27
+ * });
28
+ *
29
+ * <SegmentedControl model={model}>{Child components}</SegmentedControl>;
30
+ * ```
31
+ */
12
32
  export const SegmentedControl = createContainer()({
13
33
  displayName: 'SegmentedControl',
14
34
  modelHook: useSegmentedControlModel,
15
35
  subComponents: {
36
+ /**
37
+ * `SegmentedControl.List` renders {@link Grid} under the hood. It is a container for
38
+ * {@link SegmentedControlItem SegmentedControl.Item} subcomponents.
39
+ *
40
+ * ```tsx
41
+ * <SegmentedControl.List>{SegmentedControl.Items}</SegmentedControl.List>
42
+ * ```
43
+ */
16
44
  List: SegmentedControlList,
45
+ /**
46
+ * `SegmentedControl.Item` is a `button` element built on `BaseButton`. `SegmentedControl.Item`
47
+ * has a `data-id` prop to handle `onSelect` properly.
48
+ *
49
+ * ```tsx
50
+ * <SegmentedControl.Item data-id="table">Table</SegmentedControl.Item>
51
+ * ```
52
+ */
17
53
  Item: SegmentedControlItem,
18
54
  },
19
55
  })<SegmentedControlProps>(({children}) => {
@@ -15,7 +15,7 @@ export interface SegmentedControlListProps<T = any>
15
15
  children: ((item: T) => React.ReactNode) | React.ReactNode;
16
16
  }
17
17
 
18
- const useSegmentedControlList = createElemPropsHook(useSegmentedControlModel)(
18
+ export const useSegmentedControlList = createElemPropsHook(useSegmentedControlModel)(
19
19
  ({state: {orientation, disabled, items}}) => {
20
20
  const directionName = orientation === 'vertical' ? 'Row' : 'Column';
21
21
  return {
@@ -1,23 +1,16 @@
1
1
  /** @jsxRuntime classic */
2
2
  /** @jsx jsx */
3
3
  import * as React from 'react';
4
- import {styled, useIsRTL} from '@workday/canvas-kit-react/common';
5
- import {css, jsx, keyframes, CSSObject} from '@emotion/react';
6
- import {TertiaryButton, TertiaryButtonProps} from '@workday/canvas-kit-react/button';
7
- import {space, colors, depth} from '@workday/canvas-kit-react/tokens';
8
- import {transformationImportIcon} from '@workday/canvas-system-icons-web';
9
- import {Tooltip} from '@workday/canvas-kit-react/tooltip';
4
+ import {createComponent, styled} from '@workday/canvas-kit-react/common';
5
+ import {jsx, keyframes, CSSObject} from '@emotion/react';
6
+ import {colors, depth} from '@workday/canvas-kit-react/tokens';
7
+ import {SidePanelContext} from './hooks';
8
+ import {SidePanelToggleButton} from './SidePanelToggleButton';
10
9
 
11
10
  export type SidePanelVariant = 'standard' | 'alternate';
12
11
  export type SidePanelTransitionStates = 'collapsed' | 'collapsing' | 'expanded' | 'expanding';
13
12
 
14
- export interface SidePanelProps extends React.HTMLAttributes<HTMLDivElement> {
15
- /**
16
- * The element the side panel will render as (e.g. 'div').
17
- *
18
- * @default 'section'
19
- */
20
- as?: React.ElementType;
13
+ export interface SidePanelProps {
21
14
  /**
22
15
  * The width of the component (in `px` if it's a `number`) when it is collapsed.
23
16
  *
@@ -67,6 +60,9 @@ export interface SidePanelProps extends React.HTMLAttributes<HTMLDivElement> {
67
60
  * @param boolean
68
61
  */
69
62
  touched: boolean;
63
+ children?: React.ReactNode;
64
+ onAnimationStart?: React.AnimationEventHandler<HTMLElement>;
65
+ onAnimationEnd?: React.AnimationEventHandler<HTMLElement>;
70
66
  }
71
67
 
72
68
  const createKeyframes = (from: number | string, to: number | string) => {
@@ -98,172 +94,126 @@ const containerVariantStyle: Record<SidePanelVariant, CSSObject> = {
98
94
  },
99
95
  };
100
96
 
101
- const Panel = styled('section')<Pick<SidePanelProps, 'as'>>({
97
+ const Panel = styled('section')({
102
98
  overflow: 'hidden',
103
99
  position: 'relative',
104
100
  boxSizing: 'border-box',
105
101
  height: '100%',
106
102
  });
107
103
 
108
- export const SidePanelContext = React.createContext({
109
- state: 'expanded',
110
- origin: 'left',
111
- });
112
-
113
- export const SidePanel = ({
114
- as = 'section',
115
- children,
116
- collapsedWidth = 64,
117
- expanded = true,
118
- expandedWidth = 320,
119
- onAnimationEnd,
120
- onAnimationStart,
121
- onExpandedChange,
122
- onStateTransition,
123
- origin = 'left',
124
- variant = 'standard',
125
- touched,
126
- ...elemProps
127
- }: SidePanelProps) => {
128
- const [state, setState] = React.useState<SidePanelTransitionStates>(
129
- expanded ? 'expanded' : 'collapsed'
130
- );
131
-
132
- React.useEffect(() => {
133
- if (typeof onExpandedChange !== 'undefined') {
134
- onExpandedChange(expanded);
135
- }
136
- }, [expanded, onExpandedChange]);
137
-
138
- React.useEffect(() => {
139
- if (typeof onStateTransition !== 'undefined') {
140
- onStateTransition(state);
141
- }
142
- }, [state, onStateTransition]);
143
-
144
- const motion = {
145
- collapse: createKeyframes(expandedWidth, collapsedWidth),
146
- expand: createKeyframes(collapsedWidth, expandedWidth),
147
- };
148
-
149
- const handleAnimationEnd = (event: React.AnimationEvent<HTMLDivElement>) => {
150
- if (event.currentTarget === event.target) {
151
- if (event.animationName === motion.collapse.name) {
152
- setState('collapsed');
104
+ export const SidePanel = createComponent('section')({
105
+ displayName: 'SidePanel',
106
+ Component(
107
+ {
108
+ children,
109
+ collapsedWidth = 64,
110
+ expanded = true,
111
+ expandedWidth = 320,
112
+ onAnimationEnd,
113
+ onAnimationStart,
114
+ onExpandedChange,
115
+ onStateTransition,
116
+ origin = 'left',
117
+ variant = 'standard',
118
+ touched,
119
+ ...elemProps
120
+ }: SidePanelProps,
121
+ ref,
122
+ Element
123
+ ) {
124
+ const [state, setState] = React.useState<SidePanelTransitionStates>(
125
+ expanded ? 'expanded' : 'collapsed'
126
+ );
127
+
128
+ React.useEffect(() => {
129
+ if (typeof onExpandedChange !== 'undefined') {
130
+ onExpandedChange(expanded);
153
131
  }
132
+ }, [expanded, onExpandedChange]);
154
133
 
155
- if (event.animationName === motion.expand.name) {
156
- setState('expanded');
134
+ React.useEffect(() => {
135
+ if (typeof onStateTransition !== 'undefined') {
136
+ onStateTransition(state);
157
137
  }
158
- }
159
-
160
- if (typeof onAnimationEnd !== 'undefined') {
161
- onAnimationEnd(event);
162
- }
163
- };
164
-
165
- const handleAnimationStart = (event: React.AnimationEvent<HTMLDivElement>) => {
166
- if (event.currentTarget === event.target) {
167
- if (
168
- event.animationName === motion.collapse.name ||
169
- event.animationName === motion.expand.name
170
- ) {
171
- setState(expanded ? 'expanding' : 'collapsing');
138
+ }, [state, onStateTransition]);
139
+
140
+ const motion = {
141
+ collapse: createKeyframes(expandedWidth, collapsedWidth),
142
+ expand: createKeyframes(collapsedWidth, expandedWidth),
143
+ };
144
+
145
+ const handleAnimationEnd = (event: React.AnimationEvent<HTMLDivElement>) => {
146
+ if (event.currentTarget === event.target) {
147
+ if (event.animationName === motion.collapse.name) {
148
+ setState('collapsed');
149
+ }
150
+
151
+ if (event.animationName === motion.expand.name) {
152
+ setState('expanded');
153
+ }
172
154
  }
173
- }
174
155
 
175
- if (typeof onAnimationStart !== 'undefined') {
176
- onAnimationStart(event);
177
- }
178
- };
156
+ if (typeof onAnimationEnd !== 'undefined') {
157
+ onAnimationEnd(event);
158
+ }
159
+ };
160
+
161
+ const handleAnimationStart = (event: React.AnimationEvent<HTMLDivElement>) => {
162
+ if (event.currentTarget === event.target) {
163
+ if (
164
+ event.animationName === motion.collapse.name ||
165
+ event.animationName === motion.expand.name
166
+ ) {
167
+ setState(expanded ? 'expanding' : 'collapsing');
168
+ }
169
+ }
179
170
 
180
- return (
181
- <Panel
182
- as={as}
183
- css={[
184
- {
185
- width: expanded ? expandedWidth : collapsedWidth,
186
- maxWidth: expanded ? expandedWidth : collapsedWidth,
187
- // mounted.current will be false on the first render, thus you won't get an unwanted animation here
188
- // Will animate again if you force a re-render (like in Storybook)
189
- animation: touched
190
- ? `${expanded ? motion.expand : motion.collapse} 200ms ease-out`
191
- : undefined,
192
- },
193
- containerVariantStyle[variant],
194
- ]}
195
- onAnimationEnd={handleAnimationEnd}
196
- onAnimationStart={handleAnimationStart}
197
- {...elemProps}
198
- >
199
- <SidePanelContext.Provider
200
- value={{
201
- state,
202
- origin,
203
- }}
171
+ if (typeof onAnimationStart !== 'undefined') {
172
+ onAnimationStart(event);
173
+ }
174
+ };
175
+
176
+ return (
177
+ <Panel
178
+ ref={ref}
179
+ as={Element}
180
+ css={[
181
+ {
182
+ width: expanded ? expandedWidth : collapsedWidth,
183
+ maxWidth: expanded ? expandedWidth : collapsedWidth,
184
+ // mounted.current will be false on the first render, thus you won't get an unwanted animation here
185
+ // Will animate again if you force a re-render (like in Storybook)
186
+ animation: touched
187
+ ? `${expanded ? motion.expand : motion.collapse} 200ms ease-out`
188
+ : undefined,
189
+ },
190
+ containerVariantStyle[variant],
191
+ ]}
192
+ onAnimationEnd={handleAnimationEnd}
193
+ onAnimationStart={handleAnimationStart}
194
+ {...elemProps}
204
195
  >
205
- {children}
206
- </SidePanelContext.Provider>
207
- </Panel>
208
- );
209
- };
210
-
211
- export type ToggleButtonProps = TertiaryButtonProps & {
212
- /**
213
- * The tooltip text to expand the side panel
214
- * @default 'Expand'
215
- */
216
- tooltipTextExpand?: string;
217
- /**
218
- * The tooltip text to collapse the side panel
219
- * @default 'Collapse'
220
- */
221
- tooltipTextCollapse?: string;
222
- };
223
-
224
- /**
225
- * A toggle button styled specifically for the side panel container.
226
- */
227
- const ToggleButton = ({
228
- variant = undefined,
229
- icon = transformationImportIcon,
230
- tooltipTextExpand: expandLabel = 'Expand',
231
- tooltipTextCollapse: collapseLabel = 'Collapse',
232
- ...rest
233
- }: ToggleButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
234
- const context = React.useContext(SidePanelContext);
235
-
236
- const useRTLOrigin = () => {
237
- const isRTL = useIsRTL();
238
- // if the direction is set to RTl, flip the origin
239
- if (isRTL) {
240
- return context.origin === 'left' ? 'right' : 'left';
241
- }
242
- // Otherwise, default to returning the origin
243
- return context.origin;
244
- };
245
-
246
- const rtlOrigin = useRTLOrigin();
247
-
248
- // Note: Depending on the collapsed width, the button could "jump" to it's final position.
249
- const buttonStyle = css({
250
- position: 'absolute',
251
- top: space.m,
252
- width: space.l,
253
- right: context.state === 'collapsed' ? 0 : rtlOrigin === 'left' ? space.s : undefined,
254
- left: context.state === 'collapsed' ? 0 : rtlOrigin === 'right' ? space.s : undefined,
255
- margin: context.state === 'collapsed' ? 'auto' : 0, // to override the -8px margin for TertiaryButton.Plain
256
- transform:
257
- context.state === 'collapsed' || context.state === 'collapsing'
258
- ? `scaleX(${rtlOrigin === 'left' ? '1' : '-1'})`
259
- : `scaleX(${rtlOrigin === 'left' ? '-1' : '1'})`,
260
- });
261
-
262
- return (
263
- <Tooltip title={context.state === 'collapsed' ? expandLabel : collapseLabel} type="muted">
264
- <TertiaryButton type="button" css={buttonStyle} icon={icon} variant={variant} {...rest} />
265
- </Tooltip>
266
- );
267
- };
268
-
269
- SidePanel.ToggleButton = ToggleButton;
196
+ <SidePanelContext.Provider
197
+ value={{
198
+ state,
199
+ origin,
200
+ }}
201
+ >
202
+ {children}
203
+ </SidePanelContext.Provider>
204
+ </Panel>
205
+ );
206
+ },
207
+ subComponents: {
208
+ /**
209
+ * `SidePanel.ToggleButton` is a control that is meant to toggle between `expanded = true` and
210
+ * `expanded = false` states. It must be used within the `SidePanel` component as a child. Use
211
+ * in conjunction with `useSidePanel`'s `controlProps`, otherwise it does not come with explicit
212
+ * `onClick` handlers.
213
+ *
214
+ * For accessibility purposes, it must be the first focusable element. We recommend that you
215
+ * keep it as the first child of `SidePanel`
216
+ */
217
+ ToggleButton: SidePanelToggleButton,
218
+ },
219
+ });
@@ -0,0 +1,78 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx jsx */
3
+ import * as React from 'react';
4
+ import {createComponent, ExtractProps, useIsRTL} from '@workday/canvas-kit-react/common';
5
+ import {css, jsx} from '@emotion/react';
6
+ import {TertiaryButton} from '@workday/canvas-kit-react/button';
7
+ import {space} from '@workday/canvas-kit-react/tokens';
8
+ import {transformationImportIcon} from '@workday/canvas-system-icons-web';
9
+ import {Tooltip} from '@workday/canvas-kit-react/tooltip';
10
+ import {SidePanelContext} from './hooks';
11
+
12
+ export interface SidePanelToggleButtonProps extends ExtractProps<typeof TertiaryButton, never> {
13
+ /**
14
+ * The tooltip text to expand the side panel
15
+ */
16
+ tooltipTextExpand?: string;
17
+ /**
18
+ * The tooltip text to collapse the side panel
19
+ */
20
+ tooltipTextCollapse?: string;
21
+ }
22
+
23
+ /**
24
+ * A toggle button styled specifically for the side panel container.
25
+ */
26
+ export const SidePanelToggleButton = createComponent('button')({
27
+ displayName: 'SidePanel.ToggleButton',
28
+ Component({
29
+ variant = undefined,
30
+ icon = transformationImportIcon,
31
+ tooltipTextExpand = 'Expand',
32
+ tooltipTextCollapse = 'Collapse',
33
+ ...elemProps
34
+ }: SidePanelToggleButtonProps) {
35
+ const context = React.useContext(SidePanelContext);
36
+
37
+ const useRTLOrigin = () => {
38
+ const isRTL = useIsRTL();
39
+ // if the direction is set to RTl, flip the origin
40
+ if (isRTL) {
41
+ return context.origin === 'left' ? 'right' : 'left';
42
+ }
43
+ // Otherwise, default to returning the origin
44
+ return context.origin;
45
+ };
46
+
47
+ const rtlOrigin = useRTLOrigin();
48
+
49
+ // Note: Depending on the collapsed width, the button could "jump" to it's final position.
50
+ const buttonStyle = css({
51
+ position: 'absolute',
52
+ top: space.m,
53
+ width: space.l,
54
+ right: context.state === 'collapsed' ? 0 : rtlOrigin === 'left' ? space.s : undefined,
55
+ left: context.state === 'collapsed' ? 0 : rtlOrigin === 'right' ? space.s : undefined,
56
+ margin: context.state === 'collapsed' ? 'auto' : 0, // to override the -8px margin for TertiaryButton.Plain
57
+ transform:
58
+ context.state === 'collapsed' || context.state === 'collapsing'
59
+ ? `scaleX(${rtlOrigin === 'left' ? '1' : '-1'})`
60
+ : `scaleX(${rtlOrigin === 'left' ? '-1' : '1'})`,
61
+ });
62
+
63
+ return (
64
+ <Tooltip
65
+ title={context.state === 'collapsed' ? tooltipTextExpand : tooltipTextCollapse}
66
+ type="muted"
67
+ >
68
+ <TertiaryButton
69
+ type="button"
70
+ css={buttonStyle}
71
+ icon={icon}
72
+ variant={variant}
73
+ {...elemProps}
74
+ />
75
+ </Tooltip>
76
+ );
77
+ },
78
+ });
@@ -1,5 +1,10 @@
1
1
  import * as React from 'react';
2
2
 
3
+ export const SidePanelContext = React.createContext({
4
+ state: 'expanded',
5
+ origin: 'left',
6
+ });
7
+
3
8
  import {useUniqueId} from '@workday/canvas-kit-react/common';
4
9
  /**
5
10
  * The optional config options for the `useSidePanel` hook
@@ -77,6 +82,21 @@ export interface ControlProps {
77
82
  onClick: () => void;
78
83
  }
79
84
 
85
+ /**
86
+ *
87
+ * This hook manages the state and `aria-` attributes for the SidePanel. It takes an optional
88
+ * configuration object:
89
+ *
90
+ * ```tsx
91
+ * import {useSidePanel} from '@workday/canvas-kit-preview-react/side-panel';
92
+ *
93
+ * const {expanded, setExpanded, panelProps, labelProps, controlProps} = useSidePanel({
94
+ * initialExpanded: false,
95
+ * panelId: 'custom-panel-id',
96
+ * labelId: 'custom-label-id',
97
+ * });
98
+ * ```
99
+ */
80
100
  export const useSidePanel = (config?: UseSidePanelProps) => {
81
101
  const [touched, setTouched] = React.useState(false);
82
102
  const configParams = config
@@ -1 +1,3 @@
1
1
  export * from './lib/StatusIndicator';
2
+ export * from './lib/hooks/useStatusIndicatorModel';
3
+ export {useStatusIndicatorIcon} from './lib/StatusIndicatorIcon';
@@ -22,13 +22,39 @@ export const useStatusIndicator = createElemPropsHook(useStatusIndicatorModel)((
22
22
  };
23
23
  });
24
24
 
25
+ /**
26
+ * `StatusIndicator` is a container component which renders an {@link Flex} under the hood to
27
+ * apply spacing evenly between its children. It has a default maximum width of `200px`.
28
+ *
29
+ * ```tsx
30
+ * <StatusIndicator emphasis="low" variant="blue">
31
+ * {Child components}
32
+ * </StatusIndicator>
33
+ * ```
34
+ */
25
35
  export const StatusIndicator = createContainer('div')({
26
36
  displayName: 'StatusIndicator',
27
37
  modelHook: useStatusIndicatorModel,
28
38
  elemPropsHook: useStatusIndicator,
29
39
  subComponents: {
30
- Icon: StatusIndicatorIcon,
40
+ /**
41
+ * `StatusIndicator.Label` renders {@link Text} under the hood. It will apply an ellipsis if its
42
+ * contents exceed the component's maximum width.
43
+ *
44
+ * ```tsx
45
+ * <StatusIndicator.Label>{The text to be rendered}</StatusIndicator.Label>
46
+ * ```
47
+ */
31
48
  Label: StatusIndicatorLabel,
49
+ /**
50
+ * `StatusIndicator.Icon` renders {@link SystemIcon} under the hood. It's used as a decorative
51
+ * element to visually support the {@link StatusIndicatorLabel StatusIndicator.Label} text.
52
+ *
53
+ * ```tsx
54
+ * <StatusIndicator.Icon icon={uploadCloudIcon} />
55
+ * ```
56
+ */
57
+ Icon: StatusIndicatorIcon,
32
58
  },
33
59
  })<StatusIndicatorProps>(({children, ...elemProps}, Element, model) => {
34
60
  return (
@@ -72,7 +72,7 @@ export const statusIndicatorColors = {
72
72
  },
73
73
  };
74
74
 
75
- const useStatusIndicatorIcon = createElemPropsHook(useStatusIndicatorModel)(({state}) => {
75
+ export const useStatusIndicatorIcon = createElemPropsHook(useStatusIndicatorModel)(({state}) => {
76
76
  const colors = statusIndicatorColors[state.variant][state.emphasis];
77
77
  return {
78
78
  color: colors.text,