diff --git a/packages/components/src/components/button/_button.scss b/packages/components/src/components/button/_button.scss index 4b9719d560cf..903e0b420eda 100644 --- a/packages/components/src/components/button/_button.scss +++ b/packages/components/src/components/button/_button.scss @@ -96,12 +96,12 @@ } &:focus { - color: $text-04; + color: $inverse-01; background-color: $interactive-03; } &:active { - background-color: $active-primary; + color: $inverse-01; } &:disabled, @@ -112,6 +112,7 @@ &.#{$prefix}--btn--disabled:focus { background: transparent; color: $disabled; + outline: none; & > .#{$prefix}--btn__icon path { fill: $disabled; @@ -161,6 +162,7 @@ color: $disabled; background: transparent; border-color: transparent; + outline: none; .#{$prefix}--btn__icon path { fill: $disabled; diff --git a/packages/components/src/components/checkbox/_checkbox.scss b/packages/components/src/components/checkbox/_checkbox.scss index dc81de9a2d67..df09d8a15e55 100644 --- a/packages/components/src/components/checkbox/_checkbox.scss +++ b/packages/components/src/components/checkbox/_checkbox.scss @@ -18,7 +18,7 @@ @mixin checkbox { // Spacing between checkboxes .#{$prefix}--form-item.#{$prefix}--checkbox-wrapper { - margin-bottom: rem(8px); + margin-bottom: $carbon--spacing-02; } // Spacing above collection of checkboxes @@ -26,9 +26,10 @@ margin-top: rem(3px); } - // Remove spacing above collection of checkboxes if label is present + // Shift collection of checkboxes up if label is present + // to account for the 2px top margin for the first checkbox .#{$prefix}--label + .#{$prefix}--form-item.#{$prefix}--checkbox-wrapper { - margin-top: 0; + margin-top: -#{$carbon--spacing-01}; } // Spacing below collection of checkboxes diff --git a/packages/components/src/components/data-table/_data-table-action.scss b/packages/components/src/components/data-table/_data-table-action.scss index 0b71c9637a9c..fc7b7ec8d31f 100644 --- a/packages/components/src/components/data-table/_data-table-action.scss +++ b/packages/components/src/components/data-table/_data-table-action.scss @@ -17,11 +17,12 @@ //TOOLBAR //------------------------------------------------- .#{$prefix}--table-toolbar { - display: flex; - width: 100%; background: $ui-01; + display: flex; height: $layout-04; + overflow: hidden; position: relative; //need for batch actions + width: 100%; } .#{$prefix}--toolbar-content { diff --git a/packages/components/src/components/date-picker/_date-picker.scss b/packages/components/src/components/date-picker/_date-picker.scss index 9089d50b2b5f..426ec2a2eaa2 100644 --- a/packages/components/src/components/date-picker/_date-picker.scss +++ b/packages/components/src/components/date-picker/_date-picker.scss @@ -40,6 +40,7 @@ .#{$prefix}--date-picker-input__wrapper { display: flex; align-items: center; + position: relative; ~ .#{$prefix}--form-requirement { max-height: rem(200px); @@ -130,6 +131,9 @@ fill: $icon-01; cursor: pointer; z-index: 1; + // vertically center icon within parent container on IE11 + top: 50%; + transform: translateY(-50%); } .#{$prefix}--date-picker__icon ~ .#{$prefix}--date-picker__input { diff --git a/packages/components/src/components/radio-button/_radio-button.scss b/packages/components/src/components/radio-button/_radio-button.scss index 6b92b000c6de..14afdc9deef2 100644 --- a/packages/components/src/components/radio-button/_radio-button.scss +++ b/packages/components/src/components/radio-button/_radio-button.scss @@ -26,6 +26,11 @@ margin-top: rem(6px); } + // Remove spacing above collection of radio buttons if label is present + .#{$prefix}--label + .#{$prefix}--form-item .#{$prefix}--radio-button-group { + margin-top: 0; + } + // vertical radio button .#{$prefix}--radio-button-group--vertical { flex-direction: column; @@ -37,6 +42,7 @@ .#{$prefix}--radio-button__label { margin-right: 0; + line-height: carbon--mini-units(2.5); } .#{$prefix}--radio-button__label:not(:last-of-type) { diff --git a/packages/components/src/components/slider/_slider.scss b/packages/components/src/components/slider/_slider.scss index c0fea05b77ee..d1d4bbc40ce5 100644 --- a/packages/components/src/components/slider/_slider.scss +++ b/packages/components/src/components/slider/_slider.scss @@ -101,13 +101,13 @@ &:focus { // 20px / 14px = 1.4285714286 transform: translate(-50%, -50%) scale(1.4285714286); - box-shadow: inset 0 0 0 2px $interactive-01, inset 0 0 0 3px $ui-01; - background-color: $interactive-01; + box-shadow: inset 0 0 0 2px $interactive-04, inset 0 0 0 3px $ui-01; + background-color: $interactive-04; } &:active { transform: translate(-50%, -50%) scale(1.4285714286); - box-shadow: inset 0 0 0 2px $interactive-01; + box-shadow: inset 0 0 0 2px $interactive-04; } } @@ -130,7 +130,7 @@ } .#{$prefix}--slider__thumb:focus ~ .#{$prefix}--slider__filled-track { - background-color: $interactive-01; + background-color: $interactive-04; } // Disabled state diff --git a/packages/components/src/components/toggle/_toggle.scss b/packages/components/src/components/toggle/_toggle.scss index 3a9beb2dd4b0..d7be5bc525cc 100644 --- a/packages/components/src/components/toggle/_toggle.scss +++ b/packages/components/src/components/toggle/_toggle.scss @@ -315,6 +315,9 @@ margin-left: carbon--rem(56px); @include type-style('body-short-01'); user-select: none; + // top offset needed to vertically center absolutely positioned flex child in IE11 + top: 50%; + transform: translateY(-50%); } //---------------------------------------------- @@ -435,6 +438,19 @@ .#{$prefix}--toggle__check { fill: $disabled-02; } + + //---------------------------------------------- + // Skeleton + // --------------------------------------------- + + .#{$prefix}--toggle__label.#{$prefix}--skeleton { + flex-direction: column; + align-items: flex-start; + + .#{$prefix}--toggle__label-text { + margin-bottom: $carbon--spacing-03; + } + } } @include exports('toggle') { diff --git a/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js b/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js index 93f5c2e5207b..3003edf25be1 100644 --- a/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js +++ b/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js @@ -14,9 +14,7 @@ export default class BreadcrumbSkeleton extends React.Component { render() { const item = (
- -   - +  
); return ( diff --git a/packages/react/src/components/Breadcrumb/__tests__/__snapshots__/Breadcrumb.Skeleton-test.js.snap b/packages/react/src/components/Breadcrumb/__tests__/__snapshots__/Breadcrumb.Skeleton-test.js.snap index f062545bcca8..23512f872676 100644 --- a/packages/react/src/components/Breadcrumb/__tests__/__snapshots__/Breadcrumb.Skeleton-test.js.snap +++ b/packages/react/src/components/Breadcrumb/__tests__/__snapshots__/Breadcrumb.Skeleton-test.js.snap @@ -8,32 +8,29 @@ exports[`BreadcrumbSkeleton should render 1`] = `
-   - +
-   - +
-   - +
diff --git a/packages/react/src/components/ComboBox/ComboBox-story.js b/packages/react/src/components/ComboBox/ComboBox-story.js index a32aff37356f..f6f95713cee4 100644 --- a/packages/react/src/components/ComboBox/ComboBox-story.js +++ b/packages/react/src/components/ComboBox/ComboBox-story.js @@ -54,7 +54,7 @@ const itemToElement = item => { return (
{itemAsArray[0]} - {itemAsArray[1]} + {itemAsArray[1]}
); }; diff --git a/packages/react/src/components/ComboBox/ComboBox.js b/packages/react/src/components/ComboBox/ComboBox.js index 316279a041ad..6e7aba9d5617 100644 --- a/packages/react/src/components/ComboBox/ComboBox.js +++ b/packages/react/src/components/ComboBox/ComboBox.js @@ -320,8 +320,8 @@ export default class ComboBox extends React.Component { aria-labelledby={comboBoxA11yId} tabIndex="0" aria-disabled={disabled} - aria-controls={`${id}__menu`} - aria-owns={`${id}__menu`} + aria-controls={isOpen ? `${id}__menu` : null} + aria-owns={isOpen ? `${id}__menu` : null} aria-autocomplete="list" ref={this.textInput} {...rest} @@ -347,6 +347,7 @@ export default class ComboBox extends React.Component { )} + { ); expect(wrapper).toMatchSnapshot(); }); + it('should set an appropriate unique id', () => { + const wrapper = mount( + + ); + expect(typeof wrapper.find('Search').prop('id')).toBe('string'); + }); }); diff --git a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap index d58c1dcf3fed..13849aa5af7b 100644 --- a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap +++ b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap @@ -528,7 +528,9 @@ exports[`DataTable selection -- radio buttons should render 1`] = ` onSelect={[Function]} radio={true} > - + - + - + - + - + - + - {this.state.labelText} + {this.state.labelText} { if (matches(evt, [keys.Enter, keys.Space])) { @@ -106,7 +105,7 @@ export default function FileUploaderDropContainer(props) { } }} {...other}> -
+
{labelText} { const className = cx(`${prefix}--list-box__selection`, { [`${prefix}--tag--filter`]: selectionCount, @@ -29,13 +31,19 @@ const ListBoxSelection = ({ }); const handleOnClick = event => { event.stopPropagation(); + if (disabled) { + return; + } clearSelection(event); }; const handleOnKeyDown = event => { event.stopPropagation(); + if (disabled) { + return; + } // When a user hits ENTER, we'll clear the selection - if (event.keyCode === 13) { + if (match(event, keys.Enter)) { clearSelection(event); } }; @@ -44,7 +52,7 @@ const ListBoxSelection = ({
{ mount(); expect(mockProps.translateWithId).toHaveBeenCalledWith('clear.all'); }); + + it('should call clearSelection when clicked', () => { + const wrapper = mount(); + wrapper.simulate('click'); + expect(mockProps.clearSelection).toHaveBeenCalled(); + }); + + it('should call clearSelection on Enter keydown', () => { + const wrapper = mount(); + wrapper.simulate('keydown', { + key: 'Enter', + keyCode: 13, + which: 13, + }); + expect(mockProps.clearSelection).toHaveBeenCalled(); + }); + + it('should not clearSelection on click when disabled', () => { + const wrapper = mount(); + wrapper.simulate('click'); + expect(mockProps.clearSelection).toHaveBeenCalledTimes(0); + }); + + it('should not call clearSelection on Enter keydown when disabled', () => { + const wrapper = mount(); + wrapper.simulate('keydown', { + key: 'Enter', + keyCode: 13, + which: 13, + }); + expect(mockProps.clearSelection).toHaveBeenCalledTimes(0); + }); }); diff --git a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxField-test.js.snap b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxField-test.js.snap index cbcc02d46305..f38cfc0fa158 100644 --- a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxField-test.js.snap +++ b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxField-test.js.snap @@ -23,7 +23,7 @@ exports[`ListBoxField should render 1`] = ` onClick={[Function]} onKeyDown={[Function]} role="button" - tabIndex="0" + tabIndex={0} title="Clear selected item" > @@ -85,7 +85,7 @@ exports[`ListBoxField should set \`aria-owns\` based when expanded 1`] = ` onClick={[Function]} onKeyDown={[Function]} role="button" - tabIndex="0" + tabIndex={0} title="Clear selected item" > diff --git a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxSelection-test.js.snap b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxSelection-test.js.snap index 5813bf7356db..77dba1927ce6 100644 --- a/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxSelection-test.js.snap +++ b/packages/react/src/components/ListBox/__tests__/__snapshots__/ListBoxSelection-test.js.snap @@ -32,7 +32,7 @@ exports[`ListBoxSelection should render 1`] = ` onClick={[Function]} onKeyDown={[Function]} role="button" - tabIndex="0" + tabIndex={0} title="translation" > @@ -100,7 +100,7 @@ exports[`ListBoxSelection should render 2`] = ` onClick={[Function]} onKeyDown={[Function]} role="button" - tabIndex="0" + tabIndex={0} title="translation" > 3 diff --git a/packages/react/src/components/Modal/Modal-story.js b/packages/react/src/components/Modal/Modal-story.js index 119d51318561..9e25feda0a69 100644 --- a/packages/react/src/components/Modal/Modal-story.js +++ b/packages/react/src/components/Modal/Modal-story.js @@ -11,6 +11,7 @@ import { action } from '@storybook/addon-actions'; import { withKnobs, boolean, text } from '@storybook/addon-knobs'; import Modal from '../Modal'; +import TextInput from '../TextInput'; import { settings } from 'carbon-components'; const { prefix } = settings; @@ -74,4 +75,31 @@ storiesOf('Modal', module) `, }, } + ) + .add( + 'Trap Focus', + () => ( + <> + + + + + + ), + { + info: { + text: ` + Specify a selector for the primary element to focus when opening a modal. + `, + }, + } ); diff --git a/packages/react/src/components/Modal/Modal.js b/packages/react/src/components/Modal/Modal.js index 3e84f43f150d..b525b81976e6 100644 --- a/packages/react/src/components/Modal/Modal.js +++ b/packages/react/src/components/Modal/Modal.js @@ -220,12 +220,15 @@ export default class Modal extends Component { } initialFocus = focusContainerElement => { - const primaryFocusElement = focusContainerElement - ? focusContainerElement.querySelector(this.props.selectorPrimaryFocus) + const containerElement = focusContainerElement || this.innerModal.current; + const primaryFocusElement = containerElement + ? containerElement.querySelector(this.props.selectorPrimaryFocus) : null; + if (primaryFocusElement) { return primaryFocusElement; } + return this.button && this.button.current; }; diff --git a/packages/react/src/components/MultiSelect/FilterableMultiSelect.js b/packages/react/src/components/MultiSelect/FilterableMultiSelect.js index 960b699ed6c0..fa368bbfaaa8 100644 --- a/packages/react/src/components/MultiSelect/FilterableMultiSelect.js +++ b/packages/react/src/components/MultiSelect/FilterableMultiSelect.js @@ -358,6 +358,7 @@ export default class FilterableMultiSelect extends React.Component { clearSelection={clearSelection} selectionCount={selectedItem.length} translateWithId={translateWithId} + disabled={disabled} /> )} )} )} diff --git a/packages/react/src/components/OverflowMenu/OverflowMenu.js b/packages/react/src/components/OverflowMenu/OverflowMenu.js index bf45f2170e6b..0b858f483e46 100644 --- a/packages/react/src/components/OverflowMenu/OverflowMenu.js +++ b/packages/react/src/components/OverflowMenu/OverflowMenu.js @@ -535,7 +535,6 @@ class OverflowMenu extends Component { aria-expanded={this.state.open} className={overflowMenuClasses} onKeyDown={this.handleKeyPress} - onBlur={this.handleBlur} onClick={this.handleClick} aria-label={ariaLabel} id={id} diff --git a/packages/react/src/components/Toggle/Toggle-story.js b/packages/react/src/components/Toggle/Toggle-story.js index 9f475b019550..41599f86fbd5 100644 --- a/packages/react/src/components/Toggle/Toggle-story.js +++ b/packages/react/src/components/Toggle/Toggle-story.js @@ -12,10 +12,14 @@ import { withKnobs, text, boolean } from '@storybook/addon-knobs'; import Toggle from '../Toggle'; import ToggleSkeleton from '../Toggle/Toggle.Skeleton'; +const a11yProps = () => ({ + labelText: text('Label toggle input control (labelText)', ''), + ['aria-label']: text('ARIA label of the toggle (aria-label)', ''), +}); + const toggleProps = () => ({ + ...a11yProps(), className: 'some-class', - labelText: text('Label for toggle button input (labelText)', ''), - ['aria-label']: text('ARIA label of the toggle (aria-label)', ''), labelA: text('Label for untoggled state (labelA)', 'Off'), labelB: text('Label for toggled state (labelB)', 'On'), disabled: boolean('Disabled (disabled)', false), @@ -60,7 +64,7 @@ storiesOf('Toggle', module) }, } ) - .add('skeleton', () => , { + .add('skeleton', () => , { info: { text: ` Placeholder skeleton state to use when content is loading. diff --git a/packages/react/src/components/Toggle/Toggle.Skeleton.js b/packages/react/src/components/Toggle/Toggle.Skeleton.js index 64988791d254..8c63537625c3 100644 --- a/packages/react/src/components/Toggle/Toggle.Skeleton.js +++ b/packages/react/src/components/Toggle/Toggle.Skeleton.js @@ -5,14 +5,36 @@ * LICENSE file in the root directory of this source tree. */ +import PropTypes from 'prop-types'; import React from 'react'; import { settings } from 'carbon-components'; const { prefix } = settings; export default class ToggleSkeleton extends React.Component { + static propTypes = { + /** + * Provide an id that unique represents the underlying + */ + id: PropTypes.string, + + /** + * Provide the text that will be read by a screen reader when visiting this + * control + * `aria-label` is always required but will be null if `labelText` is also + * provided + */ + labelText: PropTypes.string, + ['aria-label']: PropTypes.string.isRequired, + }; + + static defaultProps = { + ['aria-label']: 'Toggle is loading', + }; + render() { - const { id } = this.props; + const { id, labelText } = this.props; + return (