@khanacademy/wonder-blocks-dropdown 2.7.3 → 2.7.6

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 (29) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/es/index.js +92 -162
  3. package/dist/index.js +285 -374
  4. package/package.json +6 -6
  5. package/src/components/__docs__/action-menu.argtypes.js +44 -0
  6. package/src/components/__docs__/action-menu.stories.js +435 -0
  7. package/src/components/__docs__/base-select.argtypes.js +54 -0
  8. package/src/components/__docs__/multi-select.stories.js +509 -0
  9. package/src/components/__docs__/single-select.accessibility.stories.mdx +59 -0
  10. package/src/components/__docs__/single-select.argtypes.js +54 -0
  11. package/src/components/__docs__/single-select.stories.js +464 -0
  12. package/src/components/__tests__/dropdown-core-virtualized.test.js +0 -15
  13. package/src/components/__tests__/dropdown-core.test.js +114 -208
  14. package/src/components/__tests__/multi-select.test.js +1 -3
  15. package/src/components/__tests__/single-select.test.js +15 -47
  16. package/src/components/action-menu.js +11 -0
  17. package/src/components/dropdown-core-virtualized.js +0 -5
  18. package/src/components/dropdown-core.js +140 -126
  19. package/src/components/multi-select.js +17 -33
  20. package/src/components/single-select.js +15 -30
  21. package/src/util/__tests__/dropdown-menu-styles.test.js +0 -26
  22. package/src/util/constants.js +0 -11
  23. package/src/util/dropdown-menu-styles.js +0 -5
  24. package/src/util/types.js +2 -5
  25. package/src/components/__tests__/search-text-input.test.js +0 -212
  26. package/src/components/action-menu.stories.js +0 -48
  27. package/src/components/multi-select.stories.js +0 -124
  28. package/src/components/search-text-input.js +0 -115
  29. package/src/components/single-select.stories.js +0 -247
@@ -1,115 +0,0 @@
1
- // @flow
2
- // A TextField with a search icon on its left side and X icon on its right side
3
-
4
- import * as React from "react";
5
-
6
- import SearchField from "@khanacademy/wonder-blocks-search-field";
7
- import type {StyleType} from "@khanacademy/wonder-blocks-core";
8
-
9
- import {defaultLabels} from "../util/constants.js";
10
-
11
- type Labels = {|
12
- clearSearch: string,
13
- filter: string,
14
- |};
15
-
16
- type Props = {|
17
- /**
18
- * The object containing the custom labels used inside this component.
19
- */
20
- labels: Labels,
21
-
22
- /**
23
- * the text input
24
- */
25
- searchText: string,
26
-
27
- /**
28
- * Called when the input value is changed
29
- */
30
- onChange: (searchText: string) => mixed,
31
-
32
- /**
33
- * Handler that is triggered when this component is clicked. For example,
34
- * use this to adjust focus in parent component. This gets called when we
35
- * click the dismiss icon button within the SearchTextInput.
36
- */
37
- onClick?: () => mixed,
38
-
39
- /**
40
- * Used to handle the focus order in its parent component. The itemRef is
41
- * applied to the input directly.
42
- */
43
- itemRef?: {|current: any|},
44
-
45
- /**
46
- * Custom styles for the main wrapper
47
- */
48
- style?: StyleType,
49
-
50
- /**
51
- * Test ID used for e2e testing.
52
- */
53
- testId?: string,
54
-
55
- /**
56
- * Automatically focus on this search field on mount.
57
- * TODO(WB-1310): Remove the autofocus prop after making
58
- * the search field sticky in dropdowns.
59
- */
60
- autofocus?: boolean,
61
- |};
62
-
63
- type DefaultProps = {|
64
- labels: $PropertyType<Props, "labels">,
65
- |};
66
-
67
- export default class SearchTextInput extends React.Component<Props> {
68
- static isClassOf(instance: React.Element<any>): boolean {
69
- return (
70
- instance && instance.type && instance.type.__IS_SEARCH_TEXT_INPUT__
71
- );
72
- }
73
-
74
- static defaultProps: DefaultProps = {
75
- labels: {
76
- clearSearch: defaultLabels.clearSearch,
77
- filter: defaultLabels.filter,
78
- },
79
- };
80
-
81
- // TODO(WB-1310): Remove `componentDidMount` autofocus on the search field
82
- // after making the search field sticky.
83
- componentDidMount() {
84
- // We need to re-focus on the text input after it mounts because of
85
- // the case in which the dropdown switches between virtualized and
86
- // non-virtualized. It can rerender the search field as the user is
87
- // typing based on the number of search results, which results
88
- // in losing focus on the field so the user can't type anymore.
89
- // To work around this issue, this temporary fix auto-focuses on the
90
- // search field on mount.
91
- if (this.props.autofocus) {
92
- this.props.itemRef?.current.focus();
93
- }
94
- }
95
-
96
- static __IS_SEARCH_TEXT_INPUT__: boolean = true;
97
-
98
- render(): React.Node {
99
- const {labels, onChange, onClick, itemRef, searchText, style, testId} =
100
- this.props;
101
-
102
- return (
103
- <SearchField
104
- clearAriaLabel={labels.clearSearch}
105
- onChange={onChange}
106
- onClick={onClick}
107
- placeholder={labels.filter}
108
- ref={itemRef}
109
- style={style}
110
- testId={testId}
111
- value={searchText}
112
- />
113
- );
114
- }
115
- }
@@ -1,247 +0,0 @@
1
- // @flow
2
- import * as React from "react";
3
- import {StyleSheet} from "aphrodite";
4
-
5
- import Button from "@khanacademy/wonder-blocks-button";
6
- import {View} from "@khanacademy/wonder-blocks-core";
7
- import {Strut} from "@khanacademy/wonder-blocks-layout";
8
- import {OnePaneDialog, ModalLauncher} from "@khanacademy/wonder-blocks-modal";
9
- import Spacing from "@khanacademy/wonder-blocks-spacing";
10
- import {Body} from "@khanacademy/wonder-blocks-typography";
11
-
12
- import type {StoryComponentType} from "@storybook/react";
13
-
14
- import {SingleSelect, OptionItem} from "../index.js";
15
-
16
- export default {
17
- title: "Dropdown / SingleSelect",
18
- component: SingleSelect,
19
- args: {
20
- isFilterable: true,
21
- opened: true,
22
- disabled: false,
23
- light: false,
24
- placeholder: "Choose a fruit",
25
- },
26
- };
27
-
28
- const SingleSelectTemplate = (args) => <SingleSelect {...args} />;
29
-
30
- export const DefaultSingleSelectOpened: StoryComponentType = (args) => {
31
- const [selectedValue, setSelectedValue] = React.useState("pear");
32
- const [opened, setOpened] = React.useState(args.opened);
33
- React.useEffect(() => {
34
- // Only update opened if the args.opened prop changes (using the
35
- // controls panel).
36
- setOpened(args.opened);
37
- }, [args.opened]);
38
-
39
- return (
40
- <SingleSelectTemplate
41
- {...args}
42
- onChange={setSelectedValue}
43
- selectedValue={selectedValue}
44
- opened={opened}
45
- onToggle={setOpened}
46
- >
47
- <OptionItem label="Banana" value="banana" />
48
- <OptionItem label="Strawberry" value="strawberry" disabled />
49
- <OptionItem label="Pear" value="pear" />
50
- <OptionItem label="Orange" value="orange" />
51
- <OptionItem label="Watermelon" value="watermelon" />
52
- <OptionItem label="Apple" value="apple" />
53
- <OptionItem label="Grape" value="grape" />
54
- <OptionItem label="Lemon" value="lemon" />
55
- <OptionItem label="Mango" value="mango" />
56
- </SingleSelectTemplate>
57
- );
58
- };
59
-
60
- DefaultSingleSelectOpened.parameters = {
61
- docs: {
62
- storyDescription:
63
- "This select starts with a starting selected item. One of the items is disabled and thus cannot be selected.",
64
- },
65
- // Added to ensure that the dropdown menu is rendered using PopperJS.
66
- chromatic: {delay: 400},
67
- };
68
-
69
- const fruits = ["banana", "strawberry", "pear", "orange"];
70
-
71
- const optionItems = new Array(1000)
72
- .fill(null)
73
- .map((_, i) => (
74
- <OptionItem
75
- key={i}
76
- value={(i + 1).toString()}
77
- label={`Fruit # ${i + 1} ${fruits[i % fruits.length]}`}
78
- />
79
- ));
80
-
81
- type Props = {|
82
- selectedValue?: ?string,
83
- opened: boolean,
84
- |};
85
-
86
- type State = {|
87
- selectedValue?: ?string,
88
- opened: boolean,
89
- |};
90
-
91
- type DefaultProps = {|
92
- selectedValue: $PropertyType<Props, "selectedValue">,
93
- opened: $PropertyType<Props, "opened">,
94
- |};
95
-
96
- class SingleSelectWithFilter extends React.Component<Props, State> {
97
- static defaultProps: DefaultProps = {
98
- selectedValue: "2",
99
- opened: false,
100
- };
101
-
102
- state: State = {
103
- selectedValue: this.props.selectedValue,
104
- opened: this.props.opened,
105
- };
106
-
107
- handleChange: (selected: string) => void = (selected) => {
108
- this.setState({
109
- selectedValue: selected,
110
- });
111
- };
112
-
113
- handleToggleMenu: (opened: boolean) => void = (opened) => {
114
- this.setState({
115
- opened,
116
- });
117
- };
118
-
119
- render(): React.Node {
120
- return (
121
- <View style={styles.wrapper}>
122
- <SingleSelect
123
- onChange={this.handleChange}
124
- isFilterable={true}
125
- opened={this.state.opened}
126
- onToggle={this.handleToggleMenu}
127
- placeholder="Select a fruit"
128
- selectedValue={this.state.selectedValue}
129
- dropdownStyle={styles.fullBleed}
130
- style={styles.fullBleed}
131
- >
132
- {optionItems}
133
- </SingleSelect>
134
- </View>
135
- );
136
- }
137
- }
138
-
139
- const styles = StyleSheet.create({
140
- row: {
141
- flexDirection: "row",
142
- },
143
- fullBleed: {
144
- width: "100%",
145
- },
146
- wrapper: {
147
- height: "800px",
148
- width: "600px",
149
- },
150
- centered: {
151
- alignItems: "center",
152
- justifyContent: "center",
153
- height: `calc(100vh - 16px)`,
154
- },
155
- scrollableArea: {
156
- height: "200vh",
157
- },
158
- });
159
-
160
- export const WithFilter: StoryComponentType = () => <SingleSelectWithFilter />;
161
-
162
- WithFilter.parameters = {
163
- chromatic: {
164
- // we don't need screenshots because this story only tests behavior.
165
- disableSnapshot: true,
166
- },
167
- };
168
-
169
- export const WithFilterOpened: StoryComponentType = () => (
170
- <SingleSelectWithFilter opened={true} />
171
- );
172
-
173
- export const WithFilterOpenedNoValueSelected: StoryComponentType = () => (
174
- <SingleSelectWithFilter opened={true} selectedValue={null} />
175
- );
176
-
177
- export const DropdownInModal: StoryComponentType = () => {
178
- const [value, setValue] = React.useState(null);
179
- const [opened, setOpened] = React.useState(true);
180
-
181
- const modalContent = (
182
- <View style={styles.scrollableArea}>
183
- <View>
184
- <Body>
185
- Sometimes we want to include Dropdowns inside a Modal, and
186
- these controls can be accessed only by scrolling down. This
187
- example help us to demonstrate that SingleSelect components
188
- can correctly be displayed within the visible scrolling
189
- area.
190
- </Body>
191
- <Strut size={Spacing.large_24} />
192
- <SingleSelect
193
- onChange={(selected) => setValue(selected)}
194
- isFilterable={true}
195
- opened={opened}
196
- onToggle={(opened) => setOpened(opened)}
197
- placeholder="Select a fruit"
198
- selectedValue={value}
199
- >
200
- {optionItems}
201
- </SingleSelect>
202
- </View>
203
- </View>
204
- );
205
-
206
- const modal = (
207
- <OnePaneDialog title="Dropdown in a Modal" content={modalContent} />
208
- );
209
-
210
- return (
211
- <View style={styles.centered}>
212
- <ModalLauncher modal={modal}>
213
- {({openModal}) => (
214
- <Button onClick={openModal}>Click here!</Button>
215
- )}
216
- </ModalLauncher>
217
- </View>
218
- );
219
- };
220
-
221
- DropdownInModal.storyName = "Dropdown in a modal";
222
-
223
- DropdownInModal.parameters = {
224
- chromatic: {
225
- // We don't need screenshots because this story can be tested after
226
- // the modal is opened.
227
- disableSnapshot: true,
228
- },
229
- };
230
-
231
- export const DisabledSingleSelect: StoryComponentType = () => (
232
- <SingleSelect
233
- disabled={true}
234
- placeholder="Choose a juice"
235
- onChange={() => {}}
236
- >
237
- <OptionItem label="Banana juice" value="banana" />
238
- <OptionItem label="Strawberry juice" value="strawberry" />
239
- </SingleSelect>
240
- );
241
-
242
- DisabledSingleSelect.parameters = {
243
- docs: {
244
- storyDescription:
245
- "`SingleSelect` can be disabled by passing `disabled={true}`. This can be useful when you want to disable a control temporarily.",
246
- },
247
- };