- {titleText && (
-
- )}
-
-
- {
- if (match(event, keys.Space)) {
- event.stopPropagation();
- }
-
- if (match(event, keys.Enter)) {
- toggleMenu();
- }
- },
- })}
- />
- {invalid && (
-
- )}
- {showWarning && (
- {
+ const rootProps = getRootProps(
+ {},
+ {
+ suppressRefError: true,
+ }
+ );
+ const labelProps = getLabelProps();
+ const buttonProps = getToggleButtonProps({
+ disabled,
+ onClick: this.onToggleClick(isOpen),
+ // When we moved the "root node" of Downshift to the for
+ // ARIA 1.2 compliance, we unfortunately hit this branch for the
+ // "mouseup" event that downshift listens to:
+ // https://github.com/downshift-js/downshift/blob/v5.2.1/src/downshift.js#L1051-L1065
+ //
+ // As a result, it will reset the state of the component and so we
+ // stop the event from propagating to prevent this. This allows the
+ // toggleMenu behavior for the toggleButton to correctly open and
+ // close the menu.
+ onMouseUp(event) {
+ event.stopPropagation();
+ },
+ });
+ const inputProps = getInputProps({
+ // Remove excess aria `aria-labelledby`. HTML
+ );
+ }}
);
}
diff --git a/packages/react/src/components/ListBox/next/ListBoxSelection.js b/packages/react/src/components/ListBox/next/ListBoxSelection.js
new file mode 100644
index 000000000000..c234da7f0952
--- /dev/null
+++ b/packages/react/src/components/ListBox/next/ListBoxSelection.js
@@ -0,0 +1,162 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import cx from 'classnames';
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Close16 } from '@carbon/icons-react';
+import { settings } from 'carbon-components';
+import { match, keys } from '../../../internal/keyboard';
+
+const { prefix } = settings;
+
+/**
+ * `ListBoxSelection` is used to provide controls for clearing a selection, in
+ * addition to conditionally rendering a badge if the control has more than one
+ * selection.
+ */
+function ListBoxSelection({
+ clearSelection,
+ selectionCount,
+ translateWithId: t,
+ disabled,
+ onClearSelection,
+}) {
+ const className = cx(`${prefix}--list-box__selection`, {
+ [`${prefix}--tag--filter`]: selectionCount,
+ [`${prefix}--list-box__selection--multi`]: selectionCount,
+ });
+ const description = selectionCount ? t('clear.all') : t('clear.selection');
+ const tagClasses = cx(
+ `${prefix}--tag`,
+ `${prefix}--tag--filter`,
+ `${prefix}--tag--high-contrast`,
+ {
+ [`${prefix}--tag--disabled`]: disabled,
+ }
+ );
+
+ function onClick(event) {
+ event.stopPropagation();
+ if (disabled) {
+ return;
+ }
+ clearSelection(event);
+ if (onClearSelection) {
+ onClearSelection(event);
+ }
+ }
+
+ function onKeyDown(event) {
+ event.stopPropagation();
+ if (disabled) {
+ return;
+ }
+
+ // When a user hits ENTER, we'll clear the selection
+ if (match(event, keys.Enter)) {
+ clearSelection(event);
+ if (onClearSelection) {
+ onClearSelection(event);
+ }
+ }
+ }
+
+ if (selectionCount) {
+ return (
+
+
+ {selectionCount}
+
+
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+export const translationIds = {
+ 'clear.all': 'clear.all',
+ 'clear.selection': 'clear.selection',
+};
+
+const defaultTranslations = {
+ [translationIds['clear.all']]: 'Clear all selected items',
+ [translationIds['clear.selection']]: 'Clear selected item',
+};
+
+ListBoxSelection.propTypes = {
+ /**
+ * Specify a function to be invoked when a user interacts with the clear
+ * selection element.
+ */
+ clearSelection: PropTypes.func.isRequired,
+
+ /**
+ * Specify whether or not the clear selection element should be disabled
+ */
+ disabled: PropTypes.bool,
+
+ /**
+ * Specify an optional `onClearSelection` handler that is called when the underlying
+ * element is cleared
+ */
+ onClearSelection: PropTypes.func,
+
+ /**
+ * Specify an optional `onClick` handler that is called when the underlying
+ * clear selection element is clicked
+ */
+ onClick: PropTypes.func,
+
+ /**
+ * Specify an optional `onKeyDown` handler that is called when the underlying
+ * clear selection element fires a keydown event
+ */
+ onKeyDown: PropTypes.func,
+
+ /**
+ * Specify an optional `selectionCount` value that will be used to determine
+ * whether the selection should display a badge or a single clear icon.
+ */
+ selectionCount: PropTypes.number,
+
+ /**
+ * i18n hook used to provide the appropriate description for the given menu
+ * icon. This function takes in an id defined in `translationIds` and should
+ * return a string message for that given message id.
+ */
+ translateWithId: PropTypes.func.isRequired,
+};
+
+ListBoxSelection.defaultProps = {
+ translateWithId: (id) => defaultTranslations[id],
+};
+
+export default ListBoxSelection;
diff --git a/packages/react/src/components/ListBox/next/ListBoxTrigger.js b/packages/react/src/components/ListBox/next/ListBoxTrigger.js
new file mode 100644
index 000000000000..816db162ebfd
--- /dev/null
+++ b/packages/react/src/components/ListBox/next/ListBoxTrigger.js
@@ -0,0 +1,66 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import cx from 'classnames';
+import React from 'react';
+import PropTypes from 'prop-types';
+import { ChevronDown16 } from '@carbon/icons-react';
+import { settings } from 'carbon-components';
+
+const { prefix } = settings;
+
+export const translationIds = {
+ 'close.menu': 'close.menu',
+ 'open.menu': 'open.menu',
+};
+
+const defaultTranslations = {
+ [translationIds['close.menu']]: 'Close',
+ [translationIds['open.menu']]: 'Open',
+};
+
+/**
+ * `ListBoxTrigger` is used to orient the icon up or down depending on the
+ * state of the menu for a given `ListBox`
+ */
+const ListBoxTrigger = ({ isOpen, translateWithId: t, ...rest }) => {
+ const className = cx(`${prefix}--list-box__menu-icon`, {
+ [`${prefix}--list-box__menu-icon--open`]: isOpen,
+ });
+ const description = isOpen ? t('close.menu') : t('open.menu');
+ return (
+
+ );
+};
+
+ListBoxTrigger.propTypes = {
+ /**
+ * Specify whether the menu is currently open, which will influence the
+ * direction of the menu icon
+ */
+ isOpen: PropTypes.bool.isRequired,
+
+ /**
+ * i18n hook used to provide the appropriate description for the given menu
+ * icon. This function takes in an id defined in `translationIds` and should
+ * return a string message for that given message id.
+ */
+ translateWithId: PropTypes.func.isRequired,
+};
+
+ListBoxTrigger.defaultProps = {
+ translateWithId: (id) => defaultTranslations[id],
+};
+
+export default ListBoxTrigger;
diff --git a/packages/react/src/components/ListBox/next/index.js b/packages/react/src/components/ListBox/next/index.js
new file mode 100644
index 000000000000..fca0b5be6228
--- /dev/null
+++ b/packages/react/src/components/ListBox/next/index.js
@@ -0,0 +1,9 @@
+/**
+ * Copyright IBM Corp. 2016, 2018
+ *
+ * This source code is licensed under the Apache-2.0 license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+export { default as ListBoxSelection } from './ListBoxSelection';
+export { default as ListBoxTrigger } from './ListBoxTrigger';