Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.
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
104 changes: 64 additions & 40 deletions assets/js/base/components/dropdown-selector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
* External dependencies
*/
import PropTypes from 'prop-types';
import { useRef } from '@wordpress/element';
import { useCallback, useRef } from '@wordpress/element';
import classNames from 'classnames';
import Downshift from 'downshift';
import { __, sprintf } from '@wordpress/i18n';

/**
* Internal dependencies
Expand All @@ -13,33 +14,9 @@ import DropdownSelectorInput from './input';
import DropdownSelectorInputWrapper from './input-wrapper';
import DropdownSelectorMenu from './menu';
import DropdownSelectorSelectedChip from './selected-chip';
import DropdownSelectorSelectedValue from './selected-value';
import './style.scss';

/**
* State reducer for the downshift component.
* See: https://github.com/downshift-js/downshift#statereducer
*/
const stateReducer = ( state, changes ) => {
switch ( changes.type ) {
case Downshift.stateChangeTypes.keyDownEnter:
case Downshift.stateChangeTypes.clickItem:
return {
...changes,
highlightedIndex: state.highlightedIndex,
isOpen: true,
inputValue: '',
};
case Downshift.stateChangeTypes.blurInput:
case Downshift.stateChangeTypes.mouseUp:
return {
...changes,
inputValue: state.inputValue,
};
default:
return changes;
}
};

/**
* Component used to show an input box with a dropdown with suggestions.
*/
Expand All @@ -50,6 +27,7 @@ const DropdownSelector = ( {
inputLabel = '',
isDisabled = false,
isLoading = false,
multiple = false,
onChange = () => {},
options = [],
} ) => {
Expand All @@ -60,11 +38,33 @@ const DropdownSelector = ( {
'is-loading': isLoading,
} );

const focusInput = ( isOpen ) => {
if ( ! isOpen ) {
inputRef.current.focus();
}
};
/**
* State reducer for the downshift component.
* See: https://github.com/downshift-js/downshift#statereducer
*/
const stateReducer = useCallback(
( state, changes ) => {
switch ( changes.type ) {
case Downshift.stateChangeTypes.keyDownEnter:
case Downshift.stateChangeTypes.clickItem:
return {
...changes,
highlightedIndex: state.highlightedIndex,
isOpen: multiple,
inputValue: '',
};
case Downshift.stateChangeTypes.blurInput:
case Downshift.stateChangeTypes.mouseUp:
return {
...changes,
inputValue: state.inputValue,
};
default:
return changes;
}
},
[ multiple ]
);

return (
<Downshift
Expand All @@ -82,7 +82,12 @@ const DropdownSelector = ( {
isOpen,
openMenu,
} ) => (
<div className={ classes }>
<div
className={ classNames( classes, {
'is-multiple': multiple,
'is-single': ! multiple,
} ) }
>
{ /* eslint-disable-next-line jsx-a11y/label-has-for */ }
<label
{ ...getLabelProps( {
Expand All @@ -93,34 +98,53 @@ const DropdownSelector = ( {
</label>
<DropdownSelectorInputWrapper
isOpen={ isOpen }
onClick={ () => focusInput( isOpen ) }
onClick={ () => inputRef.current.focus() }
>
{ checked.map( ( value ) => {
const option = options.find(
( o ) => o.value === value
);
return (
const onRemoveItem = ( val ) => {
onChange( val );
inputRef.current.focus();
};
return multiple ? (
<DropdownSelectorSelectedChip
key={ value }
onRemoveItem={ ( val ) => {
onChange( val );
focusInput( isOpen );
} }
onRemoveItem={ onRemoveItem }
option={ option }
/>
) : (
<DropdownSelectorSelectedValue
key={ value }
onClick={ () => inputRef.current.focus() }
onRemoveItem={ onRemoveItem }
option={ option }
/>
);
} ) }
<DropdownSelectorInput
attributeLabel={ attributeLabel }
checked={ checked }
getInputProps={ getInputProps }
inputRef={ inputRef }
isDisabled={ isDisabled }
onFocus={ openMenu }
onRemoveItem={ ( val ) => {
onChange( val );
focusInput( isOpen );
inputRef.current.focus();
} }
placeholder={
checked.length > 0 && multiple
? null
: sprintf(
// Translators: %s attribute name.
__(
'Any %s',
'woo-gutenberg-products-block'
),
attributeLabel
)
}
value={ inputValue }
/>
</DropdownSelectorInputWrapper>
Expand Down
16 changes: 2 additions & 14 deletions assets/js/base/components/dropdown-selector/input.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';

const DropdownSelectorInput = ( {
attributeLabel,
checked,
getInputProps,
inputRef,
isDisabled,
onFocus,
onRemoveItem,
placeholder,
value,
} ) => {
return (
Expand All @@ -29,14 +24,7 @@ const DropdownSelectorInput = ( {
onRemoveItem( checked[ checked.length - 1 ] );
}
},
placeholder:
checked.length === 0
? sprintf(
// Translators: %s attribute name.
__( 'Any %s', 'woo-gutenberg-products-block' ),
attributeLabel
)
: null,
placeholder,
} ) }
/>
);
Expand Down
7 changes: 4 additions & 3 deletions assets/js/base/components/dropdown-selector/selected-chip.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ const DropdownSelectorSelectedChip = ( { onRemoveItem, option } ) => {
return (
<button
className="wc-block-dropdown-selector__selected-chip"
onClick={ ( e ) => {
e.stopPropagation();
onClick={ () => {
onRemoveItem( option.value );
} }
onKeyDown={ ( e ) => {
Expand All @@ -21,7 +20,9 @@ const DropdownSelectorSelectedChip = ( { onRemoveItem, option } ) => {
option.name
) }
>
{ option.label }
<span className="wc-block-dropdown-selector__selected-chip__label">
{ option.label }
</span>
<span className="wc-block-dropdown-selector__selected-chip__remove">
𝘅
</span>
Expand Down
54 changes: 54 additions & 0 deletions assets/js/base/components/dropdown-selector/selected-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useEffect, useRef } from '@wordpress/element';

const DropdownSelectorSelectedValue = ( { onClick, onRemoveItem, option } ) => {
const labelRef = useRef( null );

useEffect( () => {
labelRef.current.focus();
}, [ labelRef ] );

return (
<div className="wc-block-dropdown-selector__selected-value">
<button
ref={ labelRef }
className="wc-block-dropdown-selector__selected-value__label"
onClick={ ( e ) => {
e.stopPropagation();
onClick( option.value );
} }
aria-label={ sprintf(
__(
'Replace current %s filter',
'woo-gutenberg-products-block'
),
option.name
) }
>
{ option.label }
</button>
<button
className="wc-block-dropdown-selector__selected-value__remove"
onClick={ () => {
onRemoveItem( option.value );
} }
onKeyDown={ ( e ) => {
if ( e.key === 'Backspace' || e.key === 'Delete' ) {
onRemoveItem( option.value );
}
} }
aria-label={ sprintf(
__( 'Remove %s filter', 'woo-gutenberg-products-block' ),
option.name
) }
>
𝘅
</button>
</div>
);
};

export default DropdownSelectorSelectedValue;
Loading