Product variants enhancements (#1918)

* Remove savebar docking

* Hide attributes if empty

* Select text in autocomplete after initial click

* Update snapshots

* Use stable macaw version

* Remove maybes

* Add util filter function
This commit is contained in:
Dominik Żegleń 2022-03-16 10:45:15 +01:00 committed by GitHub
parent 3e96a68a6e
commit ac4a219023
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 291 deletions

5
package-lock.json generated
View file

@ -4828,8 +4828,9 @@
}
},
"@saleor/macaw-ui": {
"version": "github:saleor/macaw-ui#1f78f97748c00a64ca46973c32eacc4d9a1ac2ac",
"from": "github:saleor/macaw-ui#SALEOR-5840-button-states",
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.3.3.tgz",
"integrity": "sha512-b2dLlOAXDe2OSmYeZSFKwiRMrJc+YkGhvvQCHQXHHP89TZbGZlWP0F13ycCI3OJSfYbCWZlQGM+alMiE31HOuA==",
"requires": {
"clsx": "^1.1.1",
"lodash": "^4.17.21",

View file

@ -28,7 +28,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.58",
"@material-ui/styles": "^4.11.4",
"@saleor/macaw-ui": "github:saleor/macaw-ui#SALEOR-5840-button-states",
"@saleor/macaw-ui": "^0.3.3",
"@saleor/sdk": "^0.4.2",
"@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6",

View file

@ -13,7 +13,6 @@ import {
} from "@saleor/macaw-ui";
import { isDarkTheme } from "@saleor/misc";
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
import classNames from "classnames";
import React from "react";
import { useIntl } from "react-intl";
import useRouter from "use-react-router";
@ -41,9 +40,6 @@ const useStyles = makeStyles(
position: "sticky",
zIndex: 10
},
appActionDocked: {
position: "static"
},
appLoader: {
height: appLoaderHeight,
marginBottom: theme.spacing(4),
@ -123,7 +119,7 @@ interface AppLayoutProps {
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const classes = useStyles({});
const { themeType, setTheme } = useTheme();
const { anchor: appActionAnchor, docked } = useActionBar();
const { anchor: appActionAnchor } = useActionBar();
const appHeaderAnchor = useBacklink();
const { logout, user } = useUser();
const navigate = useNavigator();
@ -234,12 +230,7 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
: children}
</main>
</div>
<div
className={classNames(classes.appAction, {
[classes.appActionDocked]: docked
})}
ref={appActionAnchor}
/>
<div className={classes.appAction} ref={appActionAnchor} />
</div>
</div>
</>

View file

@ -90,7 +90,8 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
...rest
} = 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 handleChange = (item: string) => {
onChange({
@ -188,6 +189,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
if (fetchOnFocus) {
fetchChoices(inputValue);
}
input.current.select();
}
};
@ -224,6 +226,7 @@ const SingleAutocompleteSelectFieldComponent: React.FC<SingleAutocompleteSelectF
fullWidth={true}
onBlur={onBlur}
ref={anchor}
inputRef={input}
/>
{isOpen && (!!inputValue || !!choices.length) && (
<Popper

View file

@ -29,7 +29,6 @@ import { FetchMoreProps, RelayToFlat, ReorderAction } from "@saleor/types";
import React from "react";
import { defineMessages, useIntl } from "react-intl";
import { maybe } from "../../../misc";
import ProductShipping from "../ProductShipping/ProductShipping";
import ProductStocks, { ProductStockInput } from "../ProductStocks";
import ProductVariantCheckoutSettings from "../ProductVariantCheckoutSettings/ProductVariantCheckoutSettings";
@ -73,6 +72,11 @@ export interface ProductVariantPageSubmitData
removeStocks: string[];
}
function byAttributeScope(scope: VariantAttributeScope) {
return (attribute: AttributeInput) =>
attribute.data.variantAttributeScope === scope;
}
interface ProductVariantPageProps {
assignReferencesAttributeId?: string;
defaultVariantId?: string;
@ -223,175 +227,184 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
handlers,
hasChanged,
submit
}) => (
<>
<Grid variant="inverted">
<div>
<ProductVariantNavigation
current={variant ? variant.id : undefined}
defaultVariantId={defaultVariantId}
fallbackThumbnail={maybe(
() => variant.product.thumbnail.url
)}
variants={maybe(() => variant.product.variants)}
onAdd={onAdd}
onRowClick={(variantId: string) => {
if (variant) {
return onVariantClick(variantId);
}
}}
onReorder={onVariantReorder}
/>
</div>
<div>
<VariantDetailsChannelsAvailabilityCard variant={variant} />
<Attributes
entityId={variant?.id}
title={intl.formatMessage(messages.nonSelectionAttributes)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.NOT_VARIANT_SELECTION
)}
attributeValues={attributeValues}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
/>
<CardSpacer />
<Attributes
entityId={variant?.id}
title={intl.formatMessage(
messages.selectionAttributesHeader
)}
attributes={data.attributes.filter(
attribute =>
attribute.data.variantAttributeScope ===
VariantAttributeScope.VARIANT_SELECTION
)}
attributeValues={attributeValues}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
/>
<CardSpacer />
<ProductVariantMedia
disabled={loading}
media={media}
placeholderImage={placeholderImage}
onImageAdd={toggleModal}
/>
<CardSpacer />
<ProductVariantPrice
disabled={!variant}
ProductVariantChannelListings={data.channelListings.map(
channel => ({
...channel.data,
...channel.value
})
)}
errors={channelErrors}
loading={loading}
onChange={handlers.changeChannels}
/>
<CardSpacer />
<ProductVariantCheckoutSettings
data={data}
disabled={loading}
errors={errors}
onChange={change}
/>
<CardSpacer />
}) => {
const nonSelectionAttributes = data.attributes.filter(
byAttributeScope(VariantAttributeScope.NOT_VARIANT_SELECTION)
);
const selectionAttributes = data.attributes.filter(
byAttributeScope(VariantAttributeScope.VARIANT_SELECTION)
);
<ProductShipping
data={data}
disabled={loading}
errors={errors}
weightUnit={variant?.weight?.unit || defaultWeightUnit}
onChange={change}
/>
<CardSpacer />
<ProductStocks
productVariantChannelListings={data.channelListings.map(
channel => ({
...channel.data,
...channel.value
})
return (
<>
<Grid variant="inverted">
<div>
<ProductVariantNavigation
current={variant?.id}
defaultVariantId={defaultVariantId}
fallbackThumbnail={variant?.product.thumbnail.url}
variants={variant?.product.variants}
onAdd={onAdd}
onRowClick={(variantId: string) => {
if (variant) {
return onVariantClick(variantId);
}
}}
onReorder={onVariantReorder}
/>
</div>
<div>
<VariantDetailsChannelsAvailabilityCard variant={variant} />
{nonSelectionAttributes.length > 0 && (
<>
<Attributes
entityId={variant?.id}
title={intl.formatMessage(
messages.nonSelectionAttributes
)}
attributes={nonSelectionAttributes}
attributeValues={attributeValues}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
/>
<CardSpacer />
</>
)}
onVariantChannelListingChange={handlers.changeChannels}
data={data}
disabled={loading}
hasVariants={true}
errors={errors}
formErrors={formErrors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onFormDataChange={change}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onEndPreorderTrigger={
!!variant?.preorder
? () => setIsEndPreorderModalOpened(true)
: null
}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}
/>
<CardSpacer />
<Metadata data={data} onChange={handlers.changeMetadata} />
</div>
</Grid>
<Savebar
disabled={loading || formDisabled || !hasChanged}
state={saveButtonBarState}
onCancel={onBack}
onDelete={onDelete}
onSubmit={submit}
/>
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts
)}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}
onFetchMore={handlers.fetchMoreReferences?.onFetchMore}
loading={handlers.fetchMoreReferences?.loading}
onClose={onCloseDialog}
onSubmit={attributeValues =>
handleAssignReferenceAttribute(
attributeValues,
data,
handlers
)
}
{selectionAttributes.length > 0 && (
<>
<Attributes
entityId={variant?.id}
title={intl.formatMessage(
messages.selectionAttributesHeader
)}
attributes={selectionAttributes}
attributeValues={attributeValues}
loading={loading}
disabled={loading}
errors={errors}
onChange={handlers.selectAttribute}
onMultiChange={handlers.selectAttributeMultiple}
onFileChange={handlers.selectAttributeFile}
onReferencesRemove={handlers.selectAttributeReference}
onReferencesAddClick={onAssignReferencesClick}
onReferencesReorder={handlers.reorderAttributeValue}
fetchAttributeValues={fetchAttributeValues}
fetchMoreAttributeValues={fetchMoreAttributeValues}
onAttributeSelectBlur={onAttributeSelectBlur}
/>
<CardSpacer />
</>
)}
<ProductVariantMedia
disabled={loading}
media={media}
placeholderImage={placeholderImage}
onImageAdd={toggleModal}
/>
<CardSpacer />
<ProductVariantPrice
disabled={!variant}
ProductVariantChannelListings={data.channelListings.map(
channel => ({
...channel.data,
...channel.value
})
)}
errors={channelErrors}
loading={loading}
onChange={handlers.changeChannels}
/>
<CardSpacer />
<ProductVariantCheckoutSettings
data={data}
disabled={loading}
errors={errors}
onChange={change}
/>
<CardSpacer />
<ProductShipping
data={data}
disabled={loading}
errors={errors}
weightUnit={variant?.weight?.unit || defaultWeightUnit}
onChange={change}
/>
<CardSpacer />
<ProductStocks
productVariantChannelListings={data.channelListings.map(
channel => ({
...channel.data,
...channel.value
})
)}
onVariantChannelListingChange={handlers.changeChannels}
data={data}
disabled={loading}
hasVariants={true}
errors={errors}
formErrors={formErrors}
stocks={data.stocks}
warehouses={warehouses}
onChange={handlers.changeStock}
onFormDataChange={change}
onChangePreorderEndDate={handlers.changePreorderEndDate}
onEndPreorderTrigger={
!!variant?.preorder
? () => setIsEndPreorderModalOpened(true)
: null
}
onWarehouseStockAdd={handlers.addStock}
onWarehouseStockDelete={handlers.deleteStock}
onWarehouseConfigure={onWarehouseConfigure}
/>
<CardSpacer />
<Metadata data={data} onChange={handlers.changeMetadata} />
</div>
</Grid>
<Savebar
disabled={loading || formDisabled || !hasChanged}
state={saveButtonBarState}
onCancel={onBack}
onDelete={onDelete}
onSubmit={submit}
/>
)}
</>
)}
{canOpenAssignReferencesAttributeDialog && (
<AssignAttributeValueDialog
attributeValues={getAttributeValuesFromReferences(
assignReferencesAttributeId,
data.attributes,
referencePages,
referenceProducts
)}
hasMore={handlers.fetchMoreReferences?.hasMore}
open={canOpenAssignReferencesAttributeDialog}
onFetch={handlers.fetchReferences}
onFetchMore={handlers.fetchMoreReferences?.onFetchMore}
loading={handlers.fetchMoreReferences?.loading}
onClose={onCloseDialog}
onSubmit={attributeValues =>
handleAssignReferenceAttribute(
attributeValues,
data,
handlers
)
}
/>
)}
</>
);
}}
</ProductVariantUpdateForm>
</Container>
{variant && (
@ -400,7 +413,7 @@ const ProductVariantPage: React.FC<ProductVariantPageProps> = ({
onMediaSelect={onMediaSelect}
open={isModalOpened}
media={productMedia}
selectedMedia={maybe(() => variant.media.map(image => image.id))}
selectedMedia={variant?.media.map(image => image.id)}
/>
)}
{!!variant?.preorder && (

View file

@ -225170,114 +225170,6 @@ exports[`Storyshots Views / Products / Product variant details when loading data
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id Attributes-card-id MuiPaper-elevation0-id MuiPaper-rounded-id"
>
<div
class="MuiCardHeader-root-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Variant Attributes
</span>
</div>
</div>
<div
class="MuiCardContent-root-id Attributes-cardContent-id"
>
<div
class="Attributes-expansionBar-id"
>
<div
class="Attributes-expansionBarLabelContainer-id"
>
<div
class="MuiTypography-root-id Attributes-expansionBarLabel-id MuiTypography-caption-id"
>
0 Attributes
</div>
</div>
<button
class="MuiButtonBase-root-id IconButton-secondary-id Attributes-expansionBarButton-id IconButton-hoverOutline-id"
data-test-id="attributes-expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id Attributes-expansionBarButtonIcon-id Attributes-rotate-id"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
</button>
</div>
</div>
</div>
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id Attributes-card-id MuiPaper-elevation0-id MuiPaper-rounded-id"
>
<div
class="MuiCardHeader-root-id"
>
<div
class="MuiCardHeader-content-id"
>
<span
class="MuiTypography-root-id MuiCardHeader-title-id MuiTypography-h5-id MuiTypography-displayBlock-id"
>
Variant Selection Attributes
</span>
</div>
</div>
<div
class="MuiCardContent-root-id Attributes-cardContent-id"
>
<div
class="Attributes-expansionBar-id"
>
<div
class="Attributes-expansionBarLabelContainer-id"
>
<div
class="MuiTypography-root-id Attributes-expansionBarLabel-id MuiTypography-caption-id"
>
0 Attributes
</div>
</div>
<button
class="MuiButtonBase-root-id IconButton-secondary-id Attributes-expansionBarButton-id IconButton-hoverOutline-id"
data-test-id="attributes-expand"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root-id Attributes-expansionBarButtonIcon-id Attributes-rotate-id"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M7 10l5 5 5-5z"
/>
</svg>
</button>
</div>
</div>
</div>
<div
class="CardSpacer-spacer-id"
/>
<div
class="MuiPaper-root-id MuiCard-root-id MuiPaper-elevation0-id MuiPaper-rounded-id"
>