Fix dropdown select arrow clicks bugs (#2301)

* Fix dropdown arrow click

* Fix multi-select dropdown arrow click

* Update changelog

* Update test snapshots of select inputs

* Grey out select dropdowns bottom arrow

* Prevent clearing input value in multiselect on item select

Co-authored-by: Patryk Andrzejewski <vox3r69@gmail.com>
This commit is contained in:
Dawid 2022-09-23 13:39:25 +02:00 committed by GitHub
parent 4c62ebe45d
commit 9f7c934dec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 221 additions and 912 deletions

View file

@ -6,6 +6,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Pass query params in `ORDER_DETAILS_MORE_ACTIONS` and `PRODUCT_DETAILS_MORE_ACTIONS` mounting points - #2100 by @witoszekdev - Pass query params in `ORDER_DETAILS_MORE_ACTIONS` and `PRODUCT_DETAILS_MORE_ACTIONS` mounting points - #2100 by @witoszekdev
- Add product variant reference attribute - #2268 by @droniu - Add product variant reference attribute - #2268 by @droniu
- Fix dropdown select arrow clicks bugs - #2301 by @orzechdev
- Fix invalid values in channel picker - #2313 by @orzechdev - Fix invalid values in channel picker - #2313 by @orzechdev
- Fix missing metadata and payment balance on unconfirmed orders - #2314 by @orzechdev - Fix missing metadata and payment balance on unconfirmed orders - #2314 by @orzechdev
- Fix exit form dialog false positive - #2311 by @orzechdev - Fix exit form dialog false positive - #2311 by @orzechdev

View file

@ -4,10 +4,9 @@ import {
TextField, TextField,
Typography, Typography,
} from "@material-ui/core"; } from "@material-ui/core";
import { fade } from "@material-ui/core/styles/colorManipulator";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import Debounce, { DebounceProps } from "@saleor/components/Debounce"; import Debounce, { DebounceProps } from "@saleor/components/Debounce";
import { ChevronIcon, IconButton, makeStyles } from "@saleor/macaw-ui"; import { ChevronIcon, IconButton } from "@saleor/macaw-ui";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import classNames from "classnames"; import classNames from "classnames";
import Downshift, { ControllerStateAndHelpers } from "downshift"; import Downshift, { ControllerStateAndHelpers } from "downshift";
@ -18,75 +17,7 @@ import MultiAutocompleteSelectFieldContent, {
MultiAutocompleteActionType, MultiAutocompleteActionType,
MultiAutocompleteChoiceType, MultiAutocompleteChoiceType,
} from "./MultiAutocompleteSelectFieldContent"; } from "./MultiAutocompleteSelectFieldContent";
import { useStyles } from "./styles";
const useStyles = makeStyles(
theme => ({
chip: {
width: "100%",
},
chipClose: {
height: 32,
padding: 0,
width: 32,
},
chipContainer: {
display: "flex",
flexDirection: "column",
marginTop: theme.spacing(1),
},
chipInner: {
"& svg": {
color: theme.palette.primary.contrastText,
},
alignItems: "center",
background: fade(theme.palette.primary.main, 0.8),
borderRadius: 18,
color: theme.palette.primary.contrastText,
display: "flex",
justifyContent: "space-between",
margin: theme.spacing(1, 0),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
chipLabel: {
color: theme.palette.primary.contrastText,
},
container: {
flexGrow: 1,
position: "relative",
},
disabledChipInner: {
"& svg": {
color: theme.palette.grey[200],
},
alignItems: "center",
background: fade(theme.palette.grey[400], 0.8),
borderRadius: 18,
color: theme.palette.primary.contrastText,
display: "flex",
justifyContent: "space-between",
margin: theme.spacing(1, 0),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
adornment: {
color: theme.palette.saleor.main[3],
cursor: "pointer",
userSelect: "none",
display: "flex",
alignItems: "center",
"& svg": {
transition: theme.transitions.duration.shorter + "ms",
},
},
adornmentRotate: {
"& svg": {
transform: "rotate(180deg)",
},
},
}),
{ name: "MultiAutocompleteSelectField" },
);
export interface MultiAutocompleteSelectFieldProps export interface MultiAutocompleteSelectFieldProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
@ -141,14 +72,20 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
...rest ...rest
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
const anchor = React.useRef<HTMLInputElement | null>(null); const anchor = React.useRef<HTMLDivElement | null>(null);
const input = React.useRef<HTMLInputElement | null>(null);
const [inputValue, setInputValue] = React.useState("");
const handleSelect = ( const handleSelect = (
item: string, item: string,
downshiftOpts?: ControllerStateAndHelpers<string>, downshiftOpts?: ControllerStateAndHelpers<string>,
) => { ) => {
if (downshiftOpts) { if (downshiftOpts) {
downshiftOpts.reset({ inputValue: "", isOpen: true }); downshiftOpts.reset({
inputValue: downshiftOpts.inputValue,
isOpen: true,
});
} }
onChange({ onChange({
target: { name, value: item }, target: { name, value: item },
@ -157,12 +94,17 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
return ( return (
<> <>
<DebounceAutocomplete debounceFn={fetchChoices}> <DebounceAutocomplete
debounceFn={value => {
setInputValue(value);
fetchChoices(value);
}}
>
{debounceFn => ( {debounceFn => (
<Downshift <Downshift
onInputValueChange={value => debounceFn(value)} onInputValueChange={value => debounceFn(value)}
onSelect={handleSelect} onSelect={handleSelect}
itemToString={() => ""} itemToString={() => inputValue}
// this is to prevent unwanted state updates when the dropdown is closed with an empty value, // this is to prevent unwanted state updates when the dropdown is closed with an empty value,
// which downshift interprets as the value being updated with an empty string, causing side-effects // which downshift interprets as the value being updated with an empty string, causing side-effects
stateReducer={(state, changes) => { stateReducer={(state, changes) => {
@ -181,7 +123,6 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
getMenuProps, getMenuProps,
highlightedIndex, highlightedIndex,
inputValue, inputValue,
getToggleButtonProps,
}) => { }) => {
const displayCustomValue = const displayCustomValue =
inputValue && inputValue &&
@ -192,13 +133,30 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
choice.label.toLowerCase() === inputValue.toLowerCase(), choice.label.toLowerCase() === inputValue.toLowerCase(),
); );
const handleFocus = () => {
if (fetchOnFocus) {
fetchChoices(inputValue);
}
input.current.select();
};
const handleToggleMenu = () => {
if (disabled) {
return;
}
toggleMenu();
};
return ( return (
<div className={classes.container} {...rest}> <div className={classes.container} {...rest}>
<TextField <TextField
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<div <div
{...getToggleButtonProps()} onClick={() => {
handleToggleMenu();
handleFocus();
}}
className={classNames(classes.adornment, { className={classNames(classes.adornment, {
[classes.adornmentRotate]: isOpen, [classes.adornmentRotate]: isOpen,
})} })}
@ -207,18 +165,15 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
<ChevronIcon /> <ChevronIcon />
</div> </div>
), ),
id: undefined,
onFocus: handleFocus,
ref: anchor, ref: anchor,
onFocus: () => {
if (fetchOnFocus) {
fetchChoices(inputValue);
}
},
}} }}
inputProps={{ inputProps={{
...getInputProps({ ...getInputProps({
placeholder, placeholder,
testId, testId,
onClick: toggleMenu, onClick: handleToggleMenu,
}), }),
...getMenuProps(), ...getMenuProps(),
}} }}
@ -227,7 +182,9 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
label={label} label={label}
fullWidth={true} fullWidth={true}
disabled={disabled} disabled={disabled}
autoFocus={true}
onBlur={onBlur} onBlur={onBlur}
inputRef={input}
/> />
{isOpen && ( {isOpen && (
<Popper <Popper
@ -241,7 +198,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
> >
<MultiAutocompleteSelectFieldContent <MultiAutocompleteSelectFieldContent
add={ add={
add && { !!add && {
...add, ...add,
onClick: () => { onClick: () => {
add.onClick(); add.onClick();

View file

@ -71,6 +71,7 @@ const useStyles = makeStyles(
? theme.palette.grey[50] ? theme.palette.grey[50]
: theme.palette.grey[900], : theme.palette.grey[900],
bottom: 0, bottom: 0,
color: theme.palette.grey[500],
display: "flex", display: "flex",
height: 30, height: 30,
justifyContent: "center", justifyContent: "center",

View file

@ -0,0 +1,71 @@
import { fade } from "@material-ui/core/styles/colorManipulator";
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
chip: {
width: "100%",
},
chipClose: {
height: 32,
padding: 0,
width: 32,
},
chipContainer: {
display: "flex",
flexDirection: "column",
marginTop: theme.spacing(1),
},
chipInner: {
"& svg": {
color: theme.palette.primary.contrastText,
},
alignItems: "center",
background: fade(theme.palette.primary.main, 0.8),
borderRadius: 18,
color: theme.palette.primary.contrastText,
display: "flex",
justifyContent: "space-between",
margin: theme.spacing(1, 0),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
chipLabel: {
color: theme.palette.primary.contrastText,
},
container: {
flexGrow: 1,
position: "relative",
},
disabledChipInner: {
"& svg": {
color: theme.palette.grey[200],
},
alignItems: "center",
background: fade(theme.palette.grey[400], 0.8),
borderRadius: 18,
color: theme.palette.primary.contrastText,
display: "flex",
justifyContent: "space-between",
margin: theme.spacing(1, 0),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
adornment: {
color: theme.palette.saleor.main[3],
cursor: "pointer",
userSelect: "none",
display: "flex",
alignItems: "center",
"& svg": {
transition: theme.transitions.duration.shorter + "ms",
},
},
adornmentRotate: {
"& svg": {
transform: "rotate(180deg)",
},
},
}),
{ name: "MultiAutocompleteSelectField" },
);

View file

@ -6,7 +6,7 @@ import {
} from "@material-ui/core"; } from "@material-ui/core";
import { InputProps } from "@material-ui/core/Input"; import { InputProps } from "@material-ui/core/Input";
import { ExtendedFormHelperTextProps } from "@saleor/channels/components/ChannelForm/types"; import { ExtendedFormHelperTextProps } from "@saleor/channels/components/ChannelForm/types";
import { ChevronIcon, makeStyles } from "@saleor/macaw-ui"; import { ChevronIcon } from "@saleor/macaw-ui";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import classNames from "classnames"; import classNames from "classnames";
import Downshift from "downshift"; import Downshift from "downshift";
@ -18,32 +18,7 @@ import SingleAutocompleteSelectFieldContent, {
SingleAutocompleteActionType, SingleAutocompleteActionType,
SingleAutocompleteChoiceType, SingleAutocompleteChoiceType,
} from "./SingleAutocompleteSelectFieldContent"; } from "./SingleAutocompleteSelectFieldContent";
import { useStyles } from "./styles";
const useStyles = makeStyles(
theme => ({
container: {
flexGrow: 1,
position: "relative",
},
nakedInput: {
padding: theme.spacing(2, 0),
},
adornment: {
color: theme.palette.saleor.main[3],
cursor: "pointer",
userSelect: "none",
"& svg": {
transition: theme.transitions.duration.shorter + "ms",
},
},
adornmentRotate: {
"& svg": {
transform: "rotate(180deg)",
},
},
}),
{ name: "SingleAutocompleteSelectField" },
);
export interface SingleAutocompleteSelectFieldProps export interface SingleAutocompleteSelectFieldProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
@ -142,7 +117,6 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
closeMenu, closeMenu,
highlightedIndex, highlightedIndex,
reset, reset,
getToggleButtonProps,
}) => { }) => {
const isCustomValueSelected = const isCustomValueSelected =
choices && selectedItem choices && selectedItem
@ -187,13 +161,30 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
closeMenu(); closeMenu();
}; };
const handleFocus = () => {
if (fetchOnFocus) {
fetchChoices(inputValue);
}
input.current.select();
};
const handleToggleMenu = () => {
if (disabled) {
return;
}
toggleMenu();
};
const TextFieldComponent = nakedInput ? InputBase : TextField; const TextFieldComponent = nakedInput ? InputBase : TextField;
const commonInputProps = { const commonInputProps = {
...InputProps, ...InputProps,
endAdornment: ( endAdornment: (
<div <div
{...getToggleButtonProps()} onClick={() => {
handleToggleMenu();
handleFocus();
}}
className={classNames(classes.adornment, { className={classNames(classes.adornment, {
[classes.adornmentRotate]: isOpen, [classes.adornmentRotate]: isOpen,
})} })}
@ -203,12 +194,8 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
), ),
error, error,
id: undefined, id: undefined,
onFocus: () => { onFocus: handleFocus,
if (fetchOnFocus) { ref: anchor,
fetchChoices(inputValue);
}
input.current.select();
},
}; };
const nakedInputProps = nakedInput const nakedInputProps = nakedInput
@ -234,12 +221,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
inputProps={{ inputProps={{
...getInputProps({ ...getInputProps({
placeholder, placeholder,
onClick: () => { onClick: handleToggleMenu,
if (disabled) {
return;
}
toggleMenu();
},
}), }),
}} }}
error={error} error={error}
@ -249,7 +231,6 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
label={label} label={label}
fullWidth={true} fullWidth={true}
onBlur={onBlur} onBlur={onBlur}
ref={anchor}
inputRef={input} inputRef={input}
/> />
{isOpen && (!!inputValue || !!choices.length) && ( {isOpen && (!!inputValue || !!choices.length) && (

View file

@ -69,7 +69,7 @@ const useStyles = makeStyles(
? theme.palette.grey[50] ? theme.palette.grey[50]
: theme.palette.grey[900], : theme.palette.grey[900],
bottom: 0, bottom: 0,
color: theme.palette.primary.main, color: theme.palette.grey[500],
display: "flex", display: "flex",
height: 30, height: 30,
justifyContent: "center", justifyContent: "center",

View file

@ -0,0 +1,27 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
container: {
flexGrow: 1,
position: "relative",
},
nakedInput: {
padding: theme.spacing(2, 0),
},
adornment: {
color: theme.palette.saleor.main[3],
cursor: "pointer",
userSelect: "none",
"& svg": {
transition: theme.transitions.duration.shorter + "ms",
},
},
adornmentRotate: {
"& svg": {
transform: "rotate(180deg)",
},
},
}),
{ name: "SingleAutocompleteSelectField" },
);

File diff suppressed because it is too large Load diff