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

fix(#3299): SearchAutocomplete/SearchWithin support icon prop override #3300

Merged
merged 6 commits into from Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -31,7 +31,6 @@ governing permissions and limitations under the License.
}

&.is-quiet .spectrum-Search-input {
padding-inline-start: var(--spectrum-search-quiet-padding-left);
padding-inline-end: var(--spectrum-search-quiet-padding-right);
}

Expand Down
Expand Up @@ -10,6 +10,7 @@
* governing permissions and limitations under the License.
*/

import Filter from '@spectrum-icons/workflow/Filter';
import {generatePowerset} from '@react-spectrum/story-utils';
import {Grid, repeat} from '@react-spectrum/layout';
import {Item, SearchAutocomplete} from '../';
Expand Down Expand Up @@ -128,3 +129,11 @@ PropLabelSide.args = {...PropDefaults.args, labelPosition: 'side'};
export const PropCustomWidth = Template.bind({});
PropCustomWidth.storyName = 'custom width';
PropCustomWidth.args = {...PropDefaults.args, width: 'size-1600'};

export const PropIconFilter = Template.bind({});
PropIconFilter.storyName = 'icon: Filter';
PropIconFilter.args = {...PropDefaults.args, icon: <Filter />};

export const PropIconNull = Template.bind({});
PropIconNull.storyName = 'icon: null';
PropIconNull.args = {...PropDefaults.args, icon: null};
Expand Up @@ -22,4 +22,4 @@ const meta: Meta = {

export default meta;

export {PropDefaults, PropInputValue, PropAriaLabelled, PropLabelEnd, PropLabelSide, PropCustomWidth} from './SearchAutocomplete.chromatic';
export {PropDefaults, PropInputValue, PropAriaLabelled, PropLabelEnd, PropLabelSide, PropCustomWidth, PropIconFilter, PropIconNull} from './SearchAutocomplete.chromatic';
174 changes: 91 additions & 83 deletions packages/@react-spectrum/autocomplete/src/MobileSearchAutocomplete.tsx
Expand Up @@ -19,8 +19,8 @@ import {ComboBoxState, useComboBoxState} from '@react-stately/combobox';
import {DismissButton} from '@react-aria/overlays';
import {Field} from '@react-spectrum/label';
import {FocusableRef, ValidationState} from '@react-types/shared';
import {FocusRing, FocusScope} from '@react-aria/focus';
import {focusSafely} from '@react-aria/focus';
import {FocusScope, useFocusRing} from '@react-aria/focus';
// @ts-ignore
import intlMessages from '../intl/*.json';
import {ListBoxBase, useListBoxLayout} from '@react-spectrum/listbox';
Expand Down Expand Up @@ -70,7 +70,7 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
selectedKey: undefined,
defaultSelectedKey: undefined
});

let buttonRef = useRef<HTMLElement>();
let domRef = useFocusableRef(ref, buttonRef);
let {triggerProps, overlayProps} = useOverlayTrigger({type: 'listbox'}, state, buttonRef);
Expand Down Expand Up @@ -99,7 +99,7 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
ref={domRef}
includeNecessityIndicatorInAccessibilityName>
<SearchAutocompleteButton
{...mergeProps(triggerProps, fieldProps, {autoFocus: props.autoFocus})}
{...mergeProps(triggerProps, fieldProps, {autoFocus: props.autoFocus, icon: props.icon})}
ref={buttonRef}
isQuiet={isQuiet}
isDisabled={isDisabled}
Expand All @@ -124,6 +124,7 @@ export const MobileSearchAutocomplete = React.forwardRef(function MobileSearchAu
});

interface SearchAutocompleteButtonProps extends AriaButtonProps {
icon?: ReactElement,
isQuiet?: boolean,
isDisabled?: boolean,
isReadOnly?: boolean,
Expand All @@ -137,7 +138,12 @@ interface SearchAutocompleteButtonProps extends AriaButtonProps {
}

const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteButton(props: SearchAutocompleteButtonProps, ref: RefObject<HTMLElement>) {
let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let {
icon = searchIcon,
isQuiet,
isDisabled,
isReadOnly,
Expand All @@ -148,25 +154,23 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
children,
style,
className
} = props;
} = props;
let formatMessage = useMessageFormatter(intlMessages);
let valueId = useId();
let invalidId = useId();
let validationIcon = validationState === 'invalid'
? <AlertMedium id={invalidId} aria-label={formatMessage('invalid')} />
: <CheckmarkMedium />;

let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let icon = React.cloneElement(searchIcon, {
UNSAFE_className: classNames(
textfieldStyles,
'spectrum-Textfield-icon'
),
size: 'S'
});
if (icon) {
icon = React.cloneElement(icon, {
UNSAFE_className: classNames(
textfieldStyles,
'spectrum-Textfield-icon'
),
size: 'S'
});
}

let clearButton = (
<ClearButton
Expand Down Expand Up @@ -199,6 +203,7 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
});

let {hoverProps, isHovered} = useHover({});
let {isFocused, isFocusVisible, focusProps} = useFocusRing();
let {buttonProps} = useButton({
...props,
'aria-labelledby': [
Expand All @@ -211,88 +216,88 @@ const SearchAutocompleteButton = React.forwardRef(function SearchAutocompleteBut
}, ref);

return (
<FocusRing
focusClass={classNames(styles, 'is-focused')}
focusRingClass={classNames(styles, 'focus-ring')}>
<div
{...mergeProps(hoverProps, focusProps, buttonProps)}
aria-haspopup="dialog"
ref={ref as RefObject<HTMLDivElement>}
style={{...style, outline: 'none'}}
className={
classNames(
styles,
'spectrum-InputGroup',
{
'spectrum-InputGroup--quiet': isQuiet,
'is-disabled': isDisabled,
'spectrum-InputGroup--invalid': validationState === 'invalid',
'is-hovered': isHovered,
'is-focused': isFocused,
'focus-ring': isFocusVisible
},
classNames(
searchAutocompleteStyles,
'mobile-searchautocomplete'
),
className
)
}>
<div
{...mergeProps(hoverProps, buttonProps)}
aria-haspopup="dialog"
ref={ref as RefObject<HTMLDivElement>}
style={{...style, outline: 'none'}}
className={
classNames(
styles,
'spectrum-InputGroup',
textfieldStyles,
'spectrum-Textfield',
{
'spectrum-InputGroup--quiet': isQuiet,
'is-disabled': isDisabled,
'spectrum-InputGroup--invalid': validationState === 'invalid',
'is-hovered': isHovered
'spectrum-Textfield--invalid': validationState === 'invalid',
'spectrum-Textfield--valid': validationState === 'valid',
'spectrum-Textfield--quiet': isQuiet
},
classNames(
searchAutocompleteStyles,
'mobile-searchautocomplete'
),
className
searchStyles,
'spectrum-Search',
{
'is-disabled': isDisabled,
'is-quiet': isQuiet,
'spectrum-Search--invalid': validationState === 'invalid',
'spectrum-Search--valid': validationState === 'valid'
}
)
)
}>
<div
className={
classNames(
textfieldStyles,
'spectrum-Textfield',
'spectrum-Textfield-input',
{
'spectrum-Textfield--invalid': validationState === 'invalid',
'spectrum-Textfield--valid': validationState === 'valid',
'spectrum-Textfield--quiet': isQuiet
'spectrum-Textfield-inputIcon': !!icon,
'is-hovered': isHovered,
'is-placeholder': isPlaceholder,
'is-disabled': isDisabled,
'is-quiet': isQuiet,
'is-focused': isFocused,
'focus-ring': isFocusVisible
},
classNames(
searchStyles,
'spectrum-Search',
{
'is-disabled': isDisabled,
'is-quiet': isQuiet,
'spectrum-Search--invalid': validationState === 'invalid',
'spectrum-Search--valid': validationState === 'valid'
}
'spectrum-Search-input'
)
)
}>
<div
{icon}
<span
id={valueId}
className={
classNames(
textfieldStyles,
'spectrum-Textfield-input',
'spectrum-Textfield-inputIcon',
{
'is-hovered': isHovered,
'is-placeholder': isPlaceholder,
'is-disabled': isDisabled,
'is-quiet': isQuiet
},
classNames(
searchStyles,
'spectrum-Search-input'
)
searchAutocompleteStyles,
'mobile-value'
)
}>
{icon}
<span
id={valueId}
className={
classNames(
searchAutocompleteStyles,
'mobile-value'
)
}>
{children}
</span>
</div>
{validationState ? validation : null}
{(inputValue !== '' || validationState != null) && !isReadOnly && clearButton}
{children}
</span>
</div>
{validationState ? validation : null}
{(inputValue !== '' || validationState != null) && !isReadOnly && clearButton}
</div>
</FocusRing>
</div>
);
});

Expand All @@ -304,9 +309,14 @@ interface SearchAutocompleteTrayProps extends SpectrumSearchAutocompleteProps<un
}

function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let {
// completionMode = 'suggest',
state,
icon = searchIcon,
isDisabled,
validationState,
label,
Expand Down Expand Up @@ -450,17 +460,15 @@ function SearchAutocompleteTray(props: SearchAutocompleteTrayProps) {
}
};

let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let icon = React.cloneElement(searchIcon, {
UNSAFE_className: classNames(
textfieldStyles,
'spectrum-Textfield-icon'
),
size: 'S'
});
if (icon) {
icon = React.cloneElement(icon, {
UNSAFE_className: classNames(
textfieldStyles,
'spectrum-Textfield-icon'
),
size: 'S'
});
}

return (
<FocusScope restoreFocus contain>
Expand Down
13 changes: 7 additions & 6 deletions packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx
Expand Up @@ -87,7 +87,7 @@ const SearchAutocompleteBase = React.forwardRef(function SearchAutocompleteBase<
}
);
let layout = useListBoxLayout(state);

let {inputProps, listBoxProps, labelProps, clearButtonProps} = useSearchAutocomplete(
{
...props,
Expand Down Expand Up @@ -198,7 +198,12 @@ interface SearchAutocompleteInputProps extends SpectrumSearchAutocompleteProps<u
}

const SearchAutocompleteInput = React.forwardRef(function SearchAutocompleteInput(props: SearchAutocompleteInputProps, ref: RefObject<HTMLElement>) {
let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let {
icon = searchIcon,
isQuiet,
isDisabled,
isReadOnly,
Expand Down Expand Up @@ -233,10 +238,6 @@ const SearchAutocompleteInput = React.forwardRef(function SearchAutocompleteInpu
)} />
);

let searchIcon = (
<Magnifier data-testid="searchicon" />
);

let clearButton = (
<ClearButton
{...clearButtonProps}
Expand Down Expand Up @@ -324,7 +325,7 @@ const SearchAutocompleteInput = React.forwardRef(function SearchAutocompleteInpu
validationState={validationState}
isLoading={showLoading && (isOpen || menuTrigger === 'manual' || loadingState === 'loading')}
loadingIndicator={loadingState != null && loadingCircle}
icon={searchIcon}
icon={icon}
wrapperChildren={(inputValue !== '' && !isReadOnly) && clearButton} />
</div>
</FocusRing>
Expand Down
Expand Up @@ -12,6 +12,7 @@
*/

import {action} from '@storybook/addon-actions';
import Filter from '@spectrum-icons/workflow/Filter';
import {Flex} from '@react-spectrum/layout';
import {Item, SearchAutocomplete} from '@react-spectrum/autocomplete';
import {mergeProps} from '@react-aria/utils';
Expand Down Expand Up @@ -183,3 +184,9 @@ customWidth6000.storyName = 'custom width: size-6000';

export const customOnSubmit = (props) => <CustomOnSubmit {...props} />;
customOnSubmit.storyName = 'custom onSubmit';

export const iconFilter = (props) => <Default {...props} icon={<Filter />} />;
iconFilter.storyName = 'icon: Filter';

export const iconNull = (props) => <Default {...props} icon={null} />;
iconNull.storyName = 'icon: null';