@workday/canvas-kit-docs 5.3.17 → 5.4.0-next.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/dist/commonjs/lib/specs.js +429 -19
- package/dist/es6/lib/specs.js +429 -19
- package/dist/mdx/6.0-MIGRATION-GUIDE.mdx +567 -0
- package/dist/mdx/COMPOUND_COMPONENTS.mdx +31 -30
- package/dist/mdx/TESTING.mdx +30 -6
- package/dist/mdx/changelog.stories.mdx +0 -1
- package/dist/mdx/labs-react/layout/Stack.mdx +7 -2
- package/dist/mdx/labs-react/search-form/SearchForm.mdx +62 -0
- package/dist/mdx/labs-react/search-form/examples/Basic.tsx +61 -0
- package/dist/mdx/labs-react/search-form/examples/CustomTheme.tsx +72 -0
- package/dist/mdx/labs-react/search-form/examples/Grow.tsx +62 -0
- package/dist/mdx/labs-react/search-form/examples/PropTables.splitProps.tsx +4 -0
- package/dist/mdx/labs-react/search-form/examples/RTL.tsx +70 -0
- package/dist/mdx/labs-react/search-form/examples/Theming.tsx +64 -0
- package/dist/mdx/preview-react/_examples/SelectWithFormik.mdx +8 -0
- package/dist/mdx/preview-react/_examples/TextInputWithFormik.mdx +8 -0
- package/dist/mdx/preview-react/_examples/examples/SelectWithFormik.tsx +47 -0
- package/dist/mdx/preview-react/_examples/examples/TextInputWithFormik.tsx +105 -0
- package/dist/mdx/preview-react/form-field/FormField.mdx +39 -0
- package/dist/mdx/preview-react/form-field/examples/Custom.tsx +57 -0
- package/dist/mdx/preview-react/form-field/examples/Select.tsx +50 -0
- package/dist/mdx/preview-react/side-panel/examples/AlwaysOpen.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/Basic.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/ExternalControl.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/RightOrigin.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/Variant.tsx +1 -1
- package/dist/mdx/preview-react/text-area/TextArea.mdx +122 -0
- package/dist/mdx/preview-react/text-area/examples/Alert.tsx +36 -0
- package/dist/mdx/preview-react/text-area/examples/Basic.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Disabled.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Error.tsx +40 -0
- package/dist/mdx/preview-react/text-area/examples/Grow.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/HiddenLabel.tsx +20 -0
- package/dist/mdx/preview-react/text-area/examples/LabelPositionHorizontal.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/LabelPositionVertical.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Placeholder.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/RefForwarding.tsx +28 -0
- package/dist/mdx/preview-react/text-area/examples/Required.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/ResizeConstraints.tsx +22 -0
- package/dist/mdx/preview-react/text-input/TextInput.mdx +138 -0
- package/dist/mdx/preview-react/text-input/examples/Alert.tsx +35 -0
- package/dist/mdx/preview-react/text-input/examples/Basic.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Disabled.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Error.tsx +40 -0
- package/dist/mdx/preview-react/text-input/examples/Grow.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/HiddenLabel.tsx +20 -0
- package/dist/mdx/preview-react/text-input/examples/LabelPositionHorizontal.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/LabelPositionVertical.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Password.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Placeholder.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/RefForwarding.tsx +28 -0
- package/dist/mdx/preview-react/text-input/examples/Required.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/ThemedAlert.tsx +46 -0
- package/dist/mdx/preview-react/text-input/examples/ThemedError.tsx +35 -0
- package/dist/mdx/react/_examples/CookieBanner.mdx +8 -0
- package/dist/mdx/react/_examples/GlobalHeader.mdx +12 -0
- package/dist/mdx/react/_examples/PageHeader.mdx +8 -0
- package/dist/mdx/react/_examples/examples/CookieBanner.tsx +97 -0
- package/dist/mdx/react/_examples/examples/GlobalHeader.tsx +66 -0
- package/dist/mdx/react/_examples/examples/PageHeader.tsx +63 -0
- package/dist/mdx/react/button/button/Button.mdx +26 -2
- package/dist/mdx/react/button/button/examples/Primary.tsx +10 -1
- package/dist/mdx/react/button/button/examples/PrimaryInverse.tsx +14 -0
- package/dist/mdx/react/button/button/examples/Secondary.tsx +10 -1
- package/dist/mdx/react/button/button/examples/SecondaryInverse.tsx +14 -0
- package/dist/mdx/react/button/button/examples/Tertiary.tsx +13 -1
- package/dist/mdx/react/button/button/examples/TertiaryInverse.tsx +14 -0
- package/dist/mdx/react/popup/examples/FocusRedirect.tsx +2 -1
- package/dist/mdx/react/tabs/Tabs.mdx +31 -5
- package/dist/mdx/react/tabs/examples/DisabledTab.tsx +1 -1
- package/dist/mdx/react/tabs/examples/DynamicTabs.tsx +82 -38
- package/dist/mdx/react/tabs/examples/HoistedModel.tsx +4 -4
- package/dist/mdx/react/tabs/examples/Icons.tsx +36 -0
- package/dist/mdx/react/tabs/examples/{NamedKeys.tsx → NamedTabs.tsx} +0 -0
- package/dist/mdx/react/tabs/examples/OverflowTabs.tsx +58 -0
- package/dist/mdx/react/tabs/examples/SinglePanel.tsx +1 -1
- package/dist/mdx/react/text-area/TextArea.mdx +1 -1
- package/package.json +3 -3
|
@@ -78,8 +78,8 @@ const Tabs = ({children, model, ...config}) => {
|
|
|
78
78
|
|
|
79
79
|
## Subcomponents
|
|
80
80
|
|
|
81
|
-
A subcomponent typically follows ARIA roles. For the `Tabs` example, these are the `tablist`,
|
|
82
|
-
|
|
81
|
+
A subcomponent typically follows ARIA roles. For the `Tabs` example, these are the `tablist`, `tab`,
|
|
82
|
+
and `tabpanel` roles. A subcomponent provides direct access to semantic or key elements of a
|
|
83
83
|
compound component. In the `IconButton` example, the icon is not semantic and might be hidden from
|
|
84
84
|
screen readers while the `IconButton.Text` content is instead used for a tooltip and as the
|
|
85
85
|
accessible name while being visibly hidden.
|
|
@@ -175,35 +175,35 @@ Components that directly wrap an element (most of them) will have the following
|
|
|
175
175
|
|
|
176
176
|
Compound components are also made up of [models](#models) that accept [guards](#guards) to
|
|
177
177
|
conditionally prevent state changes and [callbacks](#callbacks) to attach listeners. For example, in
|
|
178
|
-
our Tabs component clicking a Tab will
|
|
179
|
-
|
|
178
|
+
our Tabs component clicking a Tab will select that tab. The `Tabs` container component will accept a
|
|
179
|
+
`shouldSelect` and a `onSelect` for the event called `select`.
|
|
180
180
|
|
|
181
181
|
```tsx
|
|
182
182
|
const MyComponent = () => {
|
|
183
|
-
// `data` is all event data from the `
|
|
183
|
+
// `data` is all event data from the `select` event
|
|
184
184
|
|
|
185
185
|
// `state` is the current state of the `Tabs` component
|
|
186
|
-
const
|
|
187
|
-
// for some reason, we only want to allow
|
|
188
|
-
// Clicking on the first tab will
|
|
186
|
+
const shouldSelect = ({data, state}) => {
|
|
187
|
+
// for some reason, we only want to allow selection the 'first' tab
|
|
188
|
+
// Clicking on the first tab will select it, but clicking on the
|
|
189
189
|
// second tab will do nothing
|
|
190
190
|
return data.tab === 'first' ? true : false;
|
|
191
191
|
|
|
192
192
|
// returning true allows the event to trigger a state change and will
|
|
193
|
-
// also call the `
|
|
193
|
+
// also call the `onSelect` callback
|
|
194
194
|
};
|
|
195
195
|
|
|
196
196
|
// `prevState` is the previous state of the model. Callbacks are called _before_ state has resolved.
|
|
197
197
|
// This means the passed state hasn't updated yet. It also means it is safe to call `setState` without
|
|
198
198
|
// triggering extra renders. `setState` calls will add to React's batching system before a state changes
|
|
199
199
|
// are flushed and render functions are called.
|
|
200
|
-
const
|
|
201
|
-
// called any time the `
|
|
202
|
-
console.log('
|
|
200
|
+
const onSelect = ({data, prevState}) => {
|
|
201
|
+
// called any time the `select` event is triggered
|
|
202
|
+
console.log('onSelect', data, prevState);
|
|
203
203
|
};
|
|
204
204
|
|
|
205
205
|
return (
|
|
206
|
-
<Tabs
|
|
206
|
+
<Tabs shouldSelect={shouldSelect} onSelect={onSelect}>
|
|
207
207
|
<Tabs.List>
|
|
208
208
|
<Tabs.Item name="first">First</Tabs.Item>
|
|
209
209
|
<Tabs.Item name="second">Second</Tabs.Item>
|
|
@@ -324,20 +324,20 @@ const useEllipsisTooltipModel = (config = {}) => {
|
|
|
324
324
|
|
|
325
325
|
Models are meant to be composable. For example, a `TabsModel` uses a `CursorModel` (which itself
|
|
326
326
|
uses `ListModel`) and a `ListModel` for a list of panels. `TabsModel` also keeps track of which tab
|
|
327
|
-
is currently
|
|
327
|
+
is currently selected. This might look like the following:
|
|
328
328
|
|
|
329
329
|
```ts
|
|
330
330
|
const useTabsModel = (config = {}) => {
|
|
331
331
|
// id is used for ARIA attributes
|
|
332
332
|
const id = useUniqueId(config.id);
|
|
333
|
-
const [
|
|
333
|
+
const [selectedTab, setSelectedTab] = React.useState('');
|
|
334
334
|
const cursor = useCursorModel(config);
|
|
335
335
|
const panels = useListModel(config);
|
|
336
336
|
|
|
337
337
|
const state = {
|
|
338
338
|
...cursor.state, // extend the CursorModel state
|
|
339
339
|
id,
|
|
340
|
-
|
|
340
|
+
selectedTab,
|
|
341
341
|
panels: panels.state.items, // we only care about
|
|
342
342
|
};
|
|
343
343
|
|
|
@@ -346,12 +346,12 @@ const useTabsModel = (config = {}) => {
|
|
|
346
346
|
registerPanel: panels.events.registerItem,
|
|
347
347
|
unregisterPanel: panels.events.unregisterItem,
|
|
348
348
|
|
|
349
|
-
|
|
350
|
-
if (config.
|
|
349
|
+
select(data) {
|
|
350
|
+
if (config.shouldSelect?.({data, prevState: state}) === false) {
|
|
351
351
|
return;
|
|
352
352
|
}
|
|
353
|
-
|
|
354
|
-
config.
|
|
353
|
+
setSelectedTab(data.tab);
|
|
354
|
+
config.onSelect?.({data, prevState: state});
|
|
355
355
|
},
|
|
356
356
|
};
|
|
357
357
|
|
|
@@ -362,10 +362,11 @@ const useTabsModel = (config = {}) => {
|
|
|
362
362
|
Model composition allows for components to share functionality with other components. In the Tabs
|
|
363
363
|
example, `ListModel` is in charge of maintaining a list of tab elements. The `CursorModel` is in
|
|
364
364
|
charge of maintaining a current cursor position of the tab list. The `Tabs.List` component uses the
|
|
365
|
-
cursor to allow keyboard navigation of the tabs. The `TabsModel` also maintains the currently
|
|
366
|
-
tab to ensure the correct `TabPanel` is visible. The `TabsModel` is also using a
|
|
367
|
-
maintain a list of tab panels. The `TabsModel` is in charge of composing all this and
|
|
368
|
-
and events to the `Tabs` compound component - coordination state between
|
|
365
|
+
cursor to allow keyboard navigation of the tabs. The `TabsModel` also maintains the currently
|
|
366
|
+
selected tab to ensure the correct `TabPanel` is visible. The `TabsModel` is also using a
|
|
367
|
+
`ListModel` to maintain a list of tab panels. The `TabsModel` is in charge of composing all this and
|
|
368
|
+
providing data and events to the `Tabs` compound component - coordination state between
|
|
369
|
+
subcomponents.
|
|
369
370
|
|
|
370
371
|
Many other components like `Select`, `Breadcrumbs`, or dropdown menus can also use the `ListModel`
|
|
371
372
|
and/or the `CursorModel`. These models could be thought of as abstract models where they do not
|
|
@@ -463,11 +464,11 @@ const TabList = ({children, ...elemProps}) => {
|
|
|
463
464
|
|
|
464
465
|
A container component can either accept model configuration _or_ a model. Passing model
|
|
465
466
|
configuration allows for simpler model configuration of guards, callbacks, or any other model
|
|
466
|
-
configuration. The following example provides an `
|
|
467
|
-
|
|
467
|
+
configuration. The following example provides an `onSelect` callback that fetches some data from the
|
|
468
|
+
server:
|
|
468
469
|
|
|
469
470
|
```tsx
|
|
470
|
-
<Tabs
|
|
471
|
+
<Tabs onSelect={({data}) => fetch('/api/selectTab' + data.id)}>...</Tabs>
|
|
471
472
|
```
|
|
472
473
|
|
|
473
474
|
If you need direct access to a model's state or events, you can hoist the model into your component
|
|
@@ -479,15 +480,15 @@ look like this:
|
|
|
479
480
|
const MyTabs = () => {
|
|
480
481
|
const model = useTabsModel({
|
|
481
482
|
// we can still load data from the server
|
|
482
|
-
|
|
483
|
+
onSelect: ({data}) => fetch('/api/selectTab' + data.id),
|
|
483
484
|
});
|
|
484
485
|
|
|
485
486
|
return (
|
|
486
487
|
<>
|
|
487
488
|
<Tabs model={model}>...</Tabs>
|
|
488
|
-
// direct access to the model's state Currently selected tab: {model.state.
|
|
489
|
+
// direct access to the model's state Currently selected tab: {model.state.selectedTab}
|
|
489
490
|
// Now we can send events directly to the model
|
|
490
|
-
<button onClick={() => model.events.
|
|
491
|
+
<button onClick={() => model.events.select({tab: 'third'})}>Select third tab</button>
|
|
491
492
|
</>
|
|
492
493
|
);
|
|
493
494
|
};
|
package/dist/mdx/TESTING.mdx
CHANGED
|
@@ -29,7 +29,7 @@ do. A "test" simply has to pass. A specification requires meaning.
|
|
|
29
29
|
test('SomeComponent should render correctly', async () => {
|
|
30
30
|
const {getByTestId} = render(<SomeComponent data-testid="test" text="foo" />);
|
|
31
31
|
|
|
32
|
-
const component =
|
|
32
|
+
const component = getByTestId('test');
|
|
33
33
|
|
|
34
34
|
expect(component.textContent).toEqual('foo');
|
|
35
35
|
expect(component.getAttribute('aria-label')).toEqual('foo');
|
|
@@ -53,14 +53,14 @@ describe('SomeComponent', () => {
|
|
|
53
53
|
it('should render the "text" prop as the text', async () => {
|
|
54
54
|
const {getByTestId} = render(<SomeComponent data-testid="test" text="foo" />);
|
|
55
55
|
|
|
56
|
-
const component =
|
|
56
|
+
const component = getByTestId('test');
|
|
57
57
|
expect(component).toHaveTextContent('foo');
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
it('should render the "text" prop as an aria-label for accessibility', async () => {
|
|
61
61
|
const {getByTestId} = render(<SomeComponent data-testid="test" text="foo" />);
|
|
62
62
|
|
|
63
|
-
const component =
|
|
63
|
+
const component = getByTestId('test');
|
|
64
64
|
expect(component).toHaveAttribute('aria-label', 'foo');
|
|
65
65
|
});
|
|
66
66
|
});
|
|
@@ -124,8 +124,8 @@ const {getByTestId} = render(
|
|
|
124
124
|
</SomeComponent>
|
|
125
125
|
);
|
|
126
126
|
|
|
127
|
-
const component =
|
|
128
|
-
const child =
|
|
127
|
+
const component = getByTestId('container');
|
|
128
|
+
const child = getByTestId('test');
|
|
129
129
|
|
|
130
130
|
expect(component).toContainElement(child);
|
|
131
131
|
```
|
|
@@ -136,9 +136,33 @@ specification is met). Also this example will have a more useful failure message
|
|
|
136
136
|
`.toContainElement` has the context that it is expecting an element in another element vs a match of
|
|
137
137
|
a string.
|
|
138
138
|
|
|
139
|
+
### Snapshot tests
|
|
140
|
+
|
|
141
|
+
Canvas Kit does not contain DOM-based snapshot tests and uses [Visual Tests](#visual-tests) instead.
|
|
142
|
+
DOM snapshots failures are often difficult to parse. Humans tend to be better at noticing and
|
|
143
|
+
discerning visual changes than changes to a DOM structure.
|
|
144
|
+
|
|
145
|
+
If your project uses snapshot tests and Canvas Kit, chances are you'll run into issues with changing
|
|
146
|
+
ids and other ARIA attributes. Canvas Kit generates unique ids that are different every time the
|
|
147
|
+
page loads. This can be a problem with snapshot tests. To fix this, you'll need to add special code
|
|
148
|
+
to your test bootstrap file. For example:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import {setUniqueSeed, resetUniqueIdCount} from '@workday/canvas-kit-react/common';
|
|
152
|
+
|
|
153
|
+
beforeEach(() => {
|
|
154
|
+
setUniqueSeed('a'); // set a static seed
|
|
155
|
+
resetUniqueIdCount(); // reset the id counter before every test
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
This will ensure snapshot tests have stable ids for each snapshot. It is still possible to get ids
|
|
160
|
+
changing if you add an additional component that uses id generation - subsequent ids will be
|
|
161
|
+
different, but this will prevent snapshot tests that don't have any changes from showing diffs.
|
|
162
|
+
|
|
139
163
|
## Functional tests
|
|
140
164
|
|
|
141
|
-
Canvas Kit uses [Cypress]
|
|
165
|
+
Canvas Kit uses [Cypress](https://cypress.io) for browser-based behavior testing (additional info:
|
|
142
166
|
[Why Cypress?](https://github.com/Workday/canvas-kit/tree/master/cypress/WHY_CYPRESS.md)).
|
|
143
167
|
Functional tests ensure the code meets functional specifications from a user's perspective. All
|
|
144
168
|
functional tests are written against a Storybook Story. Specifications can come from many different
|
|
@@ -153,6 +153,11 @@ further to the right. You could use two separate horizontal stacks here to achie
|
|
|
153
153
|
but you could also use `shouldWrapChildren` and then put additional margin only where needed. Either
|
|
154
154
|
approach would be fine; it's a matter of personal preference.
|
|
155
155
|
|
|
156
|
+
> Note: `shouldWrapChildren` implicitly passes the child element's index as a unique `key` prop to
|
|
157
|
+
> the `Stack.Item`, which works well for static lists. If you have dynamic lists with child elements
|
|
158
|
+
> that change over time, you'll want to provide a unique `id` prop to each child element for the
|
|
159
|
+
> `Stack.Item` to use as a key instead.
|
|
160
|
+
|
|
156
161
|
<ExampleCodeBlock code={ShouldWrapChildren} />
|
|
157
162
|
|
|
158
163
|
Use this approach when:
|
|
@@ -170,8 +175,8 @@ wrapping children in `Stack.Item`s. A `Stack.Item` is a `Box` with some preset s
|
|
|
170
175
|
overridden if needed.
|
|
171
176
|
|
|
172
177
|
In the example below, we wanted to keep our `li` elements as direct children of the `ul` stack. So
|
|
173
|
-
we're wrapping each of the links with `Stack.Item`s, casting them as `li`s and applying custom
|
|
174
|
-
This would not be possible with `shouldWrapChildren`.
|
|
178
|
+
we're wrapping each of the links with `Stack.Item`s, casting them as `li`s and applying custom
|
|
179
|
+
styles to each. This would not be possible with `shouldWrapChildren`.
|
|
175
180
|
|
|
176
181
|
<ExampleCodeBlock code={StackItems} />
|
|
177
182
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {SearchForm} from '@workday/canvas-kit-labs-react/search-form';
|
|
2
|
+
// examples
|
|
3
|
+
import Basic from './examples/Basic';
|
|
4
|
+
import CustomTheme from './examples/CustomTheme';
|
|
5
|
+
import Grow from './examples/Grow';
|
|
6
|
+
import RTL from './examples/RTL';
|
|
7
|
+
import Theming from './examples/Theming';
|
|
8
|
+
import ThemeAttributes from './examples/PropTables.splitProps';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Canvas Kit Search Form
|
|
12
|
+
|
|
13
|
+
`SearchForm` is a search form that contains a `Combobox` for rendering search results. It's
|
|
14
|
+
primarily intended to be used within a `Header`.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
yarn add @workday/canvas-kit-labs-react
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Example
|
|
25
|
+
|
|
26
|
+
You will most likely use `SearchForm` within the context of a `Header` component, but it's helpful
|
|
27
|
+
to see its functionality as a standalone component. Below is a basic example that filters results
|
|
28
|
+
based on search input.
|
|
29
|
+
|
|
30
|
+
<ExampleCodeBlock code={Basic} />
|
|
31
|
+
|
|
32
|
+
### Grow
|
|
33
|
+
|
|
34
|
+
If you'd like `SearchForm` to grow to the width of its container, set the `grow` prop to `true`.
|
|
35
|
+
|
|
36
|
+
<ExampleCodeBlock code={Grow} />
|
|
37
|
+
|
|
38
|
+
### Theming
|
|
39
|
+
|
|
40
|
+
`SearchForm` can be themed to use one of the preset `SearchThemes`: `Light`, `Dark`, or
|
|
41
|
+
`Transparent`. Below is an example of `SearchForm` using the `Dark` theme.
|
|
42
|
+
|
|
43
|
+
<ExampleCodeBlock code={Theming} />
|
|
44
|
+
|
|
45
|
+
You can also provide more specific theming values by providing specific `SearchThemeAttributes`.
|
|
46
|
+
|
|
47
|
+
<ExampleCodeBlock code={CustomTheme} />
|
|
48
|
+
|
|
49
|
+
Below is a table of attributes that can be supplied to `SearchForm`:
|
|
50
|
+
|
|
51
|
+
<ArgsTable of={ThemeAttributes} />
|
|
52
|
+
|
|
53
|
+
### RTL (Right-To-Left)
|
|
54
|
+
|
|
55
|
+
`SearchForm` provides bidirectional support out of the box. You shouldn't need to provide any
|
|
56
|
+
additional configuration.
|
|
57
|
+
|
|
58
|
+
<ExampleCodeBlock code={RTL} />
|
|
59
|
+
|
|
60
|
+
## Props
|
|
61
|
+
|
|
62
|
+
<ArgsTable of={SearchForm} />
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {MenuItem} from '@workday/canvas-kit-preview-react/menu';
|
|
3
|
+
import {SearchForm} from '@workday/canvas-kit-labs-react/search-form';
|
|
4
|
+
import {Flex} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
|
|
6
|
+
const initialWineList = [
|
|
7
|
+
'Beaujolais',
|
|
8
|
+
'Bordeaux',
|
|
9
|
+
'Cabernet Sauvignon',
|
|
10
|
+
'Champagne',
|
|
11
|
+
'Chardonnay',
|
|
12
|
+
'Chianti',
|
|
13
|
+
'Malbec',
|
|
14
|
+
'Merlot',
|
|
15
|
+
'Pinot Grigio',
|
|
16
|
+
'Pinot Gris',
|
|
17
|
+
'Pinot Noir',
|
|
18
|
+
'Primitivo',
|
|
19
|
+
'Prosecco',
|
|
20
|
+
'Riesling',
|
|
21
|
+
'Rioja',
|
|
22
|
+
'Rosé',
|
|
23
|
+
'Sauvignon Blanc',
|
|
24
|
+
'Syrah',
|
|
25
|
+
'Zinfandel',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export default () => {
|
|
29
|
+
const [wineList, setWineList] = React.useState(initialWineList);
|
|
30
|
+
// Tracking the input value for onSubmit
|
|
31
|
+
const [searchInput, setSearchInput] = React.useState('');
|
|
32
|
+
const menuItems = wineList.map(wine => <MenuItem key={wine}>{wine}</MenuItem>);
|
|
33
|
+
|
|
34
|
+
const filterMenuItems = e => {
|
|
35
|
+
setSearchInput(e.target.value);
|
|
36
|
+
const formattedValue = e.target.value.toLowerCase();
|
|
37
|
+
|
|
38
|
+
// Reset the list if the input is cleared
|
|
39
|
+
if (!formattedValue.length) {
|
|
40
|
+
setWineList(initialWineList);
|
|
41
|
+
} else {
|
|
42
|
+
const filteredItems = wineList.filter(wine => wine.toLowerCase().startsWith(formattedValue));
|
|
43
|
+
setWineList(filteredItems);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleSubmit = () => {
|
|
48
|
+
// We don't need to prevent the default event because SearchForm handles that internally
|
|
49
|
+
console.log(`Searching for: ${searchInput}`);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Flex minHeight={200} alignItems="flex-start" padding="xs">
|
|
54
|
+
<SearchForm
|
|
55
|
+
autocompleteItems={menuItems}
|
|
56
|
+
onInputChange={filterMenuItems}
|
|
57
|
+
onSubmit={handleSubmit}
|
|
58
|
+
/>
|
|
59
|
+
</Flex>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {MenuItem} from '@workday/canvas-kit-preview-react/menu';
|
|
3
|
+
import {SearchForm, SearchThemeAttributes} from '@workday/canvas-kit-labs-react/search-form';
|
|
4
|
+
import {Flex} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {colors} from '@workday/canvas-kit-react/tokens';
|
|
6
|
+
|
|
7
|
+
const initialWineList = [
|
|
8
|
+
'Beaujolais',
|
|
9
|
+
'Bordeaux',
|
|
10
|
+
'Cabernet Sauvignon',
|
|
11
|
+
'Champagne',
|
|
12
|
+
'Chardonnay',
|
|
13
|
+
'Chianti',
|
|
14
|
+
'Malbec',
|
|
15
|
+
'Merlot',
|
|
16
|
+
'Pinot Grigio',
|
|
17
|
+
'Pinot Gris',
|
|
18
|
+
'Pinot Noir',
|
|
19
|
+
'Primitivo',
|
|
20
|
+
'Prosecco',
|
|
21
|
+
'Riesling',
|
|
22
|
+
'Rioja',
|
|
23
|
+
'Rosé',
|
|
24
|
+
'Sauvignon Blanc',
|
|
25
|
+
'Syrah',
|
|
26
|
+
'Zinfandel',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export default () => {
|
|
30
|
+
const [wineList, setWineList] = React.useState(initialWineList);
|
|
31
|
+
// Tracking the input value for onSubmit
|
|
32
|
+
const [searchInput, setSearchInput] = React.useState('');
|
|
33
|
+
const menuItems = wineList.map(wine => <MenuItem key={wine}>{wine}</MenuItem>);
|
|
34
|
+
|
|
35
|
+
const filterMenuItems = e => {
|
|
36
|
+
setSearchInput(e.target.value);
|
|
37
|
+
const formattedValue = e.target.value.toLowerCase();
|
|
38
|
+
|
|
39
|
+
// Reset the list if the input is cleared
|
|
40
|
+
if (!formattedValue.length) {
|
|
41
|
+
setWineList(initialWineList);
|
|
42
|
+
} else {
|
|
43
|
+
const filteredItems = wineList.filter(wine => wine.toLowerCase().startsWith(formattedValue));
|
|
44
|
+
setWineList(filteredItems);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handleSubmit = () => {
|
|
49
|
+
// We don't need to prevent the default event because SearchForm handles that internally
|
|
50
|
+
console.log(`Searching for: ${searchInput}`);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const customTheme = {
|
|
54
|
+
background: colors.cinnamon600,
|
|
55
|
+
backgroundFocus: colors.frenchVanilla100,
|
|
56
|
+
placeholderColor: colors.frenchVanilla100,
|
|
57
|
+
placeholderColorFocus: colors.blackPepper400,
|
|
58
|
+
} as SearchThemeAttributes;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Flex minHeight={200} alignItems="flex-start">
|
|
62
|
+
<Flex padding="xs" backgroundColor="cinnamon500" flex={1} flexBasis="auto">
|
|
63
|
+
<SearchForm
|
|
64
|
+
searchTheme={customTheme}
|
|
65
|
+
autocompleteItems={menuItems}
|
|
66
|
+
onInputChange={filterMenuItems}
|
|
67
|
+
onSubmit={handleSubmit}
|
|
68
|
+
/>
|
|
69
|
+
</Flex>
|
|
70
|
+
</Flex>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {MenuItem} from '@workday/canvas-kit-preview-react/menu';
|
|
3
|
+
import {SearchForm} from '@workday/canvas-kit-labs-react/search-form';
|
|
4
|
+
import {Flex} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
|
|
6
|
+
const initialWineList = [
|
|
7
|
+
'Beaujolais',
|
|
8
|
+
'Bordeaux',
|
|
9
|
+
'Cabernet Sauvignon',
|
|
10
|
+
'Champagne',
|
|
11
|
+
'Chardonnay',
|
|
12
|
+
'Chianti',
|
|
13
|
+
'Malbec',
|
|
14
|
+
'Merlot',
|
|
15
|
+
'Pinot Grigio',
|
|
16
|
+
'Pinot Gris',
|
|
17
|
+
'Pinot Noir',
|
|
18
|
+
'Primitivo',
|
|
19
|
+
'Prosecco',
|
|
20
|
+
'Riesling',
|
|
21
|
+
'Rioja',
|
|
22
|
+
'Rosé',
|
|
23
|
+
'Sauvignon Blanc',
|
|
24
|
+
'Syrah',
|
|
25
|
+
'Zinfandel',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export default () => {
|
|
29
|
+
const [wineList, setWineList] = React.useState(initialWineList);
|
|
30
|
+
// Tracking the input value for onSubmit
|
|
31
|
+
const [searchInput, setSearchInput] = React.useState('');
|
|
32
|
+
const menuItems = wineList.map(wine => <MenuItem key={wine}>{wine}</MenuItem>);
|
|
33
|
+
|
|
34
|
+
const filterMenuItems = e => {
|
|
35
|
+
setSearchInput(e.target.value);
|
|
36
|
+
const formattedValue = e.target.value.toLowerCase();
|
|
37
|
+
|
|
38
|
+
// Reset the list if the input is cleared
|
|
39
|
+
if (!formattedValue.length) {
|
|
40
|
+
setWineList(initialWineList);
|
|
41
|
+
} else {
|
|
42
|
+
const filteredItems = wineList.filter(wine => wine.toLowerCase().startsWith(formattedValue));
|
|
43
|
+
setWineList(filteredItems);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleSubmit = () => {
|
|
48
|
+
// We don't need to prevent the default event because SearchForm handles that internally
|
|
49
|
+
console.log(`Searching for: ${searchInput}`);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Flex minHeight={200} alignItems="flex-start" padding="xs">
|
|
54
|
+
<SearchForm
|
|
55
|
+
autocompleteItems={menuItems}
|
|
56
|
+
grow
|
|
57
|
+
onInputChange={filterMenuItems}
|
|
58
|
+
onSubmit={handleSubmit}
|
|
59
|
+
/>
|
|
60
|
+
</Flex>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {MenuItem} from '@workday/canvas-kit-preview-react/menu';
|
|
3
|
+
import {SearchForm} from '@workday/canvas-kit-labs-react/search-form';
|
|
4
|
+
import {Flex} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {CanvasProvider, ContentDirection} from '@workday/canvas-kit-react/common';
|
|
6
|
+
|
|
7
|
+
const initialWineList = [
|
|
8
|
+
'Beaujolais',
|
|
9
|
+
'Bordeaux',
|
|
10
|
+
'Cabernet Sauvignon',
|
|
11
|
+
'Champagne',
|
|
12
|
+
'Chardonnay',
|
|
13
|
+
'Chianti',
|
|
14
|
+
'Malbec',
|
|
15
|
+
'Merlot',
|
|
16
|
+
'Pinot Grigio',
|
|
17
|
+
'Pinot Gris',
|
|
18
|
+
'Pinot Noir',
|
|
19
|
+
'Primitivo',
|
|
20
|
+
'Prosecco',
|
|
21
|
+
'Riesling',
|
|
22
|
+
'Rioja',
|
|
23
|
+
'Rosé',
|
|
24
|
+
'Sauvignon Blanc',
|
|
25
|
+
'Syrah',
|
|
26
|
+
'Zinfandel',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export default () => {
|
|
30
|
+
const [wineList, setWineList] = React.useState(initialWineList);
|
|
31
|
+
// Tracking the input value for onSubmit
|
|
32
|
+
const [searchInput, setSearchInput] = React.useState('');
|
|
33
|
+
const menuItems = wineList.map(wine => <MenuItem key={wine}>{wine}</MenuItem>);
|
|
34
|
+
|
|
35
|
+
const filterMenuItems = e => {
|
|
36
|
+
setSearchInput(e.target.value);
|
|
37
|
+
const formattedValue = e.target.value.toLowerCase();
|
|
38
|
+
|
|
39
|
+
// Reset the list if the input is cleared
|
|
40
|
+
if (!formattedValue.length) {
|
|
41
|
+
setWineList(initialWineList);
|
|
42
|
+
} else {
|
|
43
|
+
const filteredItems = wineList.filter(wine => wine.toLowerCase().startsWith(formattedValue));
|
|
44
|
+
setWineList(filteredItems);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handleSubmit = () => {
|
|
49
|
+
// We don't need to prevent the default event because SearchForm handles that internally
|
|
50
|
+
console.log(`Searching for: ${searchInput}`);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const theme = {
|
|
54
|
+
canvas: {
|
|
55
|
+
direction: ContentDirection.RTL,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<CanvasProvider theme={theme}>
|
|
61
|
+
<Flex minHeight={200} alignItems="flex-start" padding="xs">
|
|
62
|
+
<SearchForm
|
|
63
|
+
autocompleteItems={menuItems}
|
|
64
|
+
onInputChange={filterMenuItems}
|
|
65
|
+
onSubmit={handleSubmit}
|
|
66
|
+
/>
|
|
67
|
+
</Flex>
|
|
68
|
+
</CanvasProvider>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {MenuItem} from '@workday/canvas-kit-preview-react/menu';
|
|
3
|
+
import {SearchForm, SearchTheme} from '@workday/canvas-kit-labs-react/search-form';
|
|
4
|
+
import {Flex} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
|
|
6
|
+
const initialWineList = [
|
|
7
|
+
'Beaujolais',
|
|
8
|
+
'Bordeaux',
|
|
9
|
+
'Cabernet Sauvignon',
|
|
10
|
+
'Champagne',
|
|
11
|
+
'Chardonnay',
|
|
12
|
+
'Chianti',
|
|
13
|
+
'Malbec',
|
|
14
|
+
'Merlot',
|
|
15
|
+
'Pinot Grigio',
|
|
16
|
+
'Pinot Gris',
|
|
17
|
+
'Pinot Noir',
|
|
18
|
+
'Primitivo',
|
|
19
|
+
'Prosecco',
|
|
20
|
+
'Riesling',
|
|
21
|
+
'Rioja',
|
|
22
|
+
'Rosé',
|
|
23
|
+
'Sauvignon Blanc',
|
|
24
|
+
'Syrah',
|
|
25
|
+
'Zinfandel',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export default () => {
|
|
29
|
+
const [wineList, setWineList] = React.useState(initialWineList);
|
|
30
|
+
// Tracking the input value for onSubmit
|
|
31
|
+
const [searchInput, setSearchInput] = React.useState('');
|
|
32
|
+
const menuItems = wineList.map(wine => <MenuItem key={wine}>{wine}</MenuItem>);
|
|
33
|
+
|
|
34
|
+
const filterMenuItems = e => {
|
|
35
|
+
setSearchInput(e.target.value);
|
|
36
|
+
const formattedValue = e.target.value.toLowerCase();
|
|
37
|
+
|
|
38
|
+
// Reset the list if the input is cleared
|
|
39
|
+
if (!formattedValue.length) {
|
|
40
|
+
setWineList(initialWineList);
|
|
41
|
+
} else {
|
|
42
|
+
const filteredItems = wineList.filter(wine => wine.toLowerCase().startsWith(formattedValue));
|
|
43
|
+
setWineList(filteredItems);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleSubmit = () => {
|
|
48
|
+
// We don't need to prevent the default event because SearchForm handles that internally
|
|
49
|
+
console.log(`Searching for: ${searchInput}`);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Flex minHeight={200} alignItems="flex-start">
|
|
54
|
+
<Flex padding="xs" backgroundColor="blueberry500" flex={1} flexBasis="auto">
|
|
55
|
+
<SearchForm
|
|
56
|
+
searchTheme={SearchTheme.Dark}
|
|
57
|
+
autocompleteItems={menuItems}
|
|
58
|
+
onInputChange={filterMenuItems}
|
|
59
|
+
onSubmit={handleSubmit}
|
|
60
|
+
/>
|
|
61
|
+
</Flex>
|
|
62
|
+
</Flex>
|
|
63
|
+
);
|
|
64
|
+
};
|