@khanacademy/wonder-blocks-dropdown 5.3.8 → 5.4.0
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/CHANGELOG.md +38 -0
- package/dist/components/action-menu.d.ts +15 -2
- package/dist/components/dropdown-core.d.ts +4 -0
- package/dist/components/dropdown-opener.d.ts +4 -0
- package/dist/components/multi-select.d.ts +9 -2
- package/dist/components/option-item.d.ts +1 -1
- package/dist/components/single-select.d.ts +9 -2
- package/dist/es/index.js +114 -61
- package/dist/hooks/use-listbox.d.ts +2 -2
- package/dist/index.js +113 -60
- package/package.json +6 -6
- package/src/components/__tests__/action-menu.test.tsx +630 -23
- package/src/components/__tests__/multi-select.test.tsx +293 -0
- package/src/components/__tests__/single-select.test.tsx +306 -0
- package/src/components/action-menu.tsx +85 -48
- package/src/components/dropdown-core.tsx +9 -2
- package/src/components/dropdown-opener.tsx +17 -1
- package/src/components/multi-select.tsx +94 -61
- package/src/components/option-item.tsx +1 -1
- package/src/components/select-opener.tsx +2 -2
- package/src/components/single-select.tsx +87 -57
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-dropdown
|
|
2
2
|
|
|
3
|
+
## 5.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4e82c4c2: Improves support for providing ids to the opener and dropdown elements. These ids are auto-generated if they are not provided.
|
|
8
|
+
|
|
9
|
+
Also applies attributes to elements automatically for improved accessibility (`aria-controls`, `aria-haspopup`, `aria-expanded`).
|
|
10
|
+
|
|
11
|
+
- `ActionMenu`
|
|
12
|
+
- Adds new `dropdownId` and `id` props. If these are not provided, these ids will be generated automatically
|
|
13
|
+
- `aria-controls` is set to the dropdown id for both default and custom openers
|
|
14
|
+
- Ensure `aria-haspopup` and `aria-expanded` attributes are set on both default and custom openers
|
|
15
|
+
- `SingleSelect` and `MultiSelect`
|
|
16
|
+
- Adds new `dropdownId` prop. If this is not provided, an id will be generated automatically
|
|
17
|
+
- If the `id` prop is not provided, an id for the component is now generated automatically
|
|
18
|
+
- `aria-controls` is set to the dropdown id for both default and custom openers
|
|
19
|
+
- Ensure `id`, `aria-haspopup` and `aria-expanded` attributes are set on both default and custom openers
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- f099cf87: Improves accessibility of the checked status on `OptionItem` components used
|
|
24
|
+
within the `ActionMenu` component. The checked status is communicated to
|
|
25
|
+
screenreaders by using a `menuitemcheckbox` role with the `aria-checked`
|
|
26
|
+
attribute (instead of `aria-selected`). - `CellCore` (used by `CompactCell` and `DetailCell`) has a new optional
|
|
27
|
+
prop for `aria-checked` - `ClickableRole` type now supports the `menuitemcheckbox` role - `OptionItem`'s `role` prop now also supports the `menuitemcheckbox` role
|
|
28
|
+
- Updated dependencies [f099cf87]
|
|
29
|
+
- @khanacademy/wonder-blocks-clickable@4.2.3
|
|
30
|
+
- @khanacademy/wonder-blocks-cell@3.4.0
|
|
31
|
+
- @khanacademy/wonder-blocks-search-field@2.2.18
|
|
32
|
+
- @khanacademy/wonder-blocks-modal@5.1.6
|
|
33
|
+
|
|
34
|
+
## 5.3.9
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- c8b273f0: Update default/resting border color to fix a color contrast issue
|
|
39
|
+
- @khanacademy/wonder-blocks-search-field@2.2.17
|
|
40
|
+
|
|
3
41
|
## 5.3.8
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type AriaProps, type StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import DropdownOpener from "./dropdown-opener";
|
|
4
4
|
import type { Item, DropdownItem, OpenerProps } from "../util/types";
|
|
5
5
|
type Props = AriaProps & Readonly<{
|
|
@@ -63,6 +63,19 @@ type Props = AriaProps & Readonly<{
|
|
|
63
63
|
* element to access pointer event state.
|
|
64
64
|
*/
|
|
65
65
|
opener?: (openerProps: OpenerProps) => React.ReactElement<any>;
|
|
66
|
+
/**
|
|
67
|
+
* Unique identifier attached to the menu dropdown. If used, we need to
|
|
68
|
+
* guarantee that the ID is unique within everything rendered on a page.
|
|
69
|
+
* If one is not provided, one is auto-generated. It is used for the
|
|
70
|
+
* opener's `aria-controls` attribute for screenreaders.
|
|
71
|
+
*/
|
|
72
|
+
dropdownId?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Unique identifier attached to the field control. If used, we need to
|
|
75
|
+
* guarantee that the ID is unique within everything rendered on a page.
|
|
76
|
+
* If one is not provided, one is auto-generated.
|
|
77
|
+
*/
|
|
78
|
+
id?: string;
|
|
66
79
|
}>;
|
|
67
80
|
type State = Readonly<{
|
|
68
81
|
/**
|
|
@@ -103,7 +116,7 @@ export default class ActionMenu extends React.Component<Props, State> {
|
|
|
103
116
|
getMenuItems(): Array<DropdownItem>;
|
|
104
117
|
handleOpenerRef: (node?: any) => void;
|
|
105
118
|
handleClick: (e: React.SyntheticEvent) => void;
|
|
106
|
-
renderOpener(numItems: number): React.ReactElement<React.ComponentProps<typeof DropdownOpener>>;
|
|
119
|
+
renderOpener(numItems: number, dropdownId: string): React.ReactElement<React.ComponentProps<typeof DropdownOpener>>;
|
|
107
120
|
render(): React.ReactNode;
|
|
108
121
|
}
|
|
109
122
|
export {};
|
|
@@ -81,6 +81,10 @@ declare const _default: React.ComponentType<Readonly<{
|
|
|
81
81
|
* Whether the dropdown and it's interactions should be disabled.
|
|
82
82
|
*/
|
|
83
83
|
disabled?: boolean | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Unique identifier attached to the dropdown.
|
|
86
|
+
*/
|
|
87
|
+
id?: string | undefined;
|
|
84
88
|
/**
|
|
85
89
|
* Whether this menu should be left-aligned or right-aligned with the
|
|
86
90
|
* opener component. Defaults to left-aligned.
|
|
@@ -31,6 +31,10 @@ type Props = Partial<Omit<AriaProps, "aria-disabled">> & {
|
|
|
31
31
|
* Whether the dropdown is opened.
|
|
32
32
|
*/
|
|
33
33
|
opened: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The unique identifier for the opener.
|
|
36
|
+
*/
|
|
37
|
+
id?: string;
|
|
34
38
|
};
|
|
35
39
|
type DefaultProps = {
|
|
36
40
|
disabled: Props["disabled"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type AriaProps, type StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import DropdownOpener from "./dropdown-opener";
|
|
4
4
|
import SelectOpener from "./select-opener";
|
|
5
5
|
import OptionItem from "./option-item";
|
|
@@ -129,6 +129,13 @@ type Props = AriaProps & DefaultProps & Readonly<{
|
|
|
129
129
|
* Test ID used for e2e testing.
|
|
130
130
|
*/
|
|
131
131
|
testId?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Unique identifier attached to the listbox dropdown. If used, we need to
|
|
134
|
+
* guarantee that the ID is unique within everything rendered on a page.
|
|
135
|
+
* If one is not provided, one is auto-generated. It is used for the
|
|
136
|
+
* opener's `aria-controls` attribute for screenreaders.
|
|
137
|
+
*/
|
|
138
|
+
dropdownId?: string;
|
|
132
139
|
}>;
|
|
133
140
|
type State = Readonly<{
|
|
134
141
|
/**
|
|
@@ -196,7 +203,7 @@ export default class MultiSelect extends React.Component<Props, State> {
|
|
|
196
203
|
handleOpenerRef: (node?: any) => void;
|
|
197
204
|
handleSearchTextChanged: (searchText: string) => void;
|
|
198
205
|
handleClick: (e: React.SyntheticEvent) => void;
|
|
199
|
-
renderOpener(allChildren: React.ReactElement<React.ComponentProps<typeof OptionItem>>[], isDisabled: boolean): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
|
|
206
|
+
renderOpener(allChildren: React.ReactElement<React.ComponentProps<typeof OptionItem>>[], isDisabled: boolean, dropdownId: string): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
|
|
200
207
|
render(): React.ReactNode;
|
|
201
208
|
}
|
|
202
209
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type AriaProps, type StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import DropdownOpener from "./dropdown-opener";
|
|
4
4
|
import SelectOpener from "./select-opener";
|
|
5
5
|
import OptionItem from "./option-item";
|
|
@@ -125,6 +125,13 @@ type Props = AriaProps & DefaultProps & Readonly<{
|
|
|
125
125
|
* top. The items will be filtered by the input.
|
|
126
126
|
*/
|
|
127
127
|
isFilterable?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Unique identifier attached to the listbox dropdown. If used, we need to
|
|
130
|
+
* guarantee that the ID is unique within everything rendered on a page.
|
|
131
|
+
* If one is not provided, one is auto-generated. It is used for the
|
|
132
|
+
* opener's `aria-controls` attribute for screenreaders.
|
|
133
|
+
*/
|
|
134
|
+
dropdownId?: string;
|
|
128
135
|
}>;
|
|
129
136
|
type State = Readonly<{
|
|
130
137
|
/**
|
|
@@ -204,7 +211,7 @@ export default class SingleSelect extends React.Component<Props, State> {
|
|
|
204
211
|
handleSearchTextChanged: (searchText: string) => void;
|
|
205
212
|
handleOpenerRef: (node?: any) => void;
|
|
206
213
|
handleClick: (e: React.SyntheticEvent) => void;
|
|
207
|
-
renderOpener(isDisabled: boolean): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
|
|
214
|
+
renderOpener(isDisabled: boolean, dropdownId: string): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
|
|
208
215
|
render(): React.ReactNode;
|
|
209
216
|
}
|
|
210
217
|
export {};
|
package/dist/es/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { CompactCell, DetailCell } from '@khanacademy/wonder-blocks-cell';
|
|
|
4
4
|
import * as tokens from '@khanacademy/wonder-blocks-tokens';
|
|
5
5
|
import { spacing, color, mix, fade } from '@khanacademy/wonder-blocks-tokens';
|
|
6
6
|
import { LabelMedium, LabelSmall, LabelLarge } from '@khanacademy/wonder-blocks-typography';
|
|
7
|
-
import { View, addStyle, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
|
|
7
|
+
import { View, addStyle, IDProvider, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
|
|
8
8
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
9
9
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
10
10
|
import checkIcon from '@phosphor-icons/core/bold/check-bold.svg';
|
|
@@ -492,7 +492,10 @@ class DropdownOpener extends React.Component {
|
|
|
492
492
|
disabled,
|
|
493
493
|
testId,
|
|
494
494
|
text,
|
|
495
|
-
opened
|
|
495
|
+
opened,
|
|
496
|
+
"aria-controls": ariaControls,
|
|
497
|
+
"aria-haspopup": ariaHasPopUp,
|
|
498
|
+
id
|
|
496
499
|
} = this.props;
|
|
497
500
|
const renderedChildren = this.props.children(_extends({}, eventState, {
|
|
498
501
|
text,
|
|
@@ -502,6 +505,10 @@ class DropdownOpener extends React.Component {
|
|
|
502
505
|
const childrenTestId = this.getTestIdFromProps(childrenProps);
|
|
503
506
|
return React.cloneElement(renderedChildren, _extends({}, clickableChildrenProps, {
|
|
504
507
|
disabled,
|
|
508
|
+
"aria-controls": ariaControls,
|
|
509
|
+
id,
|
|
510
|
+
"aria-expanded": opened ? "true" : "false",
|
|
511
|
+
"aria-haspopup": ariaHasPopUp,
|
|
505
512
|
onClick: childrenProps.onClick ? e => {
|
|
506
513
|
childrenProps.onClick(e);
|
|
507
514
|
clickableChildrenProps.onClick(e);
|
|
@@ -1169,7 +1176,7 @@ class DropdownCore extends React.Component {
|
|
|
1169
1176
|
this.handleItemClick(focusIndex, item);
|
|
1170
1177
|
},
|
|
1171
1178
|
ref: focusable ? currentRef : null,
|
|
1172
|
-
role: itemRole
|
|
1179
|
+
role: populatedProps.role || itemRole
|
|
1173
1180
|
}));
|
|
1174
1181
|
});
|
|
1175
1182
|
}
|
|
@@ -1177,12 +1184,15 @@ class DropdownCore extends React.Component {
|
|
|
1177
1184
|
let focusCounter = 0;
|
|
1178
1185
|
const itemRole = this.getItemRole();
|
|
1179
1186
|
return this.props.items.map((item, index) => {
|
|
1187
|
+
const {
|
|
1188
|
+
populatedProps
|
|
1189
|
+
} = item;
|
|
1180
1190
|
if (!SeparatorItem.isClassOf(item.component) && item.focusable) {
|
|
1181
1191
|
focusCounter += 1;
|
|
1182
1192
|
}
|
|
1183
1193
|
const focusIndex = focusCounter - 1;
|
|
1184
1194
|
return _extends({}, item, {
|
|
1185
|
-
role: itemRole,
|
|
1195
|
+
role: populatedProps.role || itemRole,
|
|
1186
1196
|
ref: item.focusable ? this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null : null,
|
|
1187
1197
|
onClick: () => {
|
|
1188
1198
|
this.handleItemClick(focusIndex, item);
|
|
@@ -1221,7 +1231,8 @@ class DropdownCore extends React.Component {
|
|
|
1221
1231
|
isFilterable,
|
|
1222
1232
|
light,
|
|
1223
1233
|
openerElement,
|
|
1224
|
-
role
|
|
1234
|
+
role,
|
|
1235
|
+
id
|
|
1225
1236
|
} = this.props;
|
|
1226
1237
|
const openerStyle = openerElement && window.getComputedStyle(openerElement);
|
|
1227
1238
|
const minDropdownWidth = openerStyle ? openerStyle.getPropertyValue("width") : 0;
|
|
@@ -1230,6 +1241,7 @@ class DropdownCore extends React.Component {
|
|
|
1230
1241
|
style: [styles$4.dropdown, light && styles$4.light, isReferenceHidden && styles$4.hidden, dropdownStyle],
|
|
1231
1242
|
testId: "dropdown-core-container"
|
|
1232
1243
|
}, isFilterable && this.renderSearchField(), React.createElement(View, {
|
|
1244
|
+
id: id,
|
|
1233
1245
|
role: role,
|
|
1234
1246
|
style: [styles$4.listboxOrMenu, {
|
|
1235
1247
|
minWidth: minDropdownWidth
|
|
@@ -1547,11 +1559,15 @@ class ActionMenu extends React.Component {
|
|
|
1547
1559
|
}
|
|
1548
1560
|
});
|
|
1549
1561
|
} else if (OptionItem.isClassOf(item)) {
|
|
1562
|
+
const selected = selectedValues ? selectedValues.includes(value) : false;
|
|
1550
1563
|
return _extends({}, itemObject, {
|
|
1551
1564
|
populatedProps: {
|
|
1552
1565
|
onToggle: this.handleOptionSelected,
|
|
1553
|
-
selected
|
|
1554
|
-
variant: "check"
|
|
1566
|
+
selected,
|
|
1567
|
+
variant: "check",
|
|
1568
|
+
role: "menuitemcheckbox",
|
|
1569
|
+
"aria-checked": selected,
|
|
1570
|
+
"aria-selected": undefined
|
|
1555
1571
|
}
|
|
1556
1572
|
});
|
|
1557
1573
|
} else {
|
|
@@ -1559,14 +1575,21 @@ class ActionMenu extends React.Component {
|
|
|
1559
1575
|
}
|
|
1560
1576
|
});
|
|
1561
1577
|
}
|
|
1562
|
-
renderOpener(numItems) {
|
|
1578
|
+
renderOpener(numItems, dropdownId) {
|
|
1563
1579
|
const {
|
|
1564
1580
|
disabled,
|
|
1565
1581
|
menuText,
|
|
1566
1582
|
opener,
|
|
1567
|
-
testId
|
|
1583
|
+
testId,
|
|
1584
|
+
id
|
|
1568
1585
|
} = this.props;
|
|
1569
|
-
return React.createElement(
|
|
1586
|
+
return React.createElement(IDProvider, {
|
|
1587
|
+
id: id,
|
|
1588
|
+
scope: "action-menu-opener"
|
|
1589
|
+
}, uniqueOpenerId => React.createElement(DropdownOpener, {
|
|
1590
|
+
id: uniqueOpenerId,
|
|
1591
|
+
"aria-controls": dropdownId,
|
|
1592
|
+
"aria-haspopup": "menu",
|
|
1570
1593
|
onClick: this.handleClick,
|
|
1571
1594
|
disabled: numItems === 0 || disabled,
|
|
1572
1595
|
text: menuText,
|
|
@@ -1583,29 +1606,33 @@ class ActionMenu extends React.Component {
|
|
|
1583
1606
|
opened: !!opened,
|
|
1584
1607
|
testId: testId
|
|
1585
1608
|
}), menuText);
|
|
1586
|
-
});
|
|
1609
|
+
}));
|
|
1587
1610
|
}
|
|
1588
1611
|
render() {
|
|
1589
1612
|
const {
|
|
1590
1613
|
alignment,
|
|
1591
1614
|
dropdownStyle,
|
|
1592
1615
|
style,
|
|
1593
|
-
className
|
|
1616
|
+
className,
|
|
1617
|
+
dropdownId
|
|
1594
1618
|
} = this.props;
|
|
1595
1619
|
const items = this.getMenuItems();
|
|
1596
|
-
|
|
1597
|
-
|
|
1620
|
+
return React.createElement(IDProvider, {
|
|
1621
|
+
id: dropdownId,
|
|
1622
|
+
scope: "action-menu-dropdown"
|
|
1623
|
+
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
1624
|
+
id: uniqueDropdownId,
|
|
1598
1625
|
role: "menu",
|
|
1599
1626
|
style: style,
|
|
1600
1627
|
className: className,
|
|
1601
|
-
opener:
|
|
1628
|
+
opener: this.renderOpener(items.length, uniqueDropdownId),
|
|
1602
1629
|
alignment: alignment,
|
|
1603
1630
|
open: this.state.opened,
|
|
1604
1631
|
items: items,
|
|
1605
1632
|
openerElement: this.openerElement,
|
|
1606
1633
|
onOpenChanged: this.handleOpenChanged,
|
|
1607
1634
|
dropdownStyle: [styles$2.menuTopSpace, dropdownStyle]
|
|
1608
|
-
});
|
|
1635
|
+
}));
|
|
1609
1636
|
}
|
|
1610
1637
|
}
|
|
1611
1638
|
ActionMenu.defaultProps = {
|
|
@@ -1804,13 +1831,13 @@ const _generateStyles = (light, placeholder, error) => {
|
|
|
1804
1831
|
newStyles = {
|
|
1805
1832
|
default: {
|
|
1806
1833
|
background: error ? tokens.color.fadedRed8 : tokens.color.white,
|
|
1807
|
-
borderColor: error ? tokens.color.red : tokens.color.
|
|
1834
|
+
borderColor: error ? tokens.color.red : tokens.color.offBlack50,
|
|
1808
1835
|
borderWidth: tokens.border.width.hairline,
|
|
1809
1836
|
color: placeholder ? tokens.color.offBlack64 : tokens.color.offBlack,
|
|
1810
1837
|
":hover:not([aria-disabled=true])": focusHoverStyling,
|
|
1811
1838
|
["@media not (hover: hover)"]: {
|
|
1812
1839
|
":hover:not([aria-disabled=true])": {
|
|
1813
|
-
borderColor: error ? tokens.color.red : tokens.color.
|
|
1840
|
+
borderColor: error ? tokens.color.red : tokens.color.offBlack50,
|
|
1814
1841
|
borderWidth: tokens.border.width.hairline,
|
|
1815
1842
|
paddingLeft: tokens.spacing.medium_16,
|
|
1816
1843
|
paddingRight: tokens.spacing.small_12
|
|
@@ -1932,7 +1959,7 @@ class SingleSelect extends React.Component {
|
|
|
1932
1959
|
} = this.props;
|
|
1933
1960
|
return this.mapOptionItemsToDropdownItems(isFilterable ? this.filterChildren(children) : children);
|
|
1934
1961
|
}
|
|
1935
|
-
renderOpener(isDisabled) {
|
|
1962
|
+
renderOpener(isDisabled, dropdownId) {
|
|
1936
1963
|
const _this$props = this.props,
|
|
1937
1964
|
{
|
|
1938
1965
|
children,
|
|
@@ -1948,23 +1975,32 @@ class SingleSelect extends React.Component {
|
|
|
1948
1975
|
const items = React.Children.toArray(children);
|
|
1949
1976
|
const selectedItem = items.find(option => option.props.value === selectedValue);
|
|
1950
1977
|
const menuText = selectedItem ? getLabel(selectedItem.props) : placeholder;
|
|
1951
|
-
const dropdownOpener =
|
|
1952
|
-
onClick: this.handleClick,
|
|
1953
|
-
disabled: isDisabled,
|
|
1954
|
-
ref: this.handleOpenerRef,
|
|
1955
|
-
text: menuText,
|
|
1956
|
-
opened: this.state.open
|
|
1957
|
-
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
1958
|
-
disabled: isDisabled,
|
|
1978
|
+
const dropdownOpener = React.createElement(IDProvider, {
|
|
1959
1979
|
id: id,
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1980
|
+
scope: "single-select-opener"
|
|
1981
|
+
}, uniqueOpenerId => {
|
|
1982
|
+
return opener ? React.createElement(DropdownOpener, {
|
|
1983
|
+
id: uniqueOpenerId,
|
|
1984
|
+
"aria-controls": dropdownId,
|
|
1985
|
+
"aria-haspopup": "listbox",
|
|
1986
|
+
onClick: this.handleClick,
|
|
1987
|
+
disabled: isDisabled,
|
|
1988
|
+
ref: this.handleOpenerRef,
|
|
1989
|
+
text: menuText,
|
|
1990
|
+
opened: this.state.open
|
|
1991
|
+
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
1992
|
+
"aria-controls": dropdownId,
|
|
1993
|
+
disabled: isDisabled,
|
|
1994
|
+
id: uniqueOpenerId,
|
|
1995
|
+
error: error,
|
|
1996
|
+
isPlaceholder: !selectedItem,
|
|
1997
|
+
light: light,
|
|
1998
|
+
onOpenChanged: this.handleOpenChanged,
|
|
1999
|
+
open: this.state.open,
|
|
2000
|
+
ref: this.handleOpenerRef,
|
|
2001
|
+
testId: testId
|
|
2002
|
+
}), menuText);
|
|
2003
|
+
});
|
|
1968
2004
|
return dropdownOpener;
|
|
1969
2005
|
}
|
|
1970
2006
|
render() {
|
|
@@ -1981,7 +2017,8 @@ class SingleSelect extends React.Component {
|
|
|
1981
2017
|
style,
|
|
1982
2018
|
"aria-invalid": ariaInvalid,
|
|
1983
2019
|
"aria-required": ariaRequired,
|
|
1984
|
-
disabled
|
|
2020
|
+
disabled,
|
|
2021
|
+
dropdownId
|
|
1985
2022
|
} = this.props;
|
|
1986
2023
|
const {
|
|
1987
2024
|
searchText
|
|
@@ -1990,8 +2027,11 @@ class SingleSelect extends React.Component {
|
|
|
1990
2027
|
const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
|
|
1991
2028
|
const items = this.getMenuItems(allChildren);
|
|
1992
2029
|
const isDisabled = numEnabledOptions === 0 || disabled;
|
|
1993
|
-
|
|
1994
|
-
|
|
2030
|
+
return React.createElement(IDProvider, {
|
|
2031
|
+
id: dropdownId,
|
|
2032
|
+
scope: "single-select-dropdown"
|
|
2033
|
+
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
2034
|
+
id: uniqueDropdownId,
|
|
1995
2035
|
role: "listbox",
|
|
1996
2036
|
selectionType: "single",
|
|
1997
2037
|
alignment: alignment,
|
|
@@ -2003,7 +2043,7 @@ class SingleSelect extends React.Component {
|
|
|
2003
2043
|
light: light,
|
|
2004
2044
|
onOpenChanged: this.handleOpenChanged,
|
|
2005
2045
|
open: this.state.open,
|
|
2006
|
-
opener:
|
|
2046
|
+
opener: this.renderOpener(isDisabled, uniqueDropdownId),
|
|
2007
2047
|
openerElement: this.state.openerElement,
|
|
2008
2048
|
style: style,
|
|
2009
2049
|
className: className,
|
|
@@ -2014,7 +2054,7 @@ class SingleSelect extends React.Component {
|
|
|
2014
2054
|
"aria-invalid": ariaInvalid,
|
|
2015
2055
|
"aria-required": ariaRequired,
|
|
2016
2056
|
disabled: isDisabled
|
|
2017
|
-
});
|
|
2057
|
+
}));
|
|
2018
2058
|
}
|
|
2019
2059
|
}
|
|
2020
2060
|
SingleSelect.defaultProps = {
|
|
@@ -2239,7 +2279,7 @@ class MultiSelect extends React.Component {
|
|
|
2239
2279
|
}
|
|
2240
2280
|
return [...lastSelectedItems, ...restOfTheChildren.map(this.mapOptionItemToDropdownItem)];
|
|
2241
2281
|
}
|
|
2242
|
-
renderOpener(allChildren, isDisabled) {
|
|
2282
|
+
renderOpener(allChildren, isDisabled, dropdownId) {
|
|
2243
2283
|
const _this$props = this.props,
|
|
2244
2284
|
{
|
|
2245
2285
|
id,
|
|
@@ -2252,22 +2292,31 @@ class MultiSelect extends React.Component {
|
|
|
2252
2292
|
noneSelected
|
|
2253
2293
|
} = this.state.labels;
|
|
2254
2294
|
const menuText = this.getMenuText(allChildren);
|
|
2255
|
-
const dropdownOpener =
|
|
2256
|
-
onClick: this.handleClick,
|
|
2257
|
-
disabled: isDisabled,
|
|
2258
|
-
ref: this.handleOpenerRef,
|
|
2259
|
-
text: menuText,
|
|
2260
|
-
opened: this.state.open
|
|
2261
|
-
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
2262
|
-
disabled: isDisabled,
|
|
2295
|
+
const dropdownOpener = React.createElement(IDProvider, {
|
|
2263
2296
|
id: id,
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2297
|
+
scope: "multi-select-opener"
|
|
2298
|
+
}, uniqueOpenerId => {
|
|
2299
|
+
return opener ? React.createElement(DropdownOpener, {
|
|
2300
|
+
id: uniqueOpenerId,
|
|
2301
|
+
"aria-controls": dropdownId,
|
|
2302
|
+
"aria-haspopup": "listbox",
|
|
2303
|
+
onClick: this.handleClick,
|
|
2304
|
+
disabled: isDisabled,
|
|
2305
|
+
ref: this.handleOpenerRef,
|
|
2306
|
+
text: menuText,
|
|
2307
|
+
opened: this.state.open
|
|
2308
|
+
}, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
|
|
2309
|
+
disabled: isDisabled,
|
|
2310
|
+
id: uniqueOpenerId,
|
|
2311
|
+
"aria-controls": dropdownId,
|
|
2312
|
+
isPlaceholder: menuText === noneSelected,
|
|
2313
|
+
light: light,
|
|
2314
|
+
onOpenChanged: this.handleOpenChanged,
|
|
2315
|
+
open: this.state.open,
|
|
2316
|
+
ref: this.handleOpenerRef,
|
|
2317
|
+
testId: testId
|
|
2318
|
+
}), menuText);
|
|
2319
|
+
});
|
|
2271
2320
|
return dropdownOpener;
|
|
2272
2321
|
}
|
|
2273
2322
|
render() {
|
|
@@ -2281,7 +2330,8 @@ class MultiSelect extends React.Component {
|
|
|
2281
2330
|
isFilterable,
|
|
2282
2331
|
"aria-invalid": ariaInvalid,
|
|
2283
2332
|
"aria-required": ariaRequired,
|
|
2284
|
-
disabled
|
|
2333
|
+
disabled,
|
|
2334
|
+
dropdownId
|
|
2285
2335
|
} = this.props;
|
|
2286
2336
|
const {
|
|
2287
2337
|
open,
|
|
@@ -2297,8 +2347,11 @@ class MultiSelect extends React.Component {
|
|
|
2297
2347
|
const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
|
|
2298
2348
|
const filteredItems = this.getMenuItems(allChildren);
|
|
2299
2349
|
const isDisabled = numEnabledOptions === 0 || disabled;
|
|
2300
|
-
|
|
2301
|
-
|
|
2350
|
+
return React.createElement(IDProvider, {
|
|
2351
|
+
id: dropdownId,
|
|
2352
|
+
scope: "multi-select-dropdown"
|
|
2353
|
+
}, uniqueDropdownId => React.createElement(DropdownCore$1, {
|
|
2354
|
+
id: uniqueDropdownId,
|
|
2302
2355
|
role: "listbox",
|
|
2303
2356
|
alignment: alignment,
|
|
2304
2357
|
dropdownStyle: [isFilterable && filterableDropdownStyle, selectDropdownStyle, dropdownStyle],
|
|
@@ -2307,7 +2360,7 @@ class MultiSelect extends React.Component {
|
|
|
2307
2360
|
light: light,
|
|
2308
2361
|
onOpenChanged: this.handleOpenChanged,
|
|
2309
2362
|
open: open,
|
|
2310
|
-
opener:
|
|
2363
|
+
opener: this.renderOpener(allChildren, isDisabled, uniqueDropdownId),
|
|
2311
2364
|
openerElement: this.state.openerElement,
|
|
2312
2365
|
selectionType: "multi",
|
|
2313
2366
|
style: style,
|
|
@@ -2323,7 +2376,7 @@ class MultiSelect extends React.Component {
|
|
|
2323
2376
|
"aria-invalid": ariaInvalid,
|
|
2324
2377
|
"aria-required": ariaRequired,
|
|
2325
2378
|
disabled: isDisabled
|
|
2326
|
-
});
|
|
2379
|
+
}));
|
|
2327
2380
|
}
|
|
2328
2381
|
}
|
|
2329
2382
|
MultiSelect.defaultProps = {
|
|
@@ -45,7 +45,7 @@ export declare function useListbox({ children: options, disabled, id, selectionT
|
|
|
45
45
|
onToggle: (value: string) => unknown;
|
|
46
46
|
selected: boolean;
|
|
47
47
|
focused: boolean;
|
|
48
|
-
role: "menuitem" | "option";
|
|
48
|
+
role: "menuitem" | "option" | "menuitemcheckbox";
|
|
49
49
|
testId?: string | undefined;
|
|
50
50
|
variant?: "checkbox" | "check" | undefined;
|
|
51
51
|
style?: import("@khanacademy/wonder-blocks-core").StyleType;
|
|
@@ -58,7 +58,7 @@ export declare function useListbox({ children: options, disabled, id, selectionT
|
|
|
58
58
|
subtitle2?: import("../../../wonder-blocks-cell/src/util/types").TypographyText | undefined;
|
|
59
59
|
}, "style" | "label" | "id" | "value" | "onClick" | keyof import("../../../wonder-blocks-core/src/util/aria-types").AriaAttributes | "testId" | "leftAccessory" | "rightAccessory" | "labelAsText" | "variant" | "parentComponent" | "subtitle1" | "subtitle2"> & {
|
|
60
60
|
disabled?: boolean | undefined;
|
|
61
|
-
role?: "menuitem" | "option" | undefined;
|
|
61
|
+
role?: "menuitem" | "option" | "menuitemcheckbox" | undefined;
|
|
62
62
|
selected?: boolean | undefined;
|
|
63
63
|
onToggle?: ((value: string) => unknown) | undefined;
|
|
64
64
|
focused?: boolean | undefined;
|