Add warehouse create action button

This commit is contained in:
dominik-zeglen 2020-02-10 17:07:17 +01:00
parent 3d838d8428
commit 60ec16961c
9 changed files with 108 additions and 9 deletions

View file

@ -12,7 +12,8 @@ import Debounce, { DebounceProps } from "@saleor/components/Debounce";
import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown"; import ArrowDropdownIcon from "@saleor/icons/ArrowDropdown";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import MultiAutocompleteSelectFieldContent, { import MultiAutocompleteSelectFieldContent, {
MultiAutocompleteChoiceType MultiAutocompleteChoiceType,
MultiAutocompleteActionType
} from "./MultiAutocompleteSelectFieldContent"; } from "./MultiAutocompleteSelectFieldContent";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -71,6 +72,7 @@ const useStyles = makeStyles(
export interface MultiAutocompleteSelectFieldProps export interface MultiAutocompleteSelectFieldProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
add: MultiAutocompleteActionType;
allowCustomValues?: boolean; allowCustomValues?: boolean;
displayValues: MultiAutocompleteChoiceType[]; displayValues: MultiAutocompleteChoiceType[];
error?: boolean; error?: boolean;
@ -166,6 +168,13 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
/> />
{isOpen && (!!inputValue || !!choices.length) && ( {isOpen && (!!inputValue || !!choices.length) && (
<MultiAutocompleteSelectFieldContent <MultiAutocompleteSelectFieldContent
add={{
...add,
onClick: () => {
add.onClick();
closeMenu();
}
}}
choices={choices.filter( choices={choices.filter(
choice => !value.includes(choice.value) choice => !value.includes(choice.value)
)} )}

View file

@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl";
import chevronDown from "@assets/images/ChevronDown.svg"; import chevronDown from "@assets/images/ChevronDown.svg";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import MenuItem from "@material-ui/core/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import AddIcon from "@material-ui/icons/Add"; import AddIcon from "@material-ui/icons/Add";
@ -22,6 +23,10 @@ const menuItemHeight = 46;
const maxMenuItems = 5; const maxMenuItems = 5;
const offset = 24; const offset = 24;
export interface MultiAutocompleteActionType {
label: string;
onClick: () => void;
}
export interface MultiAutocompleteChoiceType { export interface MultiAutocompleteChoiceType {
label: string; label: string;
value: any; value: any;
@ -29,6 +34,7 @@ export interface MultiAutocompleteChoiceType {
} }
export interface MultiAutocompleteSelectFieldContentProps export interface MultiAutocompleteSelectFieldContentProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
add: MultiAutocompleteActionType;
choices: MultiAutocompleteChoiceType[]; choices: MultiAutocompleteChoiceType[];
displayCustomValue: boolean; displayCustomValue: boolean;
displayValues: MultiAutocompleteChoiceType[]; displayValues: MultiAutocompleteChoiceType[];
@ -145,6 +151,7 @@ function getChoiceIndex(
const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectFieldContentProps> = props => { const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectFieldContentProps> = props => {
const { const {
add,
choices, choices,
displayCustomValue, displayCustomValue,
displayValues, displayValues,
@ -156,6 +163,10 @@ const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectField
onFetchMore onFetchMore
} = props; } = props;
if (!!add && !!displayCustomValue) {
throw new Error("Add and custom value cannot be displayed simultaneously");
}
const classes = useStyles(props); const classes = useStyles(props);
const anchor = React.useRef<HTMLDivElement>(); const anchor = React.useRef<HTMLDivElement>();
const scrollPosition = useElementScroll(anchor); const scrollPosition = useElementScroll(anchor);
@ -183,6 +194,20 @@ const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectField
displayValues.length > 0 || displayValues.length > 0 ||
displayCustomValue ? ( displayCustomValue ? (
<> <>
{add && (
<MenuItem
className={classes.menuItem}
component="div"
{...getItemProps({
item: inputValue
})}
data-tc="multiautocomplete-select-option-add"
onClick={add.onClick}
>
<AddIcon color="primary" className={classes.addIcon} />
<Typography color="primary">{add.label}</Typography>
</MenuItem>
)}
{displayCustomValue && ( {displayCustomValue && (
<MenuItem <MenuItem
className={classes.menuItem} className={classes.menuItem}

View file

@ -69,6 +69,7 @@ const Story: React.FC<Partial<
}; };
const contentProps: SingleAutocompleteSelectFieldContentProps = { const contentProps: SingleAutocompleteSelectFieldContentProps = {
add: undefined,
choices: suggestions.slice(0, 10), choices: suggestions.slice(0, 10),
displayCustomValue: false, displayCustomValue: false,
emptyOption: false, emptyOption: false,
@ -88,6 +89,15 @@ storiesOf("Generics / Select with autocomplete", module)
.add("default", () => ( .add("default", () => (
<SingleAutocompleteSelectFieldContent {...contentProps} /> <SingleAutocompleteSelectFieldContent {...contentProps} />
)) ))
.add("with add", () => (
<SingleAutocompleteSelectFieldContent
{...contentProps}
add={{
label: "Add New Collection",
onClick: () => undefined
}}
/>
))
.add("can load more", () => ( .add("can load more", () => (
<SingleAutocompleteSelectFieldContent {...contentProps} hasMore={true} /> <SingleAutocompleteSelectFieldContent {...contentProps} hasMore={true} />
)) ))

View file

@ -10,7 +10,8 @@ import { FetchMoreProps } from "@saleor/types";
import ArrowDropdownIcon from "../../icons/ArrowDropdown"; import ArrowDropdownIcon from "../../icons/ArrowDropdown";
import Debounce, { DebounceProps } from "../Debounce"; import Debounce, { DebounceProps } from "../Debounce";
import SingleAutocompleteSelectFieldContent, { import SingleAutocompleteSelectFieldContent, {
SingleAutocompleteChoiceType SingleAutocompleteChoiceType,
SingleAutocompleteActionType
} from "./SingleAutocompleteSelectFieldContent"; } from "./SingleAutocompleteSelectFieldContent";
const useStyles = makeStyles( const useStyles = makeStyles(
@ -25,6 +26,7 @@ const useStyles = makeStyles(
export interface SingleAutocompleteSelectFieldProps export interface SingleAutocompleteSelectFieldProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
add: SingleAutocompleteActionType;
error?: boolean; error?: boolean;
name: string; name: string;
displayValue: string; displayValue: string;
@ -47,6 +49,7 @@ const DebounceAutocomplete: React.ComponentType<DebounceProps<
const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectFieldProps> = props => { const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectFieldProps> = props => {
const { const {
add,
allowCustomValues, allowCustomValues,
choices, choices,
disabled, disabled,
@ -144,6 +147,13 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
/> />
{isOpen && (!!inputValue || !!choices.length) && ( {isOpen && (!!inputValue || !!choices.length) && (
<SingleAutocompleteSelectFieldContent <SingleAutocompleteSelectFieldContent
add={{
...add,
onClick: () => {
add.onClick();
closeMenu();
}
}}
choices={choices} choices={choices}
displayCustomValue={displayCustomValue} displayCustomValue={displayCustomValue}
emptyOption={emptyOption} emptyOption={emptyOption}

View file

@ -1,5 +1,6 @@
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import MenuItem from "@material-ui/core/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import Add from "@material-ui/icons/Add";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
@ -24,8 +25,13 @@ export interface SingleAutocompleteChoiceType {
label: string; label: string;
value: any; value: any;
} }
export interface SingleAutocompleteActionType {
label: string;
onClick: () => void;
}
export interface SingleAutocompleteSelectFieldContentProps export interface SingleAutocompleteSelectFieldContentProps
extends Partial<FetchMoreProps> { extends Partial<FetchMoreProps> {
add: SingleAutocompleteActionType;
choices: SingleAutocompleteChoiceType[]; choices: SingleAutocompleteChoiceType[];
displayCustomValue: boolean; displayCustomValue: boolean;
emptyOption: boolean; emptyOption: boolean;
@ -38,6 +44,14 @@ export interface SingleAutocompleteSelectFieldContentProps
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
add: {
background: theme.palette.background.default,
border: `1px solid ${theme.palette.divider}`,
borderRadius: "100%",
height: 24,
marginRight: theme.spacing(),
width: 24
},
arrowContainer: { arrowContainer: {
position: "relative" position: "relative"
}, },
@ -109,10 +123,9 @@ function getChoiceIndex(
return choiceIndex; return choiceIndex;
} }
const SingleAutocompleteSelectFieldContent: React.FC< const SingleAutocompleteSelectFieldContent: React.FC<SingleAutocompleteSelectFieldContentProps> = props => {
SingleAutocompleteSelectFieldContentProps
> = props => {
const { const {
add,
choices, choices,
displayCustomValue, displayCustomValue,
emptyOption, emptyOption,
@ -125,6 +138,10 @@ const SingleAutocompleteSelectFieldContent: React.FC<
onFetchMore onFetchMore
} = props; } = props;
if (!!add && !!displayCustomValue) {
throw new Error("Add and custom value cannot be displayed simultaneously");
}
const classes = useStyles(props); const classes = useStyles(props);
const anchor = React.useRef<HTMLDivElement>(); const anchor = React.useRef<HTMLDivElement>();
const scrollPosition = useElementScroll(anchor); const scrollPosition = useElementScroll(anchor);
@ -164,6 +181,20 @@ const SingleAutocompleteSelectFieldContent: React.FC<
</Typography> </Typography>
</MenuItem> </MenuItem>
)} )}
{add && (
<MenuItem
className={classes.menuItem}
component="div"
{...getItemProps({
item: inputValue
})}
data-tc="singleautocomplete-select-option-add"
onClick={add.onClick}
>
<Add color="primary" className={classes.add} />
<Typography color="primary">{add.label}</Typography>
</MenuItem>
)}
{displayCustomValue && ( {displayCustomValue && (
<MenuItem <MenuItem
className={classes.menuItem} className={classes.menuItem}
@ -184,14 +215,14 @@ const SingleAutocompleteSelectFieldContent: React.FC<
/> />
</MenuItem> </MenuItem>
)} )}
{choices.length > 0 && displayCustomValue && ( {choices.length > 0 && (!!add || displayCustomValue) && (
<Hr className={classes.hr} /> <Hr className={classes.hr} />
)} )}
{choices.map((suggestion, index) => { {choices.map((suggestion, index) => {
const choiceIndex = getChoiceIndex( const choiceIndex = getChoiceIndex(
index, index,
emptyOption, emptyOption,
displayCustomValue !!add || displayCustomValue
); );
return ( return (

View file

@ -43,6 +43,7 @@ export interface ShippingZoneDetailsPageProps {
onPriceRateEdit: (id: string) => void; onPriceRateEdit: (id: string) => void;
onRateRemove: (rateId: string) => void; onRateRemove: (rateId: string) => void;
onSubmit: (data: FormData) => void; onSubmit: (data: FormData) => void;
onWarehouseAdd: () => void;
onWeightRateAdd: () => void; onWeightRateAdd: () => void;
onWeightRateEdit: (id: string) => void; onWeightRateEdit: (id: string) => void;
} }
@ -67,6 +68,7 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
onPriceRateEdit, onPriceRateEdit,
onRateRemove, onRateRemove,
onSubmit, onSubmit,
onWarehouseAdd,
onWeightRateAdd, onWeightRateAdd,
onWeightRateEdit, onWeightRateEdit,
saveButtonBarState, saveButtonBarState,
@ -165,9 +167,10 @@ const ShippingZoneDetailsPage: React.FC<ShippingZoneDetailsPageProps> = ({
displayValue={warehouseDisplayValues} displayValue={warehouseDisplayValues}
hasMore={false} hasMore={false}
loading={false} loading={false}
warehouses={warehouseChoices}
onChange={handleWarehouseChange} onChange={handleWarehouseChange}
onFetchMore={() => undefined} onFetchMore={() => undefined}
warehouses={warehouseChoices} onWarehouseAdd={onWarehouseAdd}
/> />
</div> </div>
</Grid> </Grid>

View file

@ -18,6 +18,7 @@ interface ShippingZonewWarehousesProps extends FetchMoreProps {
displayValue: MultiAutocompleteChoiceType[]; displayValue: MultiAutocompleteChoiceType[];
warehouses: MultiAutocompleteChoiceType[]; warehouses: MultiAutocompleteChoiceType[];
onChange: FormChange; onChange: FormChange;
onWarehouseAdd: () => void;
} }
export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = props => { export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = props => {
@ -26,9 +27,10 @@ export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = pr
displayValue, displayValue,
hasMore, hasMore,
loading, loading,
warehouses,
onChange, onChange,
onFetchMore, onFetchMore,
warehouses onWarehouseAdd
} = props; } = props;
const intl = useIntl(); const intl = useIntl();
@ -42,6 +44,13 @@ export const ShippingZoneWarehouses: React.FC<ShippingZonewWarehousesProps> = pr
/> />
<CardContent> <CardContent>
<MultiAutocompleteSelectField <MultiAutocompleteSelectField
add={{
label: intl.formatMessage({
defaultMessage: "Add New Warehouse",
description: "button"
}),
onClick: onWarehouseAdd
}}
choices={warehouses} choices={warehouses}
displayValues={displayValue} displayValues={displayValue}
fetchChoices={() => undefined} fetchChoices={() => undefined}

View file

@ -20,6 +20,7 @@ export const shippingZonePath = (id: string) =>
urlJoin(shippingZonesListPath, id); urlJoin(shippingZonesListPath, id);
export type ShippingZoneUrlDialog = export type ShippingZoneUrlDialog =
| "add-rate" | "add-rate"
| "add-warehouse"
| "assign-country" | "assign-country"
| "edit-rate" | "edit-rate"
| "remove" | "remove"

View file

@ -176,6 +176,7 @@ const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
}) })
} }
onSubmit={handleSubmit} onSubmit={handleSubmit}
onWarehouseAdd={() => openModal("add-warehouse")}
onWeightRateAdd={() => onWeightRateAdd={() =>
openModal("add-rate", { openModal("add-rate", {
type: ShippingMethodTypeEnum.WEIGHT type: ShippingMethodTypeEnum.WEIGHT