Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Field level tooltip to Combobox / Deprecate Input assistiveText.fieldLevelHelpButton #1689

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions components/combobox/__docs__/storybook-stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { action } from '@storybook/addon-actions';
import { COMBOBOX } from '../../../utilities/constants';

import Base from '../__examples__/base';
import BaseInlineHelpTooltip from '../__examples__/base-inline-help-tooltip';
import BaseMenuSubHeader from '../__examples__/base-menu-subheader';
import BaseMenuSeparator from '../__examples__/base-menu-separator';
import BaseInheritMenuWidth from '../__examples__/base-inherit-menu-width.jsx';
Expand Down Expand Up @@ -42,6 +43,14 @@ storiesOf(COMBOBOX, module)
.add('Base Pre-defined Options Only', () => (
<PredefinedOptionsOnly action={action} />
))
.add('Base Inline Help', () => (
<section>
<h1 className="slds-text-title_caps slds-p-vertical_medium">
Field Level Help Tooltip
</h1>
<BaseInlineHelpTooltip action={action} />
</section>
))
.add('Inline Single Selection', () => <InlineSingle action={action} />)
.add('Inline Multiple Selection', () => <InlineMultiple action={action} />)
.add('Base Custom Menu Item', () => <BaseCustomMenuItem action={action} />)
Expand Down
164 changes: 164 additions & 0 deletions components/combobox/__examples__/base-inline-help-tooltip.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* eslint-disable no-console, react/prop-types */
import React from 'react';
import Combobox from '~/components/combobox';
import comboboxFilterAndLimit from '~/components/combobox/filter';
import Icon from '~/components/icon';
import IconSettings from '~/components/icon-settings';
import Tooltip from '../../tooltip';

const accounts = [
{
id: '1',
label: 'Acme',
subTitle: 'Account • San Francisco',
type: 'account',
},
{
id: '2',
label: 'Salesforce.com, Inc.',
subTitle: 'Account • San Francisco',
type: 'account',
},
{
id: '3',
label: "Paddy's Pub",
subTitle: 'Account • Boston, MA',
type: 'account',
},
{
id: '4',
label: 'Tyrell Corp',
subTitle: 'Account • San Francisco, CA',
type: 'account',
},
{
id: '5',
label: 'Paper St. Soap Company',
subTitle: 'Account • Beloit, WI',
type: 'account',
},
{
id: '6',
label: 'Nakatomi Investments',
subTitle: 'Account • Chicago, IL',
type: 'account',
},
{ id: '7', label: 'Acme Landscaping', subTitle: '\u00A0', type: 'account' },
{
id: '8',
label: 'Acme Construction',
subTitle: 'Account • Grand Marais, MN',
type: 'account',
},
];

const accountsWithIcon = accounts.map((elem) => ({
...elem,
...{
icon: (
<Icon
assistiveText={{ label: 'Account' }}
category="standard"
name={elem.type}
/>
),
},
}));

class Example extends React.Component {
constructor(props) {
super(props);

this.state = {
inputValue: '',
selection: [accountsWithIcon[0], accountsWithIcon[1]],
};
}

render() {
return (
<IconSettings iconPath="/assets/icons">
<Combobox
id="combobox-unique-id"
disabled={this.props.disabled}
events={{
onChange: (event, { value }) => {
if (this.props.action) {
this.props.action('onChange')(event, value);
} else if (console) {
console.log('onChange', event, value);
}
this.setState({ inputValue: value });
},
onRequestRemoveSelectedOption: (event, data) => {
this.setState({
inputValue: '',
selection: data.selection,
});
},
onSubmit: (event, { value }) => {
if (this.props.action) {
this.props.action('onChange')(event, value);
} else if (console) {
console.log('onChange', event, value);
}
this.setState({
inputValue: '',
selection: [
...this.state.selection,
{
label: value,
icon: (
<Icon
assistiveText="Account"
category="standard"
name="account"
/>
),
},
],
});
},
onSelect: (event, data) => {
if (this.props.action) {
this.props.action('onSelect')(
event,
...Object.keys(data).map((key) => data[key])
);
} else if (console) {
console.log('onSelect', event, data);
}
this.setState({
inputValue: '',
selection: data.selection,
});
},
}}
fieldLevelHelpTooltip={
<Tooltip
align="top left"
content="Type to search Salesforce objects..."
id="field-level-help-tooltip"
/>
}
labels={{
label: 'Search',
placeholder: 'Search Salesforce',
}}
multiple
options={comboboxFilterAndLimit({
inputValue: this.state.inputValue,
limit: 10,
options: accountsWithIcon,
selection: this.state.selection,
})}
selection={this.state.selection}
value={this.state.inputValue}
/>
</IconSettings>
);
}
}

Example.displayName = 'ComboboxExample';
export default Example; // export is replaced with `ReactDOM.render(<Example />, mountNode);` at runtime
13 changes: 12 additions & 1 deletion components/combobox/combobox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Menu from './private/menu';
import Label from '../forms/private/label';
import SelectedListBox from '../pill-container/private/selected-listbox';

import FieldLevelHelpTooltip from '../tooltip/private/field-level-help-tooltip';
import KEYS from '../../utilities/key-code';
import KeyBuffer from '../../utilities/key-buffer';
import keyLetterMenuItemSelect from '../../utilities/key-letter-menu-item-select';
Expand Down Expand Up @@ -126,6 +127,10 @@ const propTypes = {
* Message to display when the input is in an error state. When this is present, also visually highlights the component as in error. _Tested with snapshot testing._
*/
errorText: PropTypes.string,
/**
* A [Tooltip](https://react.lightningdesignsystem.com/components/tooltips/) component that is displayed next to the `labels.label`. The props from the component will be merged and override any default props.
*/
fieldLevelHelpTooltip: PropTypes.node,
/**
* By default, dialogs will flip their alignment (such as bottom to top) if they extend beyond a boundary element such as a scrolling parent or a window/viewpoint. `hasStaticAlignment` disables this behavior and allows this component to extend beyond boundary elements. _Not tested._
*/
Expand Down Expand Up @@ -1382,7 +1387,8 @@ class Combobox extends React.Component {
props.assistiveText
);
const labels = assign({}, defaultProps.labels, this.props.labels);

const hasRenderedLabel =
labels.label || (assistiveText && assistiveText.label);
const subRenderParameters = { assistiveText, labels, props: this.props };
const multipleOrSingle = this.props.multiple ? 'multiple' : 'single';
const subRenders = {
Expand Down Expand Up @@ -1411,6 +1417,11 @@ class Combobox extends React.Component {
label={labels.label}
required={props.required}
/>
{this.props.fieldLevelHelpTooltip && hasRenderedLabel ? (
<FieldLevelHelpTooltip
fieldLevelHelpTooltip={this.props.fieldLevelHelpTooltip}
/>
) : null}
{variantExists
? subRenders[this.props.variant][multipleOrSingle](
subRenderParameters
Expand Down
27 changes: 27 additions & 0 deletions components/component-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3386,6 +3386,26 @@
}
],
"returns": null
},
{
"name": "renderFieldLevelHelpTooltip",
"docblock": null,
"modifiers": [],
"params": [
{
"name": "fieldLevelHelpTooltip",
"type": null
},
{
"name": "labels",
"type": null
},
{
"name": "assistiveText",
"type": null
}
],
"returns": null
}
],
"props": {
Expand Down Expand Up @@ -3561,6 +3581,13 @@
"required": false,
"description": "Message to display when the input is in an error state. When this is present, also visually highlights the component as in error. _Tested with snapshot testing._"
},
"fieldLevelHelpTooltip": {
"type": {
"name": "node"
},
"required": false,
"description": "A [Tooltip](https://react.lightningdesignsystem.com/components/tooltips/) component that is displayed next to the `labels.label`. The props from the component will be merged and override any default props."
},
"hasStaticAlignment": {
"type": {
"name": "bool"
Expand Down
1 change: 0 additions & 1 deletion components/input/__examples__/field-level-help.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class Example extends React.Component {
</h1>
<Input
id="field-level-help"
assistiveText={{ fieldLevelHelpButton: 'Info' }}
label="My Label"
fieldLevelHelpTooltip={
<Tooltip
Expand Down
7 changes: 7 additions & 0 deletions components/input/check-props.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ if (process.env.NODE_ENV !== 'production') {
const iconDeprecatedMessage = `Please use \`iconLeft\` and \`iconRight\` to pass in a customized <Icon> component. ${createDocUrl()}`;

// Deprecated and changed to another property
deprecatedProperty(
COMPONENT,
props.assistiveText.fieldLevelHelpButton,
'assistiveText.fieldLevelHelpButton',
undefined,
`Please pass a \`Tooltip\` component into \`fieldLevelHelpTooltip\` with \`assistiveText.triggerLearnMoreIcon\`.`
);
deprecatedProperty(
COMPONENT,
props.iconCategory,
Expand Down
44 changes: 13 additions & 31 deletions components/input/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import classNames from 'classnames';
import shortid from 'shortid';

import Button from '../button';
import Tooltip from '../tooltip';

// ## Children
import InputIcon from '../icon/input-icon';
Expand All @@ -37,6 +36,7 @@ import checkProps from './check-props';

import { INPUT } from '../../utilities/constants';
import componentDoc from './docs.json';
import FieldLevelHelpTooltip from '../tooltip/private/field-level-help-tooltip';

const COUNTER = 'counter';
const DECREMENT = 'Decrement';
Expand All @@ -45,7 +45,6 @@ const INCREMENT = 'Increment';
const defaultProps = {
assistiveText: {
decrement: `${DECREMENT} ${COUNTER}`,
fieldLevelHelpButton: 'Help',
increment: `${INCREMENT} ${COUNTER}`,
},
type: 'text',
Expand Down Expand Up @@ -104,12 +103,10 @@ class Input extends React.Component {
* **Assistive text for accessibility**
* * `label`: Visually hidden label but read out loud by screen readers.
* * `spinner`: Text for loading spinner icon.
* * `fieldLevelHelpButton`: The field level help button, by default an 'info' icon.
*/
assistiveText: PropTypes.shape({
label: PropTypes.string,
spinner: PropTypes.string,
fieldLevelHelpButton: PropTypes.string,
}),
/**
* Elements are added after the `input`.
Expand Down Expand Up @@ -141,7 +138,7 @@ class Input extends React.Component {
*/
errorText: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
/**
* A [Tooltip](https://react.lightningdesignsystem.com/components/tooltips/) component that is displayed next to the label. The props from the component will be merged and override any default props.
* A [Tooltip](https://react.lightningdesignsystem.com/components/tooltips/) component that is displayed next to the label.
*/
fieldLevelHelpTooltip: PropTypes.node,
/**
Expand Down Expand Up @@ -539,33 +536,11 @@ class Input extends React.Component {
};
const inputRef =
this.props.variant === COUNTER ? this.setInputRef : this.props.inputRef;
let fieldLevelHelpTooltip;
let iconLeft = null;
let iconRight = null;

if (
(this.props.label ||
(this.props.assistiveText && this.props.assistiveText.label)) &&
this.props.fieldLevelHelpTooltip
) {
const defaultTooltipProps = {
triggerClassName: 'slds-form-element__icon',
triggerStyle: { position: 'static' },
children: (
<Button
assistiveText={{ icon: assistiveText.fieldLevelHelpButton }}
iconCategory="utility"
iconName="info"
variant="icon"
/>
),
};
const tooltipProps = {
...defaultTooltipProps,
...this.props.fieldLevelHelpTooltip.props,
};
fieldLevelHelpTooltip = <Tooltip {...tooltipProps} />;
}
const hasRenderedLabel =
this.props.label || (assistiveText && assistiveText.label);

// Remove at next breaking change
// this is a hack to make left the default prop unless overwritten by `iconPosition="right"`
Expand Down Expand Up @@ -608,13 +583,20 @@ class Input extends React.Component {
)}
>
<Label
assistiveText={this.props.assistiveText}
assistiveText={assistiveText}
htmlFor={this.props.isStatic ? undefined : this.getId()}
label={this.props.label}
required={this.props.required}
variant={this.props.isStatic ? 'static' : 'base'}
/>
{fieldLevelHelpTooltip}
{this.props.fieldLevelHelpTooltip && hasRenderedLabel ? (
<FieldLevelHelpTooltip
assistiveText={{
triggerLearnMoreIcon: assistiveText.fieldLevelHelpButton,
}}
fieldLevelHelpTooltip={this.props.fieldLevelHelpTooltip}
/>
) : null}
<InnerInput
aria-activedescendant={this.props['aria-activedescendant']}
aria-autocomplete={this.props['aria-autocomplete']}
Expand Down
Loading