@workday/canvas-kit-docs 8.4.8 → 8.4.9
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/dist/mdx/react/common/CompoundComponentUtilities.mdx +330 -0
- package/dist/mdx/react/common/examples/CreateComponent.tsx +42 -0
- package/dist/mdx/react/common/examples/CreateContainer.tsx +75 -0
- package/dist/mdx/react/common/examples/CreateElemPropsHook.tsx +131 -0
- package/dist/mdx/react/common/examples/CreateModelHook.tsx +73 -0
- package/dist/mdx/react/common/examples/CreateSubcomponent.tsx +101 -0
- package/package.json +3 -3
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import {Specifications} from '@workday/canvas-kit-docs';
|
|
2
|
+
import CreateModelHook from './examples/CreateModelHook';
|
|
3
|
+
import CreateContainer from './examples/CreateContainer';
|
|
4
|
+
import CreateSubcomponent from './examples/CreateSubcomponent';
|
|
5
|
+
import CreateElemPropsHook from './examples/CreateElemPropsHook';
|
|
6
|
+
import CreateComponent from './examples/CreateComponent';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Compound Component Utilities
|
|
10
|
+
|
|
11
|
+
The following utilities are used to create and compose
|
|
12
|
+
[compound components](/getting-started/for-developers/resources/compound-components/).
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
yarn add @workday/canvas-kit-react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
# createComponent
|
|
21
|
+
|
|
22
|
+
`createComponent` is a factory function that creates components to be exported. It enforces React
|
|
23
|
+
`ref` forwarding, `as` prop, `displayName`, `subComponents`, and handles proper typing without much
|
|
24
|
+
boiler plate. The return type is `Component<element, Props>` which looks like
|
|
25
|
+
`Component<'div', Props>` which is a clean interface that tells you the default element that is
|
|
26
|
+
used.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
`createComponent` is great to use if you want to create quick compound components or you need to set
|
|
31
|
+
up components with `ref` forwarding.
|
|
32
|
+
|
|
33
|
+
<ExampleCodeBlock code={CreateComponent} />
|
|
34
|
+
|
|
35
|
+
# createModelHook
|
|
36
|
+
|
|
37
|
+
When building compound components that might have some internal state or events, you typically want
|
|
38
|
+
a model. A model allows a component to share information. This is where `createModelHook` comes in
|
|
39
|
+
handy.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
`createModelHook` is a factory function that takes in `defaultConfig` and `requiredConfig` and
|
|
44
|
+
returns a function which is your model hook.
|
|
45
|
+
|
|
46
|
+
Let's make a simple disclosure component as an example. This is how we'd use `createModelHook`.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import {createModelHook} from '@workday/canvas-kit-react/common';
|
|
50
|
+
export const useDisclosureModel = createModelHook({
|
|
51
|
+
// This becomes the default values on the model
|
|
52
|
+
defaultConfig: {
|
|
53
|
+
// some default config
|
|
54
|
+
},
|
|
55
|
+
})(config => {
|
|
56
|
+
const state = {
|
|
57
|
+
// some state
|
|
58
|
+
};
|
|
59
|
+
// Sets events that can be used across subcomponents
|
|
60
|
+
const events = {
|
|
61
|
+
// some events
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {state, events};
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
<ExampleCodeBlock code={CreateModelHook} />
|
|
69
|
+
|
|
70
|
+
Typescript will infer all config from the returned `state`, `events`, `defaultConfig` and the
|
|
71
|
+
`requiredConfig`.
|
|
72
|
+
|
|
73
|
+
## API
|
|
74
|
+
|
|
75
|
+
When `useDisclosureModel`is created, five properties are attached to it for composability:
|
|
76
|
+
`defaultConfig`, `requiredConfig`, `TConfig`, `getElemProps`, `Context`.
|
|
77
|
+
|
|
78
|
+
### defaultConfig
|
|
79
|
+
|
|
80
|
+
All config that has default values. Optional values also go here, but are represented as `undefined`
|
|
81
|
+
as part of the union type `(undefined as undefined | string)`. Using this can be useful when
|
|
82
|
+
creating other models that share similar config.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import {createModelHook} from '@workday/canvas-kit-react/common';
|
|
86
|
+
export const useExpandableModel = createModelHook({
|
|
87
|
+
defaultConfig: {
|
|
88
|
+
// extend the default config from the `useDisclosureModel`
|
|
89
|
+
...useDisclosureModel.defaultConfig,
|
|
90
|
+
},
|
|
91
|
+
})(config => {
|
|
92
|
+
const disclosure = useDisclosureModel(config);
|
|
93
|
+
const state = {
|
|
94
|
+
...disclosure.state,
|
|
95
|
+
};
|
|
96
|
+
const events = {
|
|
97
|
+
...disclosure.events,
|
|
98
|
+
};
|
|
99
|
+
return {state, events};
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### requiredConfig
|
|
104
|
+
|
|
105
|
+
All config that is required by the model. Most config should go into default config with thoughtful
|
|
106
|
+
defaults.
|
|
107
|
+
|
|
108
|
+
### contextOverride
|
|
109
|
+
|
|
110
|
+
The React context used by components to share a model between a container component and
|
|
111
|
+
subcomponents. It is used internally by `createContainer` and `createSubcomponents` and should not
|
|
112
|
+
need to be referenced directly.
|
|
113
|
+
|
|
114
|
+
Each subcomponent is tied to a model hook's context. For example, `useModalModel` extends
|
|
115
|
+
`usePopupModel`, but `Modal` components point directly to `Popup` components. The
|
|
116
|
+
`contextOverride: usePopupModel.Context` forces `useModalModel`'s internal context to point to the
|
|
117
|
+
same context reference as the one used in `usePopupModel`. This allows the model returned by
|
|
118
|
+
`useModalModel` to be compatible with both `Modal` subcomponents and `Popup` subcomponents. If you
|
|
119
|
+
do not override context, you must create a new container and subcomponent for every component using
|
|
120
|
+
the newly created model hook.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import {createModelHook} from '@workday/canvas-kit-react/common';
|
|
124
|
+
export const useModalModel = createModelHook({
|
|
125
|
+
defaultConfig: usePopupModel.defaultConfig,
|
|
126
|
+
requiredConfig: usePopupModel.requiredConfig,
|
|
127
|
+
// share context from the usePopupModel instead of the default one created by createModelHook
|
|
128
|
+
contextOverride: usePopupModel.Context,
|
|
129
|
+
})(config => {
|
|
130
|
+
const model = usePopupModel(config);
|
|
131
|
+
|
|
132
|
+
useInitialFocus(model);
|
|
133
|
+
useReturnFocus(model);
|
|
134
|
+
useCloseOnOverlayClick(model);
|
|
135
|
+
useCloseOnEscape(model);
|
|
136
|
+
useFocusTrap(model);
|
|
137
|
+
useAssistiveHideSiblings(model);
|
|
138
|
+
useDisableBodyScroll(model);
|
|
139
|
+
|
|
140
|
+
return model;
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### TConfig
|
|
145
|
+
|
|
146
|
+
TConfig gives you the typings that are defined in `defaultConfig` and `requiredConfig` instead of
|
|
147
|
+
having to redfine those types. This is useful when building a model that share similar config and
|
|
148
|
+
you want to merge them while getting the correct typings.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import {createModelHook} from '@workday/canvas-kit-react/common';
|
|
152
|
+
export const useActionBarModel = createModelHook({
|
|
153
|
+
defaultConfig: {
|
|
154
|
+
// We define the config and use the typings that come from useMenuModel for when an action bar renders a menu
|
|
155
|
+
menuConfig: {} as typeof useMenuModel.TConfig,
|
|
156
|
+
},
|
|
157
|
+
requiredConfig: useOverflowListModel.requiredConfig,
|
|
158
|
+
})(config => {
|
|
159
|
+
// define your internal state and events
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### getElemProps
|
|
164
|
+
|
|
165
|
+
This function will separate all `elemProps` or default attributes from an element from the model
|
|
166
|
+
config props. If a prop is both a `config` _and_ an `elemProp`, you can manually apply the prop
|
|
167
|
+
again. `elemProps` is called internally automatically by `createContainer`. If you use
|
|
168
|
+
`createContainer`, you shouldn't need to use this function.
|
|
169
|
+
|
|
170
|
+
By default `createModelHook` does this for you and spreads the `elemProps` onto the component.
|
|
171
|
+
|
|
172
|
+
# createContainer and createSubcomponent
|
|
173
|
+
|
|
174
|
+
`createContainer` and `createSubcomponent` functions take a default React.ElementType which can be
|
|
175
|
+
an element string like div or button or a component like Button. It also takes a config object
|
|
176
|
+
containing the following:
|
|
177
|
+
|
|
178
|
+
- `displayName`: This will be the name of the component when shown by the React Dev tools. By
|
|
179
|
+
convention, we make that name be the same as typed in a render function. For example
|
|
180
|
+
`Disclosure.Target` vs `DisclosureTarget`.
|
|
181
|
+
- `modelHook`: This is the model hook used by the compound component (`useDisclosureModel` in our
|
|
182
|
+
case). This model hook is used to determine proper prop types and seamlessly handle the option
|
|
183
|
+
model prop. For `createContainer`, if a `model` is not passed, a `model` is created and added to
|
|
184
|
+
React Context. For `createSubcomponent`, if a `model` is not passed, the `model` comes from React
|
|
185
|
+
Context.
|
|
186
|
+
- `elemPropsHook`: This is the elemPropsHook that takes a model and elemProps and returns elemProps.
|
|
187
|
+
|
|
188
|
+
- `subComponents`: For container components. A list of sub components to add to the returned
|
|
189
|
+
component. For example, a sub component called `DisclosureTarget` will be added to the export of
|
|
190
|
+
`Disclosure` so that the user can import only `Disclosure` and use `Disclosure.Target`.
|
|
191
|
+
`subComponents` is needed for Typescript because static properties cannot be added to predefined
|
|
192
|
+
interfaces. `Disclosure.Target` = `DisclosureTarget` will caused a type error. This property
|
|
193
|
+
allows the createComponent factory function to infer the final interface of the returned
|
|
194
|
+
component.
|
|
195
|
+
|
|
196
|
+
Finally, a generic function is returned that takes the component configuration. The first argument
|
|
197
|
+
is `elemProps` with `ref` and hook props already merged in with props handed to the component. The
|
|
198
|
+
model config props will already be filtered out. We'll worry about `elemPropsHook` later. The second
|
|
199
|
+
is an Element property. Element is the value passed to the Component's as prop. It will default to
|
|
200
|
+
the provided element. The last parameter is an optional model reference. Ideally, the model is used
|
|
201
|
+
in `elemPropsHook` and therefore not normally needed inside the render function.
|
|
202
|
+
|
|
203
|
+
## createContainer
|
|
204
|
+
|
|
205
|
+
When building a component that has a model, you typically start with a container component.
|
|
206
|
+
`createContainer` is a utility function that hooks up all the pieces for you.
|
|
207
|
+
|
|
208
|
+
### Usage
|
|
209
|
+
|
|
210
|
+
- It will wrap your component in a context provider so that `subComponents` have access to the
|
|
211
|
+
model.
|
|
212
|
+
- It attaches your model hook to your component.
|
|
213
|
+
- It runs any `elemPropsHook` hooks defined.
|
|
214
|
+
- You can attach `subComponents`.
|
|
215
|
+
- It will also extract the element attributes and define the `ref` type based on the element you
|
|
216
|
+
give it.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import {createContainer} from '@workday/canvas-kit-react/common';
|
|
220
|
+
interface DisclosureProps {}
|
|
221
|
+
|
|
222
|
+
//... useDisclosureModel
|
|
223
|
+
|
|
224
|
+
export const Disclosure = createContainer('div')({
|
|
225
|
+
displayName: 'Disclosure',
|
|
226
|
+
modelHook: useDisclosureModel,
|
|
227
|
+
})<DisclosureProps>(({children, ...elemProps}, Element, model) => {
|
|
228
|
+
return (
|
|
229
|
+
// spread div attributes including ref
|
|
230
|
+
<Element {...elemProps}>{children}</Element>
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
<ExampleCodeBlock code={CreateContainer} />
|
|
236
|
+
|
|
237
|
+
## createSubcomponent
|
|
238
|
+
|
|
239
|
+
Once you've built a container component, adding a subcomponent is easy. `createSubcomponent` is
|
|
240
|
+
similar to `createContainer` in that it hooks up many of the pieces for you. The main difference is
|
|
241
|
+
it uses the context created by `createContainer`. This allows access to the model created at the
|
|
242
|
+
root level so that you can use any state or events at a child component level.
|
|
243
|
+
|
|
244
|
+
### Usage
|
|
245
|
+
|
|
246
|
+
- It subscribes to the parent context allowing you access to the model hook `state` and `events`
|
|
247
|
+
- It attaches your model hook to your component.
|
|
248
|
+
- It runs any `elemPropsHook` hooks defined.
|
|
249
|
+
- You can attach `subComponents`.
|
|
250
|
+
- It will also extract the element attributes and define the `ref` type based on the element you
|
|
251
|
+
give it.
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import {createSubcomponent} from '@workday/canvas-kit-react/common';
|
|
255
|
+
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
256
|
+
export interface DisclosureTargetProps {
|
|
257
|
+
/**
|
|
258
|
+
* The children of the `Expandable.Target`
|
|
259
|
+
*/
|
|
260
|
+
children?: React.ReactNode;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
//... useDisclosureModel
|
|
264
|
+
|
|
265
|
+
export const DisclosureTarget = createSubcomponent('button')({
|
|
266
|
+
modelHook: useDisclosureModel,
|
|
267
|
+
})<DisclosureTargetProps>(({children, ...elemProps}, Element, model) => {
|
|
268
|
+
return (
|
|
269
|
+
<PrimaryButton
|
|
270
|
+
as={Element}
|
|
271
|
+
onClick={() =>
|
|
272
|
+
model.state.visibility === 'hidden' ? model.events.show() : model.events.hide()
|
|
273
|
+
}
|
|
274
|
+
{...elemProps}
|
|
275
|
+
>
|
|
276
|
+
{children}
|
|
277
|
+
</PrimaryButton>
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
<ExampleCodeBlock code={CreateSubcomponent} />
|
|
283
|
+
|
|
284
|
+
# createElemPropsHook
|
|
285
|
+
|
|
286
|
+
This is a utility function that is helpful to use when you have elem attributes that need to be
|
|
287
|
+
dyanmic based on the model hook state. This function will also handle merging of element props.
|
|
288
|
+
|
|
289
|
+
Once you create your element props hook you then attach your hook to either your `createContainer`
|
|
290
|
+
component or `createSubcomponent` by adding it to the `elemPropsHook` property.
|
|
291
|
+
|
|
292
|
+
## Usage
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
import {createSubcomponent, createElemPropsHook} from '@workday/canvas-kit-react/common';
|
|
296
|
+
import {Box} from '@workday/canvas-kit-react/layout';
|
|
297
|
+
|
|
298
|
+
export interface DisclosureContentProps extends BoxProps {
|
|
299
|
+
/**
|
|
300
|
+
* The children of the `Expandable.Content` whose visibility is controlled by the associated
|
|
301
|
+
* `Expandable.Target`
|
|
302
|
+
*/
|
|
303
|
+
children?: React.ReactNode;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//...useDisclosureModel defined
|
|
307
|
+
|
|
308
|
+
// Use createElemPropsHook to add style and id attribute based on the disclosure model hook state
|
|
309
|
+
// These attributes will be merged with the rest of elemProps that come from DisclosureContentProps
|
|
310
|
+
const useDisclosureContent = createElemPropsHook(useDisclosureModel)(({state}) => {
|
|
311
|
+
return {
|
|
312
|
+
style: state.visibility !== 'hidden' ? {} : {display: 'none'},
|
|
313
|
+
id: state.id,
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
export const DisclosureContent = createSubcomponent('div')({
|
|
318
|
+
modelHook: useDisclosureModel,
|
|
319
|
+
// attached our elemPropsHook to the component
|
|
320
|
+
elemPropsHook: useDisclosureContent,
|
|
321
|
+
})<DisclosureContentProps>(({children, ...elementProps}, Element) => {
|
|
322
|
+
return (
|
|
323
|
+
<Box as={Element} {...elementProps}>
|
|
324
|
+
{children}
|
|
325
|
+
</Box>
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
<ExampleCodeBlock code={CreateElemPropsHook} />
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {createComponent, ExtractProps} from '@workday/canvas-kit-react/common';
|
|
3
|
+
import {Box, BoxProps} from '@workday/canvas-kit-react/layout';
|
|
4
|
+
import {Heading} from '@workday/canvas-kit-react/text';
|
|
5
|
+
import {colors} from '@workday/canvas-kit-react/tokens';
|
|
6
|
+
|
|
7
|
+
// Extend Heading Props and omitting size since we've added a default
|
|
8
|
+
interface CardHeadingProps extends Omit<ExtractProps<typeof Heading>, 'size'> {}
|
|
9
|
+
export default createComponent('h2')({
|
|
10
|
+
displayName: 'CardHeading',
|
|
11
|
+
Component: ({children, ...elemProps}: CardHeadingProps, ref, Element) => {
|
|
12
|
+
return (
|
|
13
|
+
<Heading size="medium" as={Element} ref={ref} {...elemProps}>
|
|
14
|
+
{children}
|
|
15
|
+
</Heading>
|
|
16
|
+
);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Extend Box Props for customization
|
|
21
|
+
interface CardProps extends BoxProps {}
|
|
22
|
+
export default createComponent('div')({
|
|
23
|
+
displayName: 'Card',
|
|
24
|
+
subComponents: {
|
|
25
|
+
Heading: CardHeading,
|
|
26
|
+
},
|
|
27
|
+
Component: ({children, ...elemProps}: CardProps, ref, Element) => {
|
|
28
|
+
return (
|
|
29
|
+
<Box as={Element} {...elemProps} ref={ref}>
|
|
30
|
+
{children}
|
|
31
|
+
</Box>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default () => {
|
|
37
|
+
return (
|
|
38
|
+
<Card depth={2} border={`1px solid ${colors.soap400}`} padding="s" as="section">
|
|
39
|
+
<Card.Heading>Card Heading</Card.Heading>
|
|
40
|
+
</Card>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {createContainer, createModelHook, useUniqueId} from '@workday/canvas-kit-react/common';
|
|
3
|
+
|
|
4
|
+
export type Visibility = 'hidden' | 'visible';
|
|
5
|
+
|
|
6
|
+
// First we define a model using out createModelHook
|
|
7
|
+
const useDisclosureModel = createModelHook({
|
|
8
|
+
// This becomes the default values on the model
|
|
9
|
+
defaultConfig: {
|
|
10
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
11
|
+
id: '',
|
|
12
|
+
/**
|
|
13
|
+
* The initial visibility of the disclosed content
|
|
14
|
+
* @default 'hidden'
|
|
15
|
+
*/
|
|
16
|
+
initialVisibility: 'hidden' as Visibility,
|
|
17
|
+
},
|
|
18
|
+
})(config => {
|
|
19
|
+
const id = useUniqueId(config.id);
|
|
20
|
+
const [visibility, setVisibility] = React.useState(config.initialVisibility || 'hidden');
|
|
21
|
+
// Set the default internal state for your model.
|
|
22
|
+
const state = {
|
|
23
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
24
|
+
id,
|
|
25
|
+
/**
|
|
26
|
+
* Visibility state of the disclosed content. Models are allowed to extend the states to fit
|
|
27
|
+
* their needs, so if you need to consistently determine "not hidden", use `visibility !==
|
|
28
|
+
* 'hidden'` rather than `visibility === 'visible'`
|
|
29
|
+
*/
|
|
30
|
+
visibility,
|
|
31
|
+
};
|
|
32
|
+
// Sets events that can be used across subcomponents
|
|
33
|
+
const events = {
|
|
34
|
+
/**
|
|
35
|
+
* Start showing the disclosed content. If a DOM event triggered this event, the event data will
|
|
36
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
37
|
+
*/
|
|
38
|
+
show(event?: Event | React.SyntheticEvent) {
|
|
39
|
+
setVisibility('visible');
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* Start hiding this disclosed content. If a DOM event triggered this event, the event data will
|
|
43
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
44
|
+
*/
|
|
45
|
+
hide(event?: Event | React.SyntheticEvent) {
|
|
46
|
+
setVisibility('hidden');
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {state, events};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Disclosure Container Component
|
|
54
|
+
interface DisclosureProps extends React.HtmlHTMLAttributes<HTMLButtonElement> {}
|
|
55
|
+
|
|
56
|
+
const Disclosure = createContainer('button')({
|
|
57
|
+
displayName: 'Disclosure',
|
|
58
|
+
modelHook: useDisclosureModel,
|
|
59
|
+
subComponents: {},
|
|
60
|
+
})<DisclosureProps>(({children, ...elemProps}, Element, model) => {
|
|
61
|
+
return (
|
|
62
|
+
<Element
|
|
63
|
+
onClick={() =>
|
|
64
|
+
model.state.visibility === 'visible' ? model.events.hide() : model.events.show()
|
|
65
|
+
}
|
|
66
|
+
{...elemProps}
|
|
67
|
+
>
|
|
68
|
+
{model.state.visibility === 'visible' ? 'close' : 'open'}
|
|
69
|
+
</Element>
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export default () => {
|
|
74
|
+
return <Disclosure />;
|
|
75
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
createContainer,
|
|
4
|
+
createModelHook,
|
|
5
|
+
createSubcomponent,
|
|
6
|
+
createElemPropsHook,
|
|
7
|
+
useUniqueId,
|
|
8
|
+
} from '@workday/canvas-kit-react/common';
|
|
9
|
+
import {PrimaryButton, PrimaryButtonProps} from '@workday/canvas-kit-react/button';
|
|
10
|
+
import {Box, BoxProps} from '@workday/canvas-kit-react/layout';
|
|
11
|
+
|
|
12
|
+
type Visibility = 'hidden' | 'visible';
|
|
13
|
+
|
|
14
|
+
// First we define a model using out createModelHook
|
|
15
|
+
const useDisclosureModel = createModelHook({
|
|
16
|
+
// This becomes the default values on the model
|
|
17
|
+
defaultConfig: {
|
|
18
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
19
|
+
id: '',
|
|
20
|
+
/**
|
|
21
|
+
* The initial visibility of the disclosed content
|
|
22
|
+
* @default 'hidden'
|
|
23
|
+
*/
|
|
24
|
+
initialVisibility: 'hidden' as Visibility,
|
|
25
|
+
},
|
|
26
|
+
})(config => {
|
|
27
|
+
const id = useUniqueId(config.id);
|
|
28
|
+
const [visibility, setVisibility] = React.useState(config.initialVisibility || 'hidden');
|
|
29
|
+
// Set the default internal state for your model.
|
|
30
|
+
const state = {
|
|
31
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
32
|
+
id,
|
|
33
|
+
/**
|
|
34
|
+
* Visibility state of the disclosed content. Models are allowed to extend the states to fit
|
|
35
|
+
* their needs, so if you need to consistently determine "not hidden", use `visibility !==
|
|
36
|
+
* 'hidden'` rather than `visibility === 'visible'`
|
|
37
|
+
*/
|
|
38
|
+
visibility,
|
|
39
|
+
};
|
|
40
|
+
// Sets events that can be used across subcomponents
|
|
41
|
+
const events = {
|
|
42
|
+
/**
|
|
43
|
+
* Start showing the disclosed content. If a DOM event triggered this event, the event data will
|
|
44
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
45
|
+
*/
|
|
46
|
+
show(event?: Event | React.SyntheticEvent) {
|
|
47
|
+
setVisibility('visible');
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Start hiding this disclosed content. If a DOM event triggered this event, the event data will
|
|
51
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
52
|
+
*/
|
|
53
|
+
hide(event?: Event | React.SyntheticEvent) {
|
|
54
|
+
setVisibility('hidden');
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return {state, events};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Disclose Target
|
|
62
|
+
interface DisclosureTargetProps extends PrimaryButtonProps {}
|
|
63
|
+
|
|
64
|
+
// Use createElemPropsHook to define an onClick prop using the model state and events
|
|
65
|
+
// This will be merged into the rest of the elemProps of the component
|
|
66
|
+
const useDisclosureTarget = createElemPropsHook(useDisclosureModel)(({state, events}) => {
|
|
67
|
+
return {
|
|
68
|
+
onClick: (event: React.MouseEvent) => {
|
|
69
|
+
if (state.visibility !== 'hidden') {
|
|
70
|
+
events.hide(event);
|
|
71
|
+
} else {
|
|
72
|
+
events.show(event);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const DisclosureTarget = createSubcomponent('button')({
|
|
79
|
+
modelHook: useDisclosureModel,
|
|
80
|
+
elemPropsHook: useDisclosureTarget,
|
|
81
|
+
})<DisclosureTargetProps>(({children, ...elemProps}, Element, model) => {
|
|
82
|
+
return (
|
|
83
|
+
<PrimaryButton as={Element} {...elemProps}>
|
|
84
|
+
{children}
|
|
85
|
+
</PrimaryButton>
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Disclosure Content
|
|
90
|
+
interface DisclosureContentProps extends BoxProps {}
|
|
91
|
+
const useDisclosureContent = createElemPropsHook(useDisclosureModel)(({state}) => {
|
|
92
|
+
return {
|
|
93
|
+
style: state.visibility !== 'hidden' ? {} : {display: 'none'},
|
|
94
|
+
id: state.id,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const DisclosureContent = createSubcomponent('div')({
|
|
99
|
+
modelHook: useDisclosureModel,
|
|
100
|
+
// attached our elemPropsHook to the component
|
|
101
|
+
elemPropsHook: useDisclosureContent,
|
|
102
|
+
})<DisclosureContentProps>(({children, ...elementProps}, Element) => {
|
|
103
|
+
return (
|
|
104
|
+
<Box as={Element} {...elementProps}>
|
|
105
|
+
{children}
|
|
106
|
+
</Box>
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Disclosure Container Component
|
|
111
|
+
interface DisclosureProps {}
|
|
112
|
+
|
|
113
|
+
const Disclosure = createContainer()({
|
|
114
|
+
displayName: 'Disclosure',
|
|
115
|
+
modelHook: useDisclosureModel,
|
|
116
|
+
subComponents: {
|
|
117
|
+
Target: DisclosureTarget,
|
|
118
|
+
Content: DisclosureContent,
|
|
119
|
+
},
|
|
120
|
+
})<DisclosureProps>(({children}, Element, model) => {
|
|
121
|
+
return <>{children}</>;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export default () => {
|
|
125
|
+
return (
|
|
126
|
+
<Disclosure>
|
|
127
|
+
<Disclosure.Target>Open</Disclosure.Target>
|
|
128
|
+
<Disclosure.Content>Content</Disclosure.Content>
|
|
129
|
+
</Disclosure>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {createContainer, createModelHook, useUniqueId} from '@workday/canvas-kit-react/common';
|
|
3
|
+
|
|
4
|
+
export type Visibility = 'hidden' | 'visible';
|
|
5
|
+
|
|
6
|
+
// First we define a model using out createModelHook
|
|
7
|
+
const useDisclosureModel = createModelHook({
|
|
8
|
+
// This becomes the default values on the model
|
|
9
|
+
defaultConfig: {
|
|
10
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
11
|
+
id: '',
|
|
12
|
+
/**
|
|
13
|
+
* The initial visibility of the disclosed content. The `as Visibility` overrides the inferred type.
|
|
14
|
+
*/
|
|
15
|
+
initialVisibility: 'hidden' as Visibility,
|
|
16
|
+
},
|
|
17
|
+
})(config => {
|
|
18
|
+
const id = useUniqueId(config.id);
|
|
19
|
+
const [visibility, setVisibility] = React.useState(config.initialVisibility);
|
|
20
|
+
// Set the default internal state for your model.
|
|
21
|
+
const state = {
|
|
22
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
23
|
+
id,
|
|
24
|
+
/**
|
|
25
|
+
* Visibility state of the disclosed content. Models are allowed to extend the states to fit
|
|
26
|
+
* their needs, so if you need to consistently determine "not hidden", use `visibility !==
|
|
27
|
+
* 'hidden'` rather than `visibility === 'visible'`
|
|
28
|
+
*/
|
|
29
|
+
visibility,
|
|
30
|
+
};
|
|
31
|
+
// Sets events that can be used across subcomponents
|
|
32
|
+
const events = {
|
|
33
|
+
/**
|
|
34
|
+
* Start showing the disclosed content. If a DOM event triggered this event, the event data will
|
|
35
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
36
|
+
*/
|
|
37
|
+
show(event?: Event | React.SyntheticEvent) {
|
|
38
|
+
setVisibility('visible');
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Start hiding this disclosed content. If a DOM event triggered this event, the event data will
|
|
42
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
43
|
+
*/
|
|
44
|
+
hide(event?: Event | React.SyntheticEvent) {
|
|
45
|
+
setVisibility('hidden');
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return {state, events};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Disclosure Container Component
|
|
53
|
+
interface DisclosureProps {}
|
|
54
|
+
|
|
55
|
+
const Disclosure = createContainer()({
|
|
56
|
+
displayName: 'Disclosure',
|
|
57
|
+
modelHook: useDisclosureModel,
|
|
58
|
+
})<DisclosureProps>(({children, ...elemProps}, Element, model) => {
|
|
59
|
+
return (
|
|
60
|
+
<button
|
|
61
|
+
onClick={() =>
|
|
62
|
+
model.state.visibility === 'visible' ? model.events.hide() : model.events.show()
|
|
63
|
+
}
|
|
64
|
+
{...elemProps}
|
|
65
|
+
>
|
|
66
|
+
{model.state.visibility === 'visible' ? 'close' : 'open'}
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export default () => {
|
|
72
|
+
return <Disclosure />;
|
|
73
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
createContainer,
|
|
4
|
+
createModelHook,
|
|
5
|
+
createSubcomponent,
|
|
6
|
+
useUniqueId,
|
|
7
|
+
} from '@workday/canvas-kit-react/common';
|
|
8
|
+
import {PrimaryButton, PrimaryButtonProps} from '@workday/canvas-kit-react/button';
|
|
9
|
+
|
|
10
|
+
export type Visibility = 'hidden' | 'visible';
|
|
11
|
+
|
|
12
|
+
// First we define a model using out createModelHook
|
|
13
|
+
const useDisclosureModel = createModelHook({
|
|
14
|
+
// This becomes the default values on the model
|
|
15
|
+
defaultConfig: {
|
|
16
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
17
|
+
id: '',
|
|
18
|
+
/**
|
|
19
|
+
* The initial visibility of the disclosed content
|
|
20
|
+
* @default 'hidden'
|
|
21
|
+
*/
|
|
22
|
+
initialVisibility: 'hidden' as Visibility,
|
|
23
|
+
},
|
|
24
|
+
})(config => {
|
|
25
|
+
const id = useUniqueId(config.id);
|
|
26
|
+
const [visibility, setVisibility] = React.useState(config.initialVisibility || 'hidden');
|
|
27
|
+
// Set the default internal state for your model.
|
|
28
|
+
const state = {
|
|
29
|
+
/** ID reference of the list. Children ids can be derived from this id */
|
|
30
|
+
id,
|
|
31
|
+
/**
|
|
32
|
+
* Visibility state of the disclosed content. Models are allowed to extend the states to fit
|
|
33
|
+
* their needs, so if you need to consistently determine "not hidden", use `visibility !==
|
|
34
|
+
* 'hidden'` rather than `visibility === 'visible'`
|
|
35
|
+
*/
|
|
36
|
+
visibility,
|
|
37
|
+
};
|
|
38
|
+
// Sets events that can be used across subcomponents
|
|
39
|
+
const events = {
|
|
40
|
+
/**
|
|
41
|
+
* Start showing the disclosed content. If a DOM event triggered this event, the event data will
|
|
42
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
43
|
+
*/
|
|
44
|
+
show(event?: Event | React.SyntheticEvent) {
|
|
45
|
+
setVisibility('visible');
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Start hiding this disclosed content. If a DOM event triggered this event, the event data will
|
|
49
|
+
* be passed along. This data can be used by guards and callbacks.
|
|
50
|
+
*/
|
|
51
|
+
hide(event?: Event | React.SyntheticEvent) {
|
|
52
|
+
setVisibility('hidden');
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {state, events};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Disclose Target
|
|
60
|
+
export interface DisclosureTargetProps extends PrimaryButtonProps {}
|
|
61
|
+
|
|
62
|
+
export default createSubcomponent('button')({
|
|
63
|
+
modelHook: useDisclosureModel,
|
|
64
|
+
})<DisclosureTargetProps>(({children, ...elemProps}, Element, model) => {
|
|
65
|
+
return (
|
|
66
|
+
<PrimaryButton
|
|
67
|
+
onClick={(event: React.MouseEvent) => {
|
|
68
|
+
if (model.state.visibility !== 'hidden') {
|
|
69
|
+
model.events.hide(event);
|
|
70
|
+
} else {
|
|
71
|
+
model.events.show(event);
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
as={Element}
|
|
75
|
+
{...elemProps}
|
|
76
|
+
>
|
|
77
|
+
{model.state.visibility === 'visible' ? 'Close' : 'Open'}
|
|
78
|
+
</PrimaryButton>
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Disclosure Container Component
|
|
83
|
+
interface DisclosureProps {}
|
|
84
|
+
|
|
85
|
+
const Disclosure = createContainer()({
|
|
86
|
+
displayName: 'Disclosure',
|
|
87
|
+
modelHook: useDisclosureModel,
|
|
88
|
+
subComponents: {
|
|
89
|
+
Target: DisclosureTarget,
|
|
90
|
+
},
|
|
91
|
+
})<DisclosureProps>(({children}, Element, model) => {
|
|
92
|
+
return <>{children}</>;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export default () => {
|
|
96
|
+
return (
|
|
97
|
+
<Disclosure>
|
|
98
|
+
<Disclosure.Target />
|
|
99
|
+
</Disclosure>
|
|
100
|
+
);
|
|
101
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workday/canvas-kit-docs",
|
|
3
|
-
"version": "8.4.
|
|
3
|
+
"version": "8.4.9",
|
|
4
4
|
"description": "Documentation components of Canvas Kit components",
|
|
5
5
|
"author": "Workday, Inc. (https://www.workday.com)",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@storybook/csf": "0.0.1",
|
|
45
|
-
"@workday/canvas-kit-react": "^8.4.
|
|
45
|
+
"@workday/canvas-kit-react": "^8.4.9",
|
|
46
46
|
"@workday/canvas-system-icons-web": "^3.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"mkdirp": "^1.0.3",
|
|
52
52
|
"typescript": "4.1"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "2afa0a6e3c1bae0b94a209ed3ad9c07db25edbbf"
|
|
55
55
|
}
|