From ca3e59291b0215551f02270918e6025a6002d2f6 Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 9 Feb 2021 14:36:59 -0600 Subject: [PATCH 1/9] fix(ListBoxMenuItem): add title tooltip only when content overflows --- .../react/src/components/Dropdown/Dropdown.js | 12 ++++++++++-- .../src/components/ListBox/ListBoxMenuItem.js | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/react/src/components/Dropdown/Dropdown.js b/packages/react/src/components/Dropdown/Dropdown.js index 1647967d0994..957ab692cce5 100644 --- a/packages/react/src/components/Dropdown/Dropdown.js +++ b/packages/react/src/components/Dropdown/Dropdown.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useRef } from 'react'; import { useSelect } from 'downshift'; import { settings } from 'carbon-components'; import cx from 'classnames'; @@ -130,6 +130,8 @@ const Dropdown = React.forwardRef(function Dropdown( } } + const menuItemOptionRefs = useRef(items.map((_) => React.createRef())); + return (
{titleText && ( @@ -172,6 +174,9 @@ const Dropdown = React.forwardRef(function Dropdown( {isOpen && items.map((item, index) => { const itemProps = getItemProps({ item, index }); + const title = itemToElement ? item.text : itemToString(item); + const { offsetWidth, scrollWidth } = + menuItemOptionRefs?.current[index]?.current || {}; return ( {itemToElement ? ( diff --git a/packages/react/src/components/ListBox/ListBoxMenuItem.js b/packages/react/src/components/ListBox/ListBoxMenuItem.js index b94160b2d46d..8181941a598d 100644 --- a/packages/react/src/components/ListBox/ListBoxMenuItem.js +++ b/packages/react/src/components/ListBox/ListBoxMenuItem.js @@ -17,22 +17,31 @@ const { prefix } = settings; * name, alongside any classes for any corresponding states, for a generic list * box menu item. */ -const ListBoxMenuItem = ({ children, isActive, isHighlighted, ...rest }) => { +const ListBoxMenuItem = React.forwardRef(function ListBoxMenuItem( + { children, isActive, isHighlighted, ...rest }, + ref +) { const className = cx({ [`${prefix}--list-box__menu-item`]: true, [`${prefix}--list-box__menu-item--active`]: isActive, [`${prefix}--list-box__menu-item--highlighted`]: isHighlighted, }); + const { menuItemRef, menuItemOptionRef } = ref || {}; return ( -
-
{children}
+
+
+ {children} +
); -}; +}); +ListBoxMenuItem.displayName = 'ListBoxMenuItem'; ListBoxMenuItem.propTypes = { /** - * Specify any children nodes that hsould be rendered inside of the ListBox + * Specify any children nodes that should be rendered inside of the ListBox * Menu Item */ children: PropTypes.node, From 8b2171a17a5887321475d5f435e2eaafa22cf242 Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 9 Feb 2021 14:37:43 -0600 Subject: [PATCH 2/9] docs(ListBoxMenu): update children proptype --- packages/react/src/components/ListBox/ListBoxMenu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react/src/components/ListBox/ListBoxMenu.js b/packages/react/src/components/ListBox/ListBoxMenu.js index 97c1137eb1ac..019ffca2cee7 100644 --- a/packages/react/src/components/ListBox/ListBoxMenu.js +++ b/packages/react/src/components/ListBox/ListBoxMenu.js @@ -39,6 +39,7 @@ ListBoxMenu.propTypes = { * Provide the contents of your ListBoxMenu */ children: PropTypes.oneOfType([ + PropTypes.node, PropTypes.arrayOf(ListBoxMenuItem), /** * allow single item using the workaround for functional components From fe2b32be760d82654798ddb6dbb65868f47e602b Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 9 Feb 2021 14:38:06 -0600 Subject: [PATCH 3/9] docs(Dropdown): reduce item width --- packages/react/src/components/Dropdown/Dropdown-story.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react/src/components/Dropdown/Dropdown-story.js b/packages/react/src/components/Dropdown/Dropdown-story.js index 547ff8038464..323e644747e5 100644 --- a/packages/react/src/components/Dropdown/Dropdown-story.js +++ b/packages/react/src/components/Dropdown/Dropdown-story.js @@ -15,8 +15,7 @@ import mdx from './Dropdown.mdx'; const items = [ { id: 'option-0', - text: - 'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Vitae, aliquam. Blanditiis quia nemo enim voluptatibus quos ducimus porro molestiae nesciunt error cumque quaerat, tempore vero unde eum aperiam eligendi repellendus.', + text: 'Lorem, ipsum dolor sit amet consectetur adipisicing elit.', }, { id: 'option-1', From 5af57b2a039c7959b923bc14678a29fc2b79ce07 Mon Sep 17 00:00:00 2001 From: emyarod Date: Tue, 9 Feb 2021 14:41:52 -0600 Subject: [PATCH 4/9] chore: update snapshots --- packages/react/src/components/Dropdown/Dropdown.js | 2 +- .../components/Dropdown/__snapshots__/Dropdown-test.js.snap | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/react/src/components/Dropdown/Dropdown.js b/packages/react/src/components/Dropdown/Dropdown.js index 957ab692cce5..1262ab6a8fb1 100644 --- a/packages/react/src/components/Dropdown/Dropdown.js +++ b/packages/react/src/components/Dropdown/Dropdown.js @@ -184,7 +184,7 @@ const Dropdown = React.forwardRef(function Dropdown( isHighlighted={ highlightedIndex === index || selectedItem === item } - title={(offsetWidth < scrollWidth && title) || null} + title={(offsetWidth < scrollWidth && title) || undefined} ref={{ menuItemOptionRef: menuItemOptionRefs.current[index], }} diff --git a/packages/react/src/components/Dropdown/__snapshots__/Dropdown-test.js.snap b/packages/react/src/components/Dropdown/__snapshots__/Dropdown-test.js.snap index 917f5b55ae98..04e9b29e8f40 100644 --- a/packages/react/src/components/Dropdown/__snapshots__/Dropdown-test.js.snap +++ b/packages/react/src/components/Dropdown/__snapshots__/Dropdown-test.js.snap @@ -610,7 +610,6 @@ exports[`Dropdown should render with strings as items 1`] = ` onClick={[Function]} onMouseMove={[Function]} role="option" - title="zar" >
Date: Fri, 26 Feb 2021 14:59:54 -0600 Subject: [PATCH 5/9] fix(ListBoxMenuItem): add title tooltip only when content overflows --- .../src/components/ListBox/ListBoxMenuItem.js | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/packages/react/src/components/ListBox/ListBoxMenuItem.js b/packages/react/src/components/ListBox/ListBoxMenuItem.js index 8181941a598d..bf3a32258835 100644 --- a/packages/react/src/components/ListBox/ListBoxMenuItem.js +++ b/packages/react/src/components/ListBox/ListBoxMenuItem.js @@ -6,42 +6,57 @@ */ import cx from 'classnames'; -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { settings } from 'carbon-components'; const { prefix } = settings; +function useIsTruncated(ref) { + const [isTruncated, setIsTruncated] = useState(false); + + useEffect(() => { + const { offsetWidth, scrollWidth } = ref.current; + setIsTruncated(offsetWidth < scrollWidth); + }, [ref, setIsTruncated]); + + return isTruncated; +} + /** * `ListBoxMenuItem` is a helper component for managing the container class * name, alongside any classes for any corresponding states, for a generic list * box menu item. */ -const ListBoxMenuItem = React.forwardRef(function ListBoxMenuItem( - { children, isActive, isHighlighted, ...rest }, - ref -) { - const className = cx({ - [`${prefix}--list-box__menu-item`]: true, +const ListBoxMenuItem = ({ + children, + isActive, + isHighlighted, + title, + ...rest +}) => { + const ref = useRef(null); + const isTruncated = useIsTruncated(ref); + const className = cx(`${prefix}--list-box__menu-item`, { [`${prefix}--list-box__menu-item--active`]: isActive, [`${prefix}--list-box__menu-item--highlighted`]: isHighlighted, }); - const { menuItemRef, menuItemOptionRef } = ref || {}; + return ( -
-
+
+
{children}
); -}); +}; -ListBoxMenuItem.displayName = 'ListBoxMenuItem'; ListBoxMenuItem.propTypes = { /** - * Specify any children nodes that should be rendered inside of the ListBox + * Specify any children nodes that hsould be rendered inside of the ListBox * Menu Item */ children: PropTypes.node, @@ -55,6 +70,11 @@ ListBoxMenuItem.propTypes = { * Specify whether the current menu item is "highlighed". */ isHighlighted: PropTypes.bool.isRequired, + + /** + * Provide an optional tooltip for the ListBoxMenuItem + */ + title: PropTypes.string, }; ListBoxMenuItem.defaultProps = { From f69b7f973578635f347192ac06210c5b7f62b847 Mon Sep 17 00:00:00 2001 From: emyarod Date: Fri, 26 Feb 2021 15:00:26 -0600 Subject: [PATCH 6/9] refactor(ComboBox): convert to functional component to allow hooks --- .../react/src/components/ComboBox/ComboBox.js | 813 +++++++++--------- 1 file changed, 405 insertions(+), 408 deletions(-) diff --git a/packages/react/src/components/ComboBox/ComboBox.js b/packages/react/src/components/ComboBox/ComboBox.js index 490805d56424..71945464f578 100644 --- a/packages/react/src/components/ComboBox/ComboBox.js +++ b/packages/react/src/components/ComboBox/ComboBox.js @@ -8,7 +8,7 @@ import cx from 'classnames'; import Downshift from 'downshift'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { settings } from 'carbon-components'; import { Checkmark16, @@ -32,16 +32,21 @@ const defaultItemToString = (item) => { const defaultShouldFilterItem = () => true; -const getInputValue = (props, state) => { - if (props.selectedItem) { - return props.itemToString(props.selectedItem); +const getInputValue = ({ + initialSelectedItem, + inputValue, + itemToString, + selectedItem, +}) => { + if (selectedItem) { + return itemToString(selectedItem); } // TODO: consistent `initialSelectedItem` behavior with other listbox components in v11 - if (props.initialSelectedItem) { - return props.itemToString(props.initialSelectedItem); + if (initialSelectedItem) { + return itemToString(initialSelectedItem); } - return state.inputValue || ''; + return inputValue || ''; }; const findHighlightedIndex = ({ items, itemToString }, inputValue) => { @@ -63,244 +68,103 @@ const findHighlightedIndex = ({ items, itemToString }, inputValue) => { const getInstanceId = setupGetInstanceId(); -export default class ComboBox extends React.Component { - static propTypes = { - /** - * 'aria-label' of the ListBox component. - */ - ariaLabel: PropTypes.string, - - /** - * An optional className to add to the container node - */ - className: PropTypes.string, - - /** - * Specify the direction of the combobox dropdown. Can be either top or bottom. - */ - direction: PropTypes.oneOf(['top', 'bottom']), - - /** - * Specify if the control should be disabled, or not - */ - disabled: PropTypes.bool, - - /** - * Additional props passed to Downshift - */ - downshiftProps: PropTypes.shape(Downshift.propTypes), - - /** - * Provide helper text that is used alongside the control label for - * additional help - */ - helperText: PropTypes.string, - - /** - * Specify a custom `id` for the input - */ - id: PropTypes.string.isRequired, - - /** - * Allow users to pass in an arbitrary item or a string (in case their items are an array of strings) - * from their collection that are pre-selected - */ - initialSelectedItem: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.string, - ]), - - /** - * Specify if the currently selected value is invalid. - */ - invalid: PropTypes.bool, - - /** - * Message which is displayed if the value is invalid. - */ - invalidText: PropTypes.node, - - /** - * Optional function to render items as custom components instead of strings. - * Defaults to null and is overriden by a getter - */ - itemToElement: PropTypes.func, - - /** - * Helper function passed to downshift that allows the library to render a - * given item to a string label. By default, it extracts the `label` field - * from a given item to serve as the item label in the list - */ - itemToString: PropTypes.func, - - /** - * We try to stay as generic as possible here to allow individuals to pass - * in a collection of whatever kind of data structure they prefer - */ - items: PropTypes.array.isRequired, - - /** - * should use "light theme" (white background)? - */ - light: PropTypes.bool, - - /** - * `onChange` is a utility for this controlled component to communicate to a - * consuming component when a specific dropdown item is selected. - * @param {{ selectedItem }} - */ - onChange: PropTypes.func.isRequired, - - /** - * Callback function to notify consumer when the text input changes. - * This provides support to change available items based on the text. - * @param {string} inputText - */ - onInputChange: PropTypes.func, - - /** - * Callback function that fires when the combobox menu toggle is clicked - * @param {MouseEvent} event - */ - onToggleClick: PropTypes.func, - - /** - * Used to provide a placeholder text node before a user enters any input. - * This is only present if the control has no items selected - */ - placeholder: PropTypes.string.isRequired, - - /** - * For full control of the selection - */ - selectedItem: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), - - /** - * Specify your own filtering logic by passing in a `shouldFilterItem` - * function that takes in the current input and an item and passes back - * whether or not the item should be filtered. - */ - shouldFilterItem: PropTypes.func, - - /** - * Specify the size of the ListBox. Currently supports either `sm`, `lg` or `xl` as an option. - */ - size: ListBoxPropTypes.ListBoxSize, - - /** - * Provide text to be used in a `
- - +
- - +
- - +
- - + - + @@ -601,7 +601,7 @@ exports[`Dropdown should render with strings as items 1`] = ` role="listbox" tabIndex={-1} > - - - + - + diff --git a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenu-test.js.snap b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenu-test.js.snap index 52cc01053551..c9d75663e139 100644 --- a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenu-test.js.snap +++ b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenu-test.js.snap @@ -9,7 +9,7 @@ exports[`ListBoxMenu should render 1`] = ` id="test-listbox" role="listbox" > - @@ -22,7 +22,7 @@ exports[`ListBoxMenu should render 1`] = ` Hello - + `; diff --git a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenuItem-test.js.snap b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenuItem-test.js.snap index 0f20253e8d57..8b1f126a2baf 100644 --- a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenuItem-test.js.snap +++ b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxMenuItem-test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ListBoxMenuItem should render 1`] = ` - @@ -16,11 +16,11 @@ exports[`ListBoxMenuItem should render 1`] = ` - + `; exports[`ListBoxMenuItem should render 2`] = ` - @@ -35,11 +35,11 @@ exports[`ListBoxMenuItem should render 2`] = ` - + `; exports[`ListBoxMenuItem should render 3`] = ` - @@ -54,5 +54,5 @@ exports[`ListBoxMenuItem should render 3`] = ` - + `;