From e014410537d3ad1ce9bded40d9d7479901202070 Mon Sep 17 00:00:00 2001 From: kparkerson Date: Tue, 20 Aug 2019 17:20:30 -0600 Subject: [PATCH 1/2] Add tooltip menu item ability --- components/component-docs.json | 9 +++- .../__snapshots__/storybook-stories.storyshot | 40 ++++++++++++++ .../__docs__/storybook-stories.jsx | 4 +- .../__examples__/with-tooltips.jsx | 47 +++++++++++++++++ .../__tests__/dropdown.browser-test.jsx | 34 ++++++++++++ components/menu-dropdown/component.json | 3 +- components/menu-dropdown/menu-dropdown.jsx | 6 +++ components/site-stories.js | 1 + components/utilities/menu-list/index.jsx | 52 ++++++++++++++++++- components/utilities/menu-list/item.jsx | 52 ++++++++++++++----- package.json | 3 +- 11 files changed, 233 insertions(+), 18 deletions(-) create mode 100644 components/menu-dropdown/__examples__/with-tooltips.jsx diff --git a/components/component-docs.json b/components/component-docs.json index fab0b042aa..4dbcf2fd63 100644 --- a/components/component-docs.json +++ b/components/component-docs.json @@ -9883,7 +9883,7 @@ "name": "array" }, "required": false, - "description": "An array of menu item objects. `className` and `id` object keys are applied to the `li` DOM node. `divider` key can have a value of `top` or `bottom`. `rightIcon` and `leftIcon` are not actually `Icon` components, but prop objects that get passed to an `Icon` component. The `href` key will be added to the `a` and its default click event will be prevented. Here is a sample:\n```\n[{\n className: 'custom-li-class',\n divider: 'bottom',\n label: 'A Header',\n type: 'header'\n }, {\n href: 'http://sfdc.co/',\n id: 'custom-li-id',\n label: 'Has a value',\n leftIcon: {\n name: 'settings',\n category: 'utility'\n },\n rightIcon: {\n name: 'settings',\n category: 'utility'\n },\n type: 'item',\n value: 'B0'\n }, {\n type: 'divider'\n}]\n```" + "description": "An array of menu item objects. `className` and `id` object keys are applied to the `li` DOM node. `divider` key can have a value of `top` or `bottom`. `rightIcon` and `leftIcon` are not actually `Icon` components, but prop objects that get passed to an `Icon` component. The `href` key will be added to the `a` and its default click event will be prevented. Here is a sample:\n```\n[{\n className: 'custom-li-class',\n divider: 'bottom',\n label: 'A Header',\n type: 'header'\n }, {\n href: 'http://sfdc.co/',\n id: 'custom-li-id',\n label: 'Has a value',\n leftIcon: {\n name: 'settings',\n category: 'utility'\n },\n rightIcon: {\n name: 'settings',\n category: 'utility'\n },\n type: 'item',\n value: 'B0'\n }, {\n tooltipContent: 'Displays a tooltip when hovered over with this content. The `tooltipMenuItem` prop must be set for this to work.'\n type: 'divider'\n}]\n```" }, "style": { "type": { @@ -9939,6 +9939,13 @@ "required": false, "description": "This prop is passed onto the triggering `Button`. It creates a tooltip with the content of the `node` provided." }, + "tooltipMenuItem": { + "type": { + "name": "node" + }, + "required": false, + "description": "Accepts a `Tooltip` component to be used as the template for menu item tooltips that appear via the `tooltipContent` options object attribute. Must be present for `tooltipContent` to work" + }, "triggerClassName": { "type": { "name": "union", diff --git a/components/menu-dropdown/__docs__/__snapshots__/storybook-stories.storyshot b/components/menu-dropdown/__docs__/__snapshots__/storybook-stories.storyshot index 07bd7acef1..63497519d7 100644 --- a/components/menu-dropdown/__docs__/__snapshots__/storybook-stories.storyshot +++ b/components/menu-dropdown/__docs__/__snapshots__/storybook-stories.storyshot @@ -4688,3 +4688,43 @@ exports[`DOM snapshots SLDSMenuDropdown Two Hovers 1`] = ` `; + +exports[`DOM snapshots SLDSMenuDropdown With tooltips 1`] = ` +
+ +
+`; diff --git a/components/menu-dropdown/__docs__/storybook-stories.jsx b/components/menu-dropdown/__docs__/storybook-stories.jsx index 5053fa3d2d..2c30a92e33 100644 --- a/components/menu-dropdown/__docs__/storybook-stories.jsx +++ b/components/menu-dropdown/__docs__/storybook-stories.jsx @@ -10,6 +10,7 @@ import IconSettings from '../../icon-settings'; import { MENU_DROPDOWN } from '../../../utilities/constants'; import Dropdown from '../../menu-dropdown'; import { DropdownNubbinPositions } from '../../menu-dropdown/menu-dropdown'; +import DropdownWithTooltips from '../__examples__/with-tooltips'; import List from '../../utilities/menu-list'; import Button from '../../button'; import Trigger from '../../menu-dropdown/button-trigger'; @@ -472,4 +473,5 @@ storiesOf(MENU_DROPDOWN, module) label="Dropdown Click" options={options} /> - )); + )) + .add('With tooltips', () => ); diff --git a/components/menu-dropdown/__examples__/with-tooltips.jsx b/components/menu-dropdown/__examples__/with-tooltips.jsx new file mode 100644 index 0000000000..fbbc00b9dc --- /dev/null +++ b/components/menu-dropdown/__examples__/with-tooltips.jsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import IconSettings from '~/components/icon-settings'; +import Dropdown from '~/components/menu-dropdown'; // `~` is replaced with design-system-react at runtime +import Tooltip from '~/components/tooltip'; + +class Example extends React.Component { + static displayName = 'DropdownWithTooltipsExample'; + + render() { + return ( + + } + {...this.props} + /> + + ); + } +} + +export default Example; // export is replaced with `ReactDOM.render(, mountNode);` at runtime diff --git a/components/menu-dropdown/__tests__/dropdown.browser-test.jsx b/components/menu-dropdown/__tests__/dropdown.browser-test.jsx index f8c28a2e78..bb84d78cc7 100644 --- a/components/menu-dropdown/__tests__/dropdown.browser-test.jsx +++ b/components/menu-dropdown/__tests__/dropdown.browser-test.jsx @@ -24,6 +24,7 @@ import { // Import your internal dependencies (for example): import Dropdown from '../../menu-dropdown'; import IconSettings from '../../icon-settings'; +import Tooltip from '../../tooltip'; import List from '../../utilities/menu-list'; import { keyObjects } from '../../../utilities/key-code'; @@ -548,4 +549,37 @@ describe('SLDSMenuDropdown', function() { }, 2); }); }); + + describe('Tooltips function as expected', () => { + beforeEach( + mountComponent( + } + /> + ) + ); + + afterEach(unmountComponent); + + it('Tooltip component shows when focused on menu item.', function() { + const nodes = getNodes({ wrapper: this.wrapper }); + nodes.trigger.simulate('focus'); + nodes.trigger.simulate('keyDown', keyObjects.ENTER); + nodes.trigger.simulate('keyDown', keyObjects.DOWN); + + const tooltip = this.wrapper + .find('#sample-dropdown-item-1-tooltip') + .hostNodes(); + expect(tooltip.length).to.equal(1); + }); + }); }); diff --git a/components/menu-dropdown/component.json b/components/menu-dropdown/component.json index 6a940b9cb5..0ab24f399b 100644 --- a/components/menu-dropdown/component.json +++ b/components/menu-dropdown/component.json @@ -20,7 +20,8 @@ "/__examples__/default-right-to-left.jsx", "/__examples__/sub-heading.jsx", "/__examples__/custom-trigger.jsx", - "/__examples__/checkmark.jsx" + "/__examples__/checkmark.jsx", + "/__examples__/with-tooltips.jsx" ], "url-slug": "menu-dropdowns" } diff --git a/components/menu-dropdown/menu-dropdown.jsx b/components/menu-dropdown/menu-dropdown.jsx index 765e4a3e97..678905fd25 100644 --- a/components/menu-dropdown/menu-dropdown.jsx +++ b/components/menu-dropdown/menu-dropdown.jsx @@ -345,6 +345,7 @@ const propTypes = { * type: 'item', * value: 'B0' * }, { + * tooltipContent: 'Displays a tooltip when hovered over with this content. The `tooltipMenuItem` prop must be set for this to work.' * type: 'divider' * }] * ``` @@ -374,6 +375,10 @@ const propTypes = { * This prop is passed onto the triggering `Button`. It creates a tooltip with the content of the `node` provided. */ tooltip: PropTypes.node, + /** + * Accepts a `Tooltip` component to be used as the template for menu item tooltips that appear via the `tooltipContent` options object attribute. Must be present for `tooltipContent` to work + */ + tooltipMenuItem: PropTypes.node, /** * CSS classes to be added to wrapping trigger `div` around the button. */ @@ -848,6 +853,7 @@ class MenuDropdown extends React.Component { selectedIndices={ this.props.multiple ? this.state.selectedIndices : undefined } + tooltipMenuItem={this.props.tooltipMenuItem} triggerId={this.getId()} length={this.props.length} {...customListProps} diff --git a/components/site-stories.js b/components/site-stories.js index b38e47bce0..5347613903 100644 --- a/components/site-stories.js +++ b/components/site-stories.js @@ -169,6 +169,7 @@ const documentationSiteLiveExamples = { require('raw-loader!@salesforce/design-system-react/components/menu-dropdown/__examples__/sub-heading.jsx'), require('raw-loader!@salesforce/design-system-react/components/menu-dropdown/__examples__/custom-trigger.jsx'), require('raw-loader!@salesforce/design-system-react/components/menu-dropdown/__examples__/checkmark.jsx'), + require('raw-loader!@salesforce/design-system-react/components/menu-dropdown/__examples__/with-tooltips.jsx'), ], modal: [ require('raw-loader!@salesforce/design-system-react/components/modal/__examples__/menu-contents.jsx'), diff --git a/components/utilities/menu-list/index.jsx b/components/utilities/menu-list/index.jsx index c0863de5fa..f22b83ee0d 100644 --- a/components/utilities/menu-list/index.jsx +++ b/components/utilities/menu-list/index.jsx @@ -62,6 +62,10 @@ class List extends React.Component { * The index of the currently selected item in the list. */ selectedIndex: PropTypes.number, + /** + * Accepts a `Tooltip` component to be used as the template for menu item tooltips that appear via the `tooltipContent` options object attribute + */ + tooltipMenuItem: PropTypes.node, /** * The id of the element which triggered this list (in a menu context). */ @@ -76,10 +80,13 @@ class List extends React.Component { render() { let lengthClassName; + let list; + if (this.props.length) { lengthClassName = `slds-dropdown_length-${this.props.length}`; } - return ( + + list = (
    this.props.itemRefs(listItem, index)} + tooltipTemplate={this.props.tooltipMenuItem} /> ); })}
); + + if (this.props.tooltipMenuItem) { + /* eslint-disable react/no-danger */ + list = ( + +