Skip to content

Commit

Permalink
Try addinig a keyboard navigatble font size selector with visual feed…
Browse files Browse the repository at this point in the history
…back
  • Loading branch information
youknowriad committed Sep 20, 2019
1 parent a0a1a79 commit 059790e
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 13 deletions.
11 changes: 4 additions & 7 deletions packages/components/src/font-size-picker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { __ } from '@wordpress/i18n';
*/
import Button from '../button';
import RangeControl from '../range-control';
import SelectControl from '../select-control';
import FontSizePickerSelect from './select';

function getSelectValueFromFontSize( fontSizes, value ) {
if ( value ) {
Expand All @@ -22,7 +22,7 @@ function getSelectValueFromFontSize( fontSizes, value ) {

function getSelectOptions( optionsArray ) {
return [
...optionsArray.map( ( option ) => ( { value: option.slug, label: option.name } ) ),
...optionsArray.map( ( option ) => ( { value: option.slug, label: option.name, size: option.size } ) ),
{ value: 'custom', label: __( 'Custom' ) },
];
}
Expand Down Expand Up @@ -66,13 +66,10 @@ function FontSizePicker( {
</legend>
<div className="components-font-size-picker__controls">
{ ( fontSizes.length > 0 ) &&
<SelectControl
className={ 'components-font-size-picker__select' }
label={ 'Choose preset' }
hideLabelFromVision={ true }
<FontSizePickerSelect
value={ currentSelectValue }
onChange={ onSelectChangeValue }
options={ getSelectOptions( fontSizes ) }
fontSizes={ getSelectOptions( fontSizes ) }
/>
}
{ ( ! withSlider && ! disableCustomFontSizes ) &&
Expand Down
204 changes: 204 additions & 0 deletions packages/components/src/font-size-picker/select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/**
* External dependencies
*/
import { map } from 'lodash';

/**
* WordPress dependencies
*/
import { __, _x, sprintf } from '@wordpress/i18n';
import { withInstanceId } from '@wordpress/compose';
import { useState, useRef, useCallback, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import {
Dashicon,
BaseControl,
Button,
NavigableMenu,
Dropdown,
} from '../';

function FontSizePickerSelect( {
fontSizes = [],
onChange,
value,
} ) {
const currentFont = fontSizes.find( ( font ) => font.value === value );
const currentFontName =
( currentFont && currentFont.name ) ||
( ! value && _x( 'Normal', 'font size name' ) ) ||
_x( 'Custom', 'font size name' );
const currentFontSlug = currentFont ? currentFont.slug : '';

const onChangeValue = ( event ) => {
const newValue = event.target.value;
if ( newValue === '' ) {
onChange( undefined );
return;
}
onChange( Number( newValue ) );
};

/**
* CHANGES
*/
// Work-around to focus active item
const popoverRef = useRef( null );
const focusActiveItem = useCallback( () => {
// Hack work-arounds to control focus timing...
window.requestAnimationFrame( () => {
const { current } = popoverRef;
if ( ! current ) {
return;
}
const node = current.querySelector( '[aria-selected="true"]' );
node.blur();
window.requestAnimationFrame( () => {
if ( node ) {
// Hack work-arounds to control focus timing...
node.focus();
}
} );
} );
}, [] );

useEffect( () => {
focusActiveItem();
}, [ focusActiveItem, value ] );

// Work around to manage + force open state outside of Dropdown
const [ isOpen, setOpen ] = useState( false );
const openDropdown = () => {
setOpen( true );
};
const closeDropdown = () => setOpen( false );
const handleOnToggle = ( nextOpen ) => {
setOpen( nextOpen );
if ( nextOpen ) {
focusActiveItem();
}
};

// Work around to force dropdown to open via Button
const handleOnButtonKeyDown = ( event ) => {
const { key } = event;
switch ( key ) {
case 'ArrowUp':
openDropdown();
break;
case 'ArrowDown':
openDropdown();
break;
default:
}
};

// Work around to prevent scrolling.
// Need to adjust navigable-content/container
// https://github.com/WordPress/gutenberg/blob/master/packages/components/src/navigable-container/container.js#L89
const handleOnKeyDown = ( event ) => {
// event.preventDefault();
const { key } = event;
switch ( key ) {
case 'ArrowDown':
event.preventDefault();
break;
case 'ArrowUp':
event.preventDefault();
break;
default:
break;
}
};

// Improve voiceover consistency compared to native select
const buttonRole = 'combobox';
const ariaActiveDescendant = `item-${ currentFontSlug }`;
const ariaHasPopup = 'listbox';
const ariaProps = {
role: buttonRole,
'aria-haspopup': ariaHasPopup,
'aria-activedescendant': ariaActiveDescendant,
};

/**
* / CHANGES
*/

return (
<BaseControl className="components-font-size-picker__select">
<Dropdown
className="components-font-size-picker__select-dropdown"
contentClassName="components-font-size-picker__select-dropdown-content"
position="bottom"
focusOnMount="container"
onChange={ onChangeValue }
onOpen={ openDropdown }
onClose={ closeDropdown }
onToggle={ handleOnToggle }
isOpen={ isOpen }
renderToggle={ ( { onToggle } ) => (
<Button
className="components-font-size-picker__select-selector"
isLarge
onClick={ onToggle }
aria-expanded={ isOpen }
aria-label={ sprintf( __( 'Customize font size. %s' ), currentFontName ) }
onKeyDown={ handleOnButtonKeyDown }
{ ...ariaProps }
>
{ currentFontName }
</Button>
) }
renderContent={ () => {
return (
<NavigableMenu
role="listbox"
aria-label="Choose Font Size"
onKeyDown={ handleOnKeyDown }
>
<div ref={ popoverRef }>
{ map( fontSizes, ( option ) => {
const isSelected = value === option.value;
const optionLabel = isSelected ? `${ option.label } (Selected)` : option.label;
const itemId = `item-${ option.value }`;
const itemRole = 'option';
const labelRole = 'presentation';

return (
<Button
key={ option.value }
onClick={ () => {
onChange( option.value );
closeDropdown();
} }
className={ `is-font-${ option.value }` }
role={ itemRole }
id={ itemId }
aria-label={ optionLabel }
aria-selected={ isSelected }
>
{ isSelected && <Dashicon icon="saved" /> }
<span
className="components-font-size-picker__select-dropdown-text-size"
style={ option.size ? { fontSize: option.size } : {} }
role={ labelRole }
>
{ option.label }
</span>
</Button>
);
} ) }
</div>
</NavigableMenu>
);
} }
/>
</BaseControl>
);
}

export default withInstanceId( FontSizePickerSelect );
63 changes: 63 additions & 0 deletions packages/components/src/font-size-picker/select.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.components-font-size-picker__select {
// Need to override a generic rule
// that can apply to all controls in the sidebar
// Ideally that generic rule shouldn't target
// this select in the first place.
margin-bottom: 0 !important;

.components-base-control__field {
margin-bottom: 0;
}
}

.components-font-size-picker__select-dropdown {
display: inline-block;
width: 80px;
}

.components-font-size-picker__select-dropdown-content button {
display: block;
width: 100%;
text-align: left;
position: relative;
padding: 10px 5px;
line-height: 1;
}

.components-font-size-picker__select-dropdown-content button:hover {
background: #eee !important;
}

.components-font-size-picker__select-dropdown-content button.highlighted {
background: #eee !important;
}

.components-font-size-picker__select-dropdown-content button svg {
position: absolute;
left: 5px;
top: 50%;
transform: translateY(-50%);
}

.components-font-size-picker__select-dropdown-text-size {
margin-left: 30px;
display: block;
}

.components-font-size-picker__select-selector {
background: #fff !important;
padding: 0 !important;
padding: 0 10px !important;
}

.components-font-size-picker__select-selector::after {
content: "";
position: relative;
right: -10px;
padding: 0 8px;
}

.components-font-size-picker__select-selector:focus {
border: 1px solid $blue-medium-focus !important;
outline: none !important;
}
8 changes: 2 additions & 6 deletions packages/components/src/font-size-picker/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "./select.scss";

.components-font-size-picker__controls {
max-width: $sidebar-width - ( 2 * $panel-padding );
display: flex;
Expand All @@ -19,12 +21,6 @@
}
}

// needed to override CSS set in https://github.com/WordPress/gutenberg/blob/9c438f93a7215d50d1efc0492c308e4cbaa59c52/packages/edit-post/src/components/sidebar/settings-sidebar/style.scss#L6
.components-font-size-picker__select.components-font-size-picker__select.components-font-size-picker__select.components-font-size-picker__select,
.components-font-size-picker__select .components-base-control__field {
margin-bottom: 0;
}

.components-font-size-picker__custom-input {
.components-range-control__slider + .dashicon {
width: 30px;
Expand Down

0 comments on commit 059790e

Please sign in to comment.