Skip to content
This repository has been archived by the owner on Oct 19, 2021. It is now read-only.

Commit

Permalink
feat(MultiSelect): match experimental spec (#2239)
Browse files Browse the repository at this point in the history
  • Loading branch information
emyarod authored and asudoh committed May 3, 2019
1 parent 2ca3c07 commit 0724ed5
Show file tree
Hide file tree
Showing 10 changed files with 483 additions and 347 deletions.
Binary file removed .yarn-offline-mirror/carbon-components-10.1.0.tgz
Binary file not shown.
Binary file added .yarn-offline-mirror/carbon-components-10.2.0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"babel-loader": "^8.0.4",
"babel-plugin-dev-expression": "^0.2.1",
"babel-plugin-react-docgen": "^2.0.0",
"carbon-components": "^10.1.0",
"carbon-components": "^10.2.0",
"carbon-icons": "^7.0.5",
"chalk": "^2.3.0",
"cli-table": "^0.3.0",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Checkbox = React.forwardRef(function Checkbox(
ref
) {
const labelClasses = classNames(`${prefix}--checkbox-label`, className);
const innerLabelClasses = classNames({
const innerLabelClasses = classNames(`${prefix}--checkbox-label-text`, {
[`${prefix}--visually-hidden`]: hideLabel,
});
const wrapperClasses = classNames(
Expand Down
212 changes: 133 additions & 79 deletions src/components/MultiSelect/FilterableMultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import React from 'react';
import Downshift from 'downshift';
import isEqual from 'lodash.isequal';
import { settings } from 'carbon-components';
import WarningFilled16 from '@carbon/icons-react/lib/warning--filled/16';
import ListBox from '../ListBox';
import Checkbox from '../Checkbox';
import Selection from '../../internal/Selection';
Expand Down Expand Up @@ -242,6 +243,9 @@ export default class FilterableMultiSelect extends React.Component {
filterItems,
items,
itemToString,
titleText,
helperText,
type,
initialSelectedItems,
id,
locale,
Expand All @@ -253,15 +257,33 @@ export default class FilterableMultiSelect extends React.Component {
invalidText,
translateWithId,
} = this.props;
const className = cx(
`${prefix}--multi-select`,
`${prefix}--combo-box`,
containerClassName,
const inline = type === 'inline';
const wrapperClasses = cx(
`${prefix}--multi-select__wrapper`,
`${prefix}--list-box__wrapper`,
{
[`${prefix}--list-box--light`]: light,
[`${prefix}--multi-select__wrapper--inline`]: inline,
[`${prefix}--list-box__wrapper--inline`]: inline,
[`${prefix}--multi-select__wrapper--inline--invalid`]:
inline && invalid,
[`${prefix}--list-box__wrapper--inline--invalid`]: inline && invalid,
}
);
return (
const titleClasses = cx(`${prefix}--label`, {
[`${prefix}--label--disabled`]: disabled,
});
const title = titleText ? (
<label htmlFor={id} className={titleClasses}>
{titleText}
</label>
) : null;
const helperClasses = cx(`${prefix}--form__helper-text`, {
[`${prefix}--form__helper-text--disabled`]: disabled,
});
const helper = helperText ? (
<div className={helperClasses}>{helperText}</div>
) : null;
const input = (
<Selection
onChange={this.handleOnChange}
initialSelectedItems={initialSelectedItems}
Expand All @@ -284,85 +306,117 @@ export default class FilterableMultiSelect extends React.Component {
isOpen,
inputValue,
selectedItem,
}) => (
<ListBox
className={className}
disabled={disabled}
invalid={invalid}
invalidText={invalidText}
{...getRootProps({ refKey: 'innerRef' })}>
<ListBox.Field id={id} {...getButtonProps({ disabled })}>
{selectedItem.length > 0 && (
<ListBox.Selection
clearSelection={clearSelection}
selectionCount={selectedItem.length}
translateWithId={translateWithId}
}) => {
const className = cx(
`${prefix}--multi-select`,
`${prefix}--combo-box`,
`${prefix}--multi-select--filterable`,
containerClassName,
{
[`${prefix}--multi-select--invalid`]: invalid,
[`${prefix}--multi-select--open`]: isOpen,
[`${prefix}--multi-select--inline`]: inline,
[`${prefix}--multi-select--selected`]:
selectedItem.length > 0,
}
);
return (
<ListBox
className={className}
disabled={disabled}
light={light}
invalid={invalid}
invalidText={invalidText}
isOpen={isOpen}
{...getRootProps({ refKey: 'innerRef' })}>
{invalid && (
<WarningFilled16
className={`${prefix}--list-box__invalid-icon`}
/>
)}
<input
className={`${prefix}--text-input`}
aria-controls={`${id}__menu`}
aria-autocomplete="list"
ref={el => (this.inputNode = el)}
{...getInputProps({
disabled,
id,
placeholder,
onKeyDown: this.handleOnInputKeyDown,
})}
/>
{inputValue && isOpen && (
<ListBox.Selection clearSelection={this.clearInputValue} />
<ListBox.Field id={id} {...getButtonProps({ disabled })}>
{selectedItem.length > 0 && (
<ListBox.Selection
clearSelection={clearSelection}
selectionCount={selectedItem.length}
translateWithId={translateWithId}
/>
)}
<input
className={`${prefix}--text-input`}
aria-controls={`${id}__menu`}
aria-autocomplete="list"
ref={el => (this.inputNode = el)}
{...getInputProps({
disabled,
id,
placeholder,
onKeyDown: this.handleOnInputKeyDown,
})}
/>
{inputValue && isOpen && (
<ListBox.Selection
clearSelection={this.clearInputValue}
/>
)}
<ListBox.MenuIcon
isOpen={isOpen}
translateWithId={translateWithId}
/>
</ListBox.Field>
{isOpen && (
<ListBox.Menu aria-label={ariaLabel} id={id}>
{sortItems(
filterItems(items, { itemToString, inputValue }),
{
selectedItems: {
top: selectedItems,
fixed: [],
'top-after-reopen': this.state.topItems,
}[this.props.selectionFeedback],
itemToString,
compareItems,
locale,
}
).map((item, index) => {
const itemProps = getItemProps({ item });
const itemText = itemToString(item);
const isChecked =
selectedItem.filter(selected =>
isEqual(selected, item)
).length > 0;
return (
<ListBox.MenuItem
key={itemProps.id}
isActive={isChecked}
isHighlighted={highlightedIndex === index}
{...itemProps}>
<Checkbox
id={itemProps.id}
name={itemText}
checked={isChecked}
readOnly={true}
tabIndex="-1"
labelText={itemText}
/>
</ListBox.MenuItem>
);
})}
</ListBox.Menu>
)}
<ListBox.MenuIcon
isOpen={isOpen}
translateWithId={translateWithId}
/>
</ListBox.Field>
{isOpen && (
<ListBox.Menu aria-label={ariaLabel} id={id}>
{sortItems(
filterItems(items, { itemToString, inputValue }),
{
selectedItems: {
top: selectedItems,
fixed: [],
'top-after-reopen': this.state.topItems,
}[this.props.selectionFeedback],
itemToString,
compareItems,
locale,
}
).map((item, index) => {
const itemProps = getItemProps({ item });
const itemText = itemToString(item);
const isChecked =
selectedItem.filter(selected => isEqual(selected, item))
.length > 0;
return (
<ListBox.MenuItem
key={itemProps.id}
isActive={isChecked}
isHighlighted={highlightedIndex === index}
{...itemProps}>
<Checkbox
id={itemProps.id}
name={itemText}
checked={isChecked}
readOnly={true}
tabIndex="-1"
labelText={itemText}
/>
</ListBox.MenuItem>
);
})}
</ListBox.Menu>
)}
</ListBox>
)}
</ListBox>
);
}}
/>
)}
/>
);
return (
<div className={wrapperClasses}>
{title}
{!inline && helper}
{input}
</div>
);
}
}
27 changes: 20 additions & 7 deletions src/components/MultiSelect/MultiSelect-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { componentsX } from '../../internal/FeatureFlags';

import {
withKnobs,
boolean,
Expand All @@ -23,12 +21,25 @@ import MultiSelect from '../MultiSelect';

const items = [
{
id: 'item-1',
text: 'Item 1',
id: 'downshift-1-item-0',
text: 'Option 1',
},
{
id: 'downshift-1-item-1',
text: 'Option 2',
},
{
id: 'downshift-1-item-2',
text: 'Option 3',
},
{
id: 'downshift-1-item-3',
text: 'Option 4',
},
{
id: 'item-2',
text: 'Item 2',
id: 'downshift-1-item-4',
text:
'An example option that is really long to show what should be done to handle long text',
},
];

Expand All @@ -42,6 +53,8 @@ const types = {

const props = () => ({
id: text('MultiSelect ID (id)', 'carbon-multiselect-example'),
titleText: text('Title (titleText)', 'Multiselect title'),
helperText: text('Helper text (helperText)', 'This is not helper text'),
filterable: boolean(
'Filterable (`<MultiSelect.Filterable>` instead of `<MultiSelect>`)',
false
Expand All @@ -67,7 +80,7 @@ const props = () => ({
selectionFeedback: select(
'Selection feedback',
['top', 'fixed', 'top-after-reopen'],
componentsX ? 'top-after-reopen' : 'top'
'top-after-reopen'
),
});

Expand Down
Loading

0 comments on commit 0724ed5

Please sign in to comment.