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

Components: Add isFocusable state to Button #19337

Merged
merged 8 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion packages/block-editor/src/components/inserter/test/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ describe( 'InserterMenu', () => {
TestUtils.Simulate.click( layoutTab );

const disabledBlocks = element.querySelectorAll(
'.block-editor-block-types-list__item[disabled]'
'.block-editor-block-types-list__item[disabled], .block-editor-block-types-list__item[aria-disabled="true"]'
);

expect( disabledBlocks ).toHaveLength( 1 );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Basic rendering should display with required props 1`] = `"<span><div tabindex=\\"-1\\"><div><div><div class=\\"components-popover block-editor-link-control\\"><div class=\\"components-popover__content\\" tabindex=\\"-1\\"><div class=\\"block-editor-link-control__popover-inner\\"><div class=\\"block-editor-link-control__search\\"><form><div class=\\"components-base-control block-editor-url-input block-editor-link-control__search-input\\"><div class=\\"components-base-control__field\\"><input type=\\"text\\" aria-label=\\"URL\\" required=\\"\\" placeholder=\\"Search or type url\\" role=\\"combobox\\" aria-expanded=\\"false\\" aria-autocomplete=\\"list\\" aria-owns=\\"block-editor-url-input-suggestions-0\\" value=\\"\\"></div></div><button type=\\"reset\\" disabled=\\"\\" class=\\"components-button block-editor-link-control__search-reset has-icon\\" aria-label=\\"Reset\\"><svg aria-hidden=\\"true\\" role=\\"img\\" focusable=\\"false\\" xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"20\\" height=\\"20\\" viewBox=\\"0 0 20 20\\" class=\\"dashicon dashicons-no-alt\\"><path d=\\"M14.95 6.46L11.41 10l3.54 3.54-1.41 1.41L10 11.42l-3.53 3.53-1.42-1.42L8.58 10 5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z\\"></path></svg></button></form></div></div></div></div></div></div></div></span>"`;
exports[`Basic rendering should display with required props 1`] = `"<span><div tabindex=\\"-1\\"><div><div><div class=\\"components-popover block-editor-link-control\\"><div class=\\"components-popover__content\\" tabindex=\\"-1\\"><div class=\\"block-editor-link-control__popover-inner\\"><div class=\\"block-editor-link-control__search\\"><form><div class=\\"components-base-control block-editor-url-input block-editor-link-control__search-input\\"><div class=\\"components-base-control__field\\"><input type=\\"text\\" aria-label=\\"URL\\" required=\\"\\" placeholder=\\"Search or type url\\" role=\\"combobox\\" aria-expanded=\\"false\\" aria-autocomplete=\\"list\\" aria-owns=\\"block-editor-url-input-suggestions-0\\" value=\\"\\"></div></div><button type=\\"reset\\" aria-disabled=\\"true\\" class=\\"components-button block-editor-link-control__search-reset has-icon\\" aria-label=\\"Reset\\"><svg aria-hidden=\\"true\\" role=\\"img\\" focusable=\\"false\\" xmlns=\\"http://www.w3.org/2000/svg\\" width=\\"20\\" height=\\"20\\" viewBox=\\"0 0 20 20\\" class=\\"dashicon dashicons-no-alt\\"><path d=\\"M14.95 6.46L11.41 10l3.54 3.54-1.41 1.41L10 11.42l-3.53 3.53-1.42-1.42L8.58 10 5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z\\"></path></svg></button></form></div></div></div></div></div></div></div></span>"`;
1 change: 1 addition & 0 deletions packages/components/src/button/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Name | Type | Default | Description
`isPrimary` | `bool` | `false` | Renders a primary button style.
`isTertiary` | `bool` | `false` | Renders a text-based button style.
`isDestructive` | `bool` | `false` | Renders a red text-based button style to indicate destructive behavior.
`isFocusable` | `bool` | `false` | Whether the button can be focusable even when `disabled`.
`isLarge` | `bool` | `false` | Increases the size of the button.
`isSmall` | `bool` | `false` | Decreases the size of the button.
`isPressed` | `bool` | `false` | Renders a pressed button style.
Expand Down
26 changes: 23 additions & 3 deletions packages/components/src/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import { forwardRef } from '@wordpress/element';
import Tooltip from '../tooltip';
import Icon from '../icon';

const disabledEventsOnDisabledButton = [
'onMouseDown',
'onClick',
];

export function Button( props, ref ) {
const {
href,
Expand All @@ -30,6 +35,7 @@ export function Button( props, ref ) {
isSecondary,
isLink,
isDestructive,
isFocusable = true,
className,
disabled,
icon,
Expand Down Expand Up @@ -62,13 +68,27 @@ export function Button( props, ref ) {
'has-icon': !! icon,
} );

const Tag = href !== undefined && ! disabled ? 'a' : 'button';
const trulyDisabled = disabled && ! isFocusable;
const Tag = href !== undefined && ! trulyDisabled ? 'a' : 'button';
const tagProps = Tag === 'a' ?
{ href, target } :
{ type: 'button', disabled, 'aria-pressed': isPressed };
{ type: 'button', disabled: trulyDisabled, 'aria-pressed': isPressed };

if ( disabled && isFocusable ) {
// In this case, the button will be disabled, but still focusable and
// perceivable by screen reader users.
tagProps[ 'aria-disabled' ] = true;

for ( const disabledEvent of disabledEventsOnDisabledButton ) {
additionalProps[ disabledEvent ] = ( event ) => {
event.stopPropagation();
event.preventDefault();
};
}
}

// Should show the tooltip if...
const shouldShowTooltip = ! disabled && (
const shouldShowTooltip = ! trulyDisabled && (
// an explicit tooltip is passed or...
( showTooltip && label ) ||
// there's a shortcut or...
Expand Down
15 changes: 15 additions & 0 deletions packages/components/src/button/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ export const icon = () => {
);
};

export const disabledIcon = () => {
const usedIcon = text( 'Icon', 'ellipsis' );
const label = text( 'Label', 'More' );
const size = number( 'Size' );

return (
<Button
icon={ usedIcon }
label={ label }
iconSize={ size }
disabled
/>
);
};

export const groupedIcons = () => {
const GroupContainer = ( { children } ) => (
<div style={ { display: 'inline-flex' } }>{ children }</div>
Expand Down
10 changes: 5 additions & 5 deletions packages/components/src/button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

}

&:active:enabled {
&:not([aria-disabled="true"]):active:enabled {
background: #f3f5f6;
color: color(theme(button) shade(5%));
border-color: #7e8993;
Expand Down Expand Up @@ -87,7 +87,7 @@
0 0 0 3px color(theme(button));
}

&:active:enabled {
&:not([aria-disabled="true"]):active:enabled {
background: color(theme(button) shade(20%));
border-color: color(theme(button) shade(20%));
color: $white;
Expand Down Expand Up @@ -149,7 +149,7 @@
height: auto;

&:not(:disabled):not([aria-disabled="true"]):hover,
&:active {
&:not([aria-disabled="true"]):active {
color: #00a0d2;
}

Expand All @@ -166,7 +166,7 @@
color: $alert-red;
}

&:active {
&:not([aria-disabled="true"]):active {
color: inherit;
}

Expand Down Expand Up @@ -212,7 +212,7 @@
outline: none;
}

&:active:focus:enabled {
&:not([aria-disabled="true"]):active:focus:enabled {
box-shadow: none;
}
Copy link
Contributor

@youknowriad youknowriad Dec 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styles changes make me a bit nervous :P because we have some many button styles ovverrides in the UI that I know this can trigger specificity issues and should be tested properly.


Expand Down
13 changes: 10 additions & 3 deletions packages/components/src/button/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe( 'Button', () => {
expect( button.hasClass( 'is-primary' ) ).toBe( false );
expect( button.hasClass( 'is-pressed' ) ).toBe( false );
expect( button.prop( 'disabled' ) ).toBeUndefined();
expect( button.prop( 'aria-disabled' ) ).toBeUndefined();
expect( button.prop( 'type' ) ).toBe( 'button' );
expect( button.type() ).toBe( 'button' );
} );
Expand Down Expand Up @@ -53,11 +54,17 @@ describe( 'Button', () => {
expect( button.hasClass( 'is-pressed' ) ).toBe( true );
} );

it( 'should add a disabled prop to the button', () => {
const button = shallow( <Button disabled /> );
it( 'should add a disabled prop to the button when isFocusable is false', () => {
const button = shallow( <Button disabled isFocusable={ false } /> );
expect( button.prop( 'disabled' ) ).toBe( true );
} );

it( 'should add only aria-disabled attribute when disabled is true', () => {
const button = shallow( <Button disabled /> );
expect( button.prop( 'disabled' ) ).toBe( false );
expect( button.prop( 'aria-disabled' ) ).toBe( true );
} );

it( 'should not poss the prop target into the element', () => {
const button = shallow( <Button target="_blank" /> );
expect( button.prop( 'target' ) ).toBeUndefined();
Expand Down Expand Up @@ -142,7 +149,7 @@ describe( 'Button', () => {
} );

it( 'should become a button again when disabled is supplied', () => {
const button = shallow( <Button href="https://wordpress.org/" disabled /> );
const button = shallow( <Button href="https://wordpress.org/" disabled isFocusable={ false } /> );

expect( button.type() ).toBe( 'button' );
} );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe( 'Editing modes (visual/HTML)', () => {
expect( noBlocksElement ).not.toBeNull();

// The inserter is disabled
const disabledInserter = await page.$( '.block-editor-inserter > button:disabled' );
const disabledInserter = await page.$( '.block-editor-inserter > button:disabled, .block-editor-inserter > button[aria-disabled="true"]' );
expect( disabledInserter ).not.toBeNull();
} );
} );
2 changes: 1 addition & 1 deletion packages/e2e-tests/specs/editor/various/preview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe( 'Preview', () => {

// Disabled until content present.
const isPreviewDisabled = await editorPage.$$eval(
'.editor-post-preview:not( :disabled )',
'.editor-post-preview:not( :disabled ):not( [aria-disabled="true"] )',
( enabledButtons ) => ! enabledButtons.length,
);
expect( isPreviewDisabled ).toBe( true );
Expand Down
47 changes: 42 additions & 5 deletions storybook/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -555,24 +555,61 @@ exports[`Storyshots Components|Button Default 1`] = `

exports[`Storyshots Components|Button Disabled 1`] = `
<button
aria-disabled={true}
className="components-button"
disabled={true}
disabled={false}
onClick={[Function]}
onMouseDown={[Function]}
type="button"
>
Disabled Button
</button>
`;

exports[`Storyshots Components|Button Disabled Link 1`] = `
exports[`Storyshots Components|Button Disabled Icon 1`] = `
<button
className="components-button"
disabled={true}
aria-disabled={true}
aria-label="More"
className="components-button has-icon"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
type="button"
>
Disabled Link Button
<svg
aria-hidden="true"
className="dashicon dashicons-ellipsis"
focusable="false"
height={20}
role="img"
viewBox="0 0 20 20"
width={20}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 10c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm12-2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-7 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
/>
</svg>
</button>
`;

exports[`Storyshots Components|Button Disabled Link 1`] = `
<a
aria-disabled={true}
className="components-button"
href="https://wordpress.org/"
onClick={[Function]}
onMouseDown={[Function]}
target="_blank"
>
Disabled Link Button
</a>
`;

exports[`Storyshots Components|Button Grouped Icons 1`] = `
<div
style={
Expand Down