Fix strict null check in attributes (#3005)

This commit is contained in:
poulch 2023-01-26 09:38:19 +01:00 committed by GitHub
parent 01172aed95
commit 993595caac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 207 additions and 178 deletions

View file

@ -197,7 +197,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = props => {
label={intl.formatMessage(messages.entityType)} label={intl.formatMessage(messages.entityType)}
name="entityType" name="entityType"
onChange={onChange} onChange={onChange}
value={data.entityType} value={data.entityType ?? undefined}
/> />
)} )}
</div> </div>

View file

@ -40,7 +40,7 @@ const useStyles = makeStyles(
); );
interface UnitData { interface UnitData {
unit?: MeasurementUnitsEnum; unit: MeasurementUnitsEnum | null | undefined;
system?: UnitSystem; system?: UnitSystem;
type?: UnitType; type?: UnitType;
} }
@ -145,7 +145,10 @@ export const NumericUnits: React.FC<NumericUnitsProps> = ({
label={formatMessage(M.messages.unitSystem)} label={formatMessage(M.messages.unitSystem)}
choices={systemChoices} choices={systemChoices}
onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) => onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) =>
setUnitData({ system: target.value as UnitSystem }) setUnitData(data => ({
...data,
system: target.value as UnitSystem,
}))
} }
value={system} value={system}
disabled={disabled} disabled={disabled}
@ -156,8 +159,8 @@ export const NumericUnits: React.FC<NumericUnitsProps> = ({
label={formatMessage(M.messages.unitOf)} label={formatMessage(M.messages.unitOf)}
choices={typeChoices} choices={typeChoices}
onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) => onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) =>
setUnitData(({ system }) => ({ setUnitData(data => ({
system, ...data,
type: target.value as UnitType, type: target.value as UnitType,
})) }))
} }
@ -168,7 +171,7 @@ export const NumericUnits: React.FC<NumericUnitsProps> = ({
{...(type && !unit && errorProps)} {...(type && !unit && errorProps)}
testId="unit" testId="unit"
label={formatMessage(M.messages.unit)} label={formatMessage(M.messages.unit)}
choices={type ? unitChoices[system][type] : []} choices={type && system ? unitChoices[system][type] : []}
onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) => onChange={({ target }: React.ChangeEvent<HTMLSelectElement>) =>
setUnitData(data => ({ setUnitData(data => ({
...data, ...data,
@ -177,7 +180,7 @@ export const NumericUnits: React.FC<NumericUnitsProps> = ({
} }
disabled={!type || disabled} disabled={!type || disabled}
value={ value={
type && unitMapping[system][type].includes(unit) type && system && unit && unitMapping[system][type].includes(unit)
? unit ? unit
: undefined : undefined
} }

View file

@ -11,7 +11,7 @@ import { TablePaginationWithContext } from "@dashboard/components/TablePaginatio
import TableRowLink from "@dashboard/components/TableRowLink"; import TableRowLink from "@dashboard/components/TableRowLink";
import { AttributeFragment } from "@dashboard/graphql"; import { AttributeFragment } from "@dashboard/graphql";
import { translateBoolean } from "@dashboard/intl"; import { translateBoolean } from "@dashboard/intl";
import { maybe, renderCollection } from "@dashboard/misc"; import { renderCollection } from "@dashboard/misc";
import { ListActions, ListProps, SortPage } from "@dashboard/types"; import { ListActions, ListProps, SortPage } from "@dashboard/types";
import { getArrowDirection } from "@dashboard/utils/sort"; import { getArrowDirection } from "@dashboard/utils/sort";
import { TableBody, TableCell, TableFooter } from "@material-ui/core"; import { TableBody, TableCell, TableFooter } from "@material-ui/core";
@ -95,7 +95,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
className={classes.colSlug} className={classes.colSlug}
direction={ direction={
sort.sort === AttributeListUrlSortField.slug sort.sort === AttributeListUrlSortField.slug
? getArrowDirection(sort.asc) ? getArrowDirection(!!sort.asc)
: undefined : undefined
} }
arrowPosition="right" arrowPosition="right"
@ -107,7 +107,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
className={classes.colName} className={classes.colName}
direction={ direction={
sort.sort === AttributeListUrlSortField.name sort.sort === AttributeListUrlSortField.name
? getArrowDirection(sort.asc) ? getArrowDirection(!!sort.asc)
: undefined : undefined
} }
onClick={() => onSort(AttributeListUrlSortField.name)} onClick={() => onSort(AttributeListUrlSortField.name)}
@ -122,7 +122,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
className={classes.colVisible} className={classes.colVisible}
direction={ direction={
sort.sort === AttributeListUrlSortField.visible sort.sort === AttributeListUrlSortField.visible
? getArrowDirection(sort.asc) ? getArrowDirection(!!sort.asc)
: undefined : undefined
} }
textAlign="center" textAlign="center"
@ -138,7 +138,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
className={classes.colSearchable} className={classes.colSearchable}
direction={ direction={
sort.sort === AttributeListUrlSortField.searchable sort.sort === AttributeListUrlSortField.searchable
? getArrowDirection(sort.asc) ? getArrowDirection(!!sort.asc)
: undefined : undefined
} }
textAlign="center" textAlign="center"
@ -154,7 +154,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
className={classes.colFaceted} className={classes.colFaceted}
direction={ direction={
sort.sort === AttributeListUrlSortField.useInFacetedSearch sort.sort === AttributeListUrlSortField.useInFacetedSearch
? getArrowDirection(sort.asc) ? getArrowDirection(!!sort.asc)
: undefined : undefined
} }
textAlign="center" textAlign="center"
@ -185,14 +185,14 @@ const AttributeList: React.FC<AttributeListProps> = ({
key={attribute ? attribute.id : "skeleton"} key={attribute ? attribute.id : "skeleton"}
href={attribute && attributeUrl(attribute.id)} href={attribute && attributeUrl(attribute.id)}
className={classes.link} className={classes.link}
data-test-id={"id-" + maybe(() => attribute.id)} data-test-id={`id-${attribute?.id}`}
> >
<TableCell padding="checkbox"> <TableCell padding="checkbox">
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
disabled={disabled} disabled={disabled}
disableClickPropagation disableClickPropagation
onChange={() => toggle(attribute.id)} onChange={() => toggle(attribute?.id ?? "")}
/> />
</TableCell> </TableCell>
<TableCell className={classes.colSlug} data-test-id="slug"> <TableCell className={classes.colSlug} data-test-id="slug">
@ -204,7 +204,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
<TableCell <TableCell
className={classes.colVisible} className={classes.colVisible}
data-test-id="visible" data-test-id="visible"
data-test-visible={maybe(() => attribute.visibleInStorefront)} data-test-visible={attribute?.visibleInStorefront}
> >
{attribute ? ( {attribute ? (
translateBoolean(attribute.visibleInStorefront, intl) translateBoolean(attribute.visibleInStorefront, intl)
@ -215,9 +215,7 @@ const AttributeList: React.FC<AttributeListProps> = ({
<TableCell <TableCell
className={classes.colSearchable} className={classes.colSearchable}
data-test-id="searchable" data-test-id="searchable"
data-test-searchable={maybe( data-test-searchable={attribute?.filterableInDashboard}
() => attribute.filterableInDashboard,
)}
> >
{attribute ? ( {attribute ? (
translateBoolean(attribute.filterableInDashboard, intl) translateBoolean(attribute.filterableInDashboard, intl)
@ -228,9 +226,9 @@ const AttributeList: React.FC<AttributeListProps> = ({
<TableCell <TableCell
className={classes.colFaceted} className={classes.colFaceted}
data-test-id="use-in-faceted-search" data-test-id="use-in-faceted-search"
data-test-use-in-faceted-search={maybe( data-test-use-in-faceted-search={
() => attribute.filterableInStorefront, attribute?.filterableInStorefront
)} }
> >
{attribute ? ( {attribute ? (
translateBoolean(attribute.filterableInStorefront, intl) translateBoolean(attribute.filterableInStorefront, intl)

View file

@ -22,7 +22,6 @@ import {
import { SubmitPromise } from "@dashboard/hooks/useForm"; import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import { maybe } from "@dashboard/misc";
import { ListSettings, ReorderAction } from "@dashboard/types"; import { ListSettings, ReorderAction } from "@dashboard/types";
import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps"; import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps";
import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger"; import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger";
@ -37,11 +36,13 @@ import AttributeProperties from "../AttributeProperties";
import AttributeValues from "../AttributeValues"; import AttributeValues from "../AttributeValues";
export interface AttributePageProps { export interface AttributePageProps {
attribute: AttributeDetailsFragment | null; attribute?: AttributeDetailsFragment | null | undefined;
disabled: boolean; disabled: boolean;
errors: AttributeErrorFragment[]; errors: AttributeErrorFragment[];
saveButtonBarState: ConfirmButtonTransitionState; saveButtonBarState: ConfirmButtonTransitionState;
values: AttributeDetailsQuery["attribute"]["choices"]; values?:
| NonNullable<AttributeDetailsQuery["attribute"]>["choices"]
| undefined;
onDelete: () => void; onDelete: () => void;
onSubmit: (data: AttributePageFormData) => SubmitPromise; onSubmit: (data: AttributePageFormData) => SubmitPromise;
onValueAdd: () => void; onValueAdd: () => void;
@ -64,7 +65,7 @@ export interface AttributePageFormData extends MetadataFormData {
availableInGrid: boolean; availableInGrid: boolean;
filterableInDashboard: boolean; filterableInDashboard: boolean;
inputType: AttributeInputTypeEnum; inputType: AttributeInputTypeEnum;
entityType: AttributeEntityTypeEnum; entityType: AttributeEntityTypeEnum | null;
filterableInStorefront: boolean; filterableInStorefront: boolean;
name: string; name: string;
slug: string; slug: string;
@ -102,8 +103,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
makeChangeHandler: makeMetadataChangeHandler, makeChangeHandler: makeMetadataChangeHandler,
} = useMetadataChangeTrigger(); } = useMetadataChangeTrigger();
const initialForm: AttributePageFormData = const initialForm: AttributePageFormData = !attribute
attribute === null
? { ? {
availableInGrid: true, availableInGrid: true,
entityType: null, entityType: null,
@ -121,40 +121,33 @@ const AttributePage: React.FC<AttributePageProps> = ({
unit: undefined, unit: undefined,
} }
: { : {
availableInGrid: attribute?.availableInGrid ?? true, availableInGrid: attribute.availableInGrid,
entityType: attribute?.entityType ?? null, entityType: attribute.entityType,
filterableInDashboard: attribute?.filterableInDashboard ?? true, filterableInDashboard: attribute.filterableInDashboard,
filterableInStorefront: attribute?.filterableInStorefront ?? true, filterableInStorefront: attribute.filterableInStorefront,
inputType: attribute?.inputType ?? AttributeInputTypeEnum.DROPDOWN, inputType: attribute?.inputType ?? AttributeInputTypeEnum.DROPDOWN,
metadata: attribute?.metadata?.map(mapMetadataItemToInput), metadata: attribute.metadata.map(mapMetadataItemToInput),
name: attribute?.name ?? "", name: attribute?.name ?? "",
privateMetadata: attribute?.privateMetadata?.map( privateMetadata: attribute.privateMetadata.map(mapMetadataItemToInput),
mapMetadataItemToInput,
),
slug: attribute?.slug ?? "", slug: attribute?.slug ?? "",
storefrontSearchPosition: storefrontSearchPosition: attribute.storefrontSearchPosition.toString(),
attribute?.storefrontSearchPosition.toString() ?? "", type: attribute?.type ?? AttributeTypeEnum.PRODUCT_TYPE,
type: attribute?.type || AttributeTypeEnum.PRODUCT_TYPE, valueRequired: !!attribute.valueRequired,
valueRequired: !!attribute?.valueRequired ?? true, visibleInStorefront: attribute.visibleInStorefront,
visibleInStorefront: attribute?.visibleInStorefront ?? true, unit: attribute?.unit ?? null,
unit: attribute?.unit || null,
}; };
const handleSubmit = (data: AttributePageFormData) => { const handleSubmit = (data: AttributePageFormData) => {
const metadata = const metadata = !attribute || isMetadataModified ? data.metadata : [];
!attribute || isMetadataModified ? data.metadata : undefined;
const privateMetadata = const privateMetadata =
!attribute || isPrivateMetadataModified !attribute || isPrivateMetadataModified ? data.privateMetadata : [];
? data.privateMetadata
: undefined;
const type = attribute === null ? data.type : undefined;
return onSubmit({ return onSubmit({
...data, ...data,
metadata, metadata,
privateMetadata, privateMetadata,
slug: data.slug || slugify(data.name).toLowerCase(), slug: data.slug || slugify(data.name).toLowerCase(),
type, type: data.type,
}); });
}; };
@ -185,13 +178,13 @@ const AttributePage: React.FC<AttributePageProps> = ({
</Backlink> </Backlink>
<PageHeader <PageHeader
title={ title={
attribute === null !attribute
? intl.formatMessage({ ? intl.formatMessage({
id: "8cUEPV", id: "8cUEPV",
defaultMessage: "Create New Attribute", defaultMessage: "Create New Attribute",
description: "page title", description: "page title",
}) })
: maybe(() => attribute.name) : attribute.name
} }
/> />
<Grid> <Grid>
@ -215,7 +208,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
<AttributeValues <AttributeValues
inputType={data.inputType} inputType={data.inputType}
disabled={disabled} disabled={disabled}
values={mapEdgesToItems(values)} values={mapEdgesToItems(values) ?? []}
onValueAdd={onValueAdd} onValueAdd={onValueAdd}
onValueDelete={onValueDelete} onValueDelete={onValueDelete}
onValueReorder={onValueReorder} onValueReorder={onValueReorder}
@ -248,7 +241,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
</div> </div>
</Grid> </Grid>
<Savebar <Savebar
disabled={isSaveDisabled} disabled={!!isSaveDisabled}
state={saveButtonBarState} state={saveButtonBarState}
onCancel={() => navigate(attributeListUrl())} onCancel={() => navigate(attributeListUrl())}
onSubmit={submit} onSubmit={submit}

View file

@ -41,11 +41,9 @@ const AttributeSwatchField: React.FC<AttributeSwatchFieldProps<
const handleFileUpload = async (file: File) => { const handleFileUpload = async (file: File) => {
setProcessing(true); setProcessing(true);
const { const { data } = await uploadFile({ variables: { file } });
data: { fileUpload },
} = await uploadFile({ variables: { file } });
if (fileUpload.errors?.length) { if (data?.fileUpload?.errors?.length) {
notify({ notify({
status: "error", status: "error",
title: intl.formatMessage(errorMessages.imgageUploadErrorTitle), title: intl.formatMessage(errorMessages.imgageUploadErrorTitle),
@ -53,8 +51,8 @@ const AttributeSwatchField: React.FC<AttributeSwatchFieldProps<
}); });
} else { } else {
set({ set({
fileUrl: fileUpload.uploadedFile.url, fileUrl: data?.fileUpload?.uploadedFile?.url,
contentType: fileUpload.uploadedFile.contentType, contentType: data?.fileUpload?.uploadedFile?.contentType ?? "",
value: undefined, value: undefined,
}); });
} }
@ -95,7 +93,7 @@ const AttributeSwatchField: React.FC<AttributeSwatchFieldProps<
<FileUploadField <FileUploadField
disabled={processing} disabled={processing}
loading={processing} loading={processing}
file={{ label: null, value: null, file: null }} file={{ label: "", value: "", file: undefined }}
onFileUpload={handleFileUpload} onFileUpload={handleFileUpload}
onFileDelete={handleFileDelete} onFileDelete={handleFileDelete}
inputProps={{ inputProps={{

View file

@ -71,13 +71,13 @@ const useStyles = makeStyles(
{ name: "AttributeValues" }, { name: "AttributeValues" },
); );
const getSwatchCellStyle = (value: AttributeValueFragment) => { const getSwatchCellStyle = (value?: AttributeValueFragment | undefined) => {
if (!value) { if (!value) {
return; return;
} }
return value.file return value.file
? { backgroundImage: `url(${value.file.url})` } ? { backgroundImage: `url(${value.file.url})` }
: { backgroundColor: value.value }; : { backgroundColor: value.value ?? undefined };
}; };
const AttributeValues: React.FC<AttributeValuesProps> = ({ const AttributeValues: React.FC<AttributeValuesProps> = ({
@ -198,7 +198,9 @@ const AttributeValues: React.FC<AttributeValuesProps> = ({
<IconButton <IconButton
variant="secondary" variant="secondary"
disabled={disabled} disabled={disabled}
onClick={stopPropagation(() => onValueDelete(value.id))} onClick={stopPropagation(() =>
onValueDelete(value?.id ?? ""),
)}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>

View file

@ -14,9 +14,9 @@ const messages = defineMessages({
}); });
export function getAttributeSlugErrorMessage( export function getAttributeSlugErrorMessage(
err: AttributeErrorFragment, err: AttributeErrorFragment | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
switch (err?.code) { switch (err?.code) {
case AttributeErrorCode.UNIQUE: case AttributeErrorCode.UNIQUE:
return intl.formatMessage(messages.attributeSlugUnique); return intl.formatMessage(messages.attributeSlugUnique);
@ -26,9 +26,9 @@ export function getAttributeSlugErrorMessage(
} }
export function getAttributeValueErrorMessage( export function getAttributeValueErrorMessage(
err: AttributeErrorFragment, err: AttributeErrorFragment | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
switch (err?.code) { switch (err?.code) {
case AttributeErrorCode.ALREADY_EXISTS: case AttributeErrorCode.ALREADY_EXISTS:
return intl.formatMessage(messages.attributeValueAlreadyExists); return intl.formatMessage(messages.attributeValueAlreadyExists);

View file

@ -80,8 +80,10 @@ export const attribute: AttributeDetailsQuery["attribute"] = {
visibleInStorefront: true, visibleInStorefront: true,
}; };
export const attributes: Array<AttributeListQuery["attributes"]["edges"][0]["node"] & export const attributes: Array<NonNullable<
ProductDetailsQuery["product"]["attributes"][0]["attribute"]> = [ AttributeListQuery["attributes"]
>["edges"][0]["node"] &
NonNullable<ProductDetailsQuery["product"]>["attributes"][0]["attribute"]> = [
{ {
__typename: "Attribute" as "Attribute", __typename: "Attribute" as "Attribute",
entityType: AttributeEntityTypeEnum.PRODUCT, entityType: AttributeEntityTypeEnum.PRODUCT,

View file

@ -66,7 +66,7 @@ export function filterable(
attribute: Pick<AttributeFragment, "inputType">, attribute: Pick<AttributeFragment, "inputType">,
): boolean { ): boolean {
return ATTRIBUTE_TYPES_WITH_CONFIGURABLE_FACED_NAVIGATION.includes( return ATTRIBUTE_TYPES_WITH_CONFIGURABLE_FACED_NAVIGATION.includes(
attribute.inputType, attribute.inputType!,
); );
} }
@ -86,9 +86,9 @@ export function attributeValueFragmentToFormData(
data: AttributeValueFragment | null, data: AttributeValueFragment | null,
): AttributeValueEditDialogFormData { ): AttributeValueEditDialogFormData {
return { return {
name: data?.name, name: data?.name ?? "",
value: data?.value, value: data?.value ?? "",
contentType: data?.file?.contentType, contentType: data?.file?.contentType ?? "",
fileUrl: data?.file?.url, fileUrl: data?.file?.url,
}; };
} }
@ -265,7 +265,7 @@ export const mergeAttributeValues = (
) => { ) => {
const attribute = attributes.find(attribute => attribute.id === attributeId); const attribute = attributes.find(attribute => attribute.id === attributeId);
return attribute.value return attribute?.value
? [...attribute.value, ...attributeValues] ? [...attribute.value, ...attributeValues]
: attributeValues; : attributeValues;
}; };
@ -332,8 +332,8 @@ export const getAttributesOfUploadedFiles = (
const attribute = fileValuesToUpload[index]; const attribute = fileValuesToUpload[index];
return { return {
file: uploadFileResult.data.fileUpload.uploadedFile.url, file: uploadFileResult.data?.fileUpload?.uploadedFile?.url,
contentType: uploadFileResult.data.fileUpload.uploadedFile.contentType, contentType: uploadFileResult.data?.fileUpload?.uploadedFile?.contentType,
id: attribute.id, id: attribute.id,
values: [], values: [],
}; };
@ -380,7 +380,7 @@ export const getFileAttributeDisplayData = (
export const getPageReferenceAttributeDisplayData = ( export const getPageReferenceAttributeDisplayData = (
attribute: AttributeInput, attribute: AttributeInput,
referencePages: RelayToFlat<SearchPagesQuery["search"]>, referencePages: RelayToFlat<NonNullable<SearchPagesQuery["search"]>>,
) => ({ ) => ({
...attribute, ...attribute,
data: { data: {
@ -388,12 +388,18 @@ export const getPageReferenceAttributeDisplayData = (
references: references:
referencePages?.length > 0 && attribute.value?.length > 0 referencePages?.length > 0 && attribute.value?.length > 0
? mapPagesToChoices( ? mapPagesToChoices(
attribute.value.map(value => { attribute.value.reduce<
RelayToFlat<NonNullable<SearchPagesQuery["search"]>>
>((acc, value) => {
const reference = referencePages.find( const reference = referencePages.find(
reference => reference.id === value, reference => reference.id === value,
); );
return { ...reference };
}), if (reference) {
acc.push(reference);
}
return acc;
}, []),
) )
: [], : [],
}, },
@ -401,7 +407,7 @@ export const getPageReferenceAttributeDisplayData = (
export const getProductReferenceAttributeDisplayData = ( export const getProductReferenceAttributeDisplayData = (
attribute: AttributeInput, attribute: AttributeInput,
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>, referenceProducts: RelayToFlat<NonNullable<SearchProductsQuery["search"]>>,
) => ({ ) => ({
...attribute, ...attribute,
data: { data: {
@ -409,12 +415,18 @@ export const getProductReferenceAttributeDisplayData = (
references: references:
referenceProducts?.length > 0 && attribute.value?.length > 0 referenceProducts?.length > 0 && attribute.value?.length > 0
? mapNodeToChoice( ? mapNodeToChoice(
attribute.value.map(value => { attribute.value.reduce<
RelayToFlat<NonNullable<SearchProductsQuery["search"]>>
>((acc, value) => {
const reference = referenceProducts.find( const reference = referenceProducts.find(
reference => reference.id === value, reference => reference.id === value,
); );
return { ...reference };
}), if (reference) {
acc.push(reference);
}
return acc;
}, []),
) )
: [], : [],
}, },
@ -422,7 +434,7 @@ export const getProductReferenceAttributeDisplayData = (
export const getProductVariantReferenceAttributeDisplayData = ( export const getProductVariantReferenceAttributeDisplayData = (
attribute: AttributeInput, attribute: AttributeInput,
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>, referenceProducts: RelayToFlat<NonNullable<SearchProductsQuery["search"]>>,
) => ({ ) => ({
...attribute, ...attribute,
data: { data: {
@ -430,12 +442,19 @@ export const getProductVariantReferenceAttributeDisplayData = (
references: references:
referenceProducts?.length > 0 && attribute.value?.length > 0 referenceProducts?.length > 0 && attribute.value?.length > 0
? mapNodeToChoice( ? mapNodeToChoice(
attribute.value.map(value => { attribute.value.reduce<Array<Node & Record<"name", string>>>(
(acc, value) => {
const reference = mapReferenceProductsToVariants( const reference = mapReferenceProductsToVariants(
referenceProducts, referenceProducts,
).find(reference => reference.id === value); ).find(reference => reference.id === value);
return { ...reference };
}), if (reference) {
acc.push(reference);
}
return acc;
},
[],
),
) )
: [], : [],
}, },
@ -443,8 +462,8 @@ export const getProductVariantReferenceAttributeDisplayData = (
export const getReferenceAttributeDisplayData = ( export const getReferenceAttributeDisplayData = (
attribute: AttributeInput, attribute: AttributeInput,
referencePages: RelayToFlat<SearchPagesQuery["search"]>, referencePages: RelayToFlat<NonNullable<SearchPagesQuery["search"]>>,
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>, referenceProducts: RelayToFlat<NonNullable<SearchProductsQuery["search"]>>,
) => { ) => {
if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) { if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) {
return getPageReferenceAttributeDisplayData(attribute, referencePages); return getPageReferenceAttributeDisplayData(attribute, referencePages);
@ -466,8 +485,8 @@ export const getReferenceAttributeDisplayData = (
export const getAttributesDisplayData = ( export const getAttributesDisplayData = (
attributes: AttributeInput[], attributes: AttributeInput[],
attributesWithNewFileValue: FormsetData<null, File>, attributesWithNewFileValue: FormsetData<null, File>,
referencePages: RelayToFlat<SearchPagesQuery["search"]>, referencePages: RelayToFlat<NonNullable<SearchPagesQuery["search"]>>,
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>, referenceProducts: RelayToFlat<NonNullable<SearchProductsQuery["search"]>>,
) => ) =>
attributes.map(attribute => { attributes.map(attribute => {
if (attribute.data.inputType === AttributeInputTypeEnum.REFERENCE) { if (attribute.data.inputType === AttributeInputTypeEnum.REFERENCE) {
@ -499,11 +518,11 @@ export const getReferenceAttributeEntityTypeFromAttribute = (
attributes?.find(attribute => attribute.id === attributeId)?.data?.entityType; attributes?.find(attribute => attribute.id === attributeId)?.data?.entityType;
export const mapReferenceProductsToVariants = ( export const mapReferenceProductsToVariants = (
referenceProducts: RelayToFlat<SearchProductsQuery["search"]>, referenceProducts: RelayToFlat<NonNullable<SearchProductsQuery["search"]>>,
) => ) =>
referenceProducts.flatMap(product => referenceProducts.flatMap(product =>
product.variants.map(variant => ({ (product.variants || []).map(variant => ({
...variant, ...variant,
name: product.name + " " + variant.name, name: `${product.name} ${variant.name}`,
})), })),
); );

View file

@ -136,7 +136,7 @@ const createAttribute = ({
value, value,
}: CreateAttribute): AttributeInput => ({ }: CreateAttribute): AttributeInput => ({
data: { data: {
entityType: null, entityType: undefined,
inputType, inputType,
isRequired: false, isRequired: false,
// those values don't matter // those values don't matter
@ -146,7 +146,7 @@ const createAttribute = ({
}, },
id: ATTR_ID, id: ATTR_ID,
label: "MyAttribute", label: "MyAttribute",
value: value !== null ? [value] : [], value: value !== null && value !== undefined ? [value] : [],
}); });
const createSelectAttribute = (value: string) => const createSelectAttribute = (value: string) =>
@ -478,7 +478,7 @@ describe("Sending only changed attributes", () => {
}); });
describe("works with file attributes", () => { describe("works with file attributes", () => {
it("removes existing image (img -> null)", () => { it("removes existing image (img -> null)", () => {
const attribute = createFileAttribute(null); const attribute = createFileAttribute("");
const prevAttribute = createNumericAttribute("bob.jpg"); const prevAttribute = createNumericAttribute("bob.jpg");
const result = prepareAttributesInput({ const result = prepareAttributesInput({
@ -494,7 +494,7 @@ describe("Sending only changed attributes", () => {
}); });
it("adds new image (null -> img)", () => { it("adds new image (null -> img)", () => {
const attribute = createFileAttribute("bob.jpg"); const attribute = createFileAttribute("bob.jpg");
const prevAttribute = createNumericAttribute(null); const prevAttribute = createNumericAttribute("");
const uploadUrl = "http://some-url.com/media/file_upload/bob.jpg"; const uploadUrl = "http://some-url.com/media/file_upload/bob.jpg";
const result = prepareAttributesInput({ const result = prepareAttributesInput({

View file

@ -48,7 +48,7 @@ export function createAttributeMultiChangeHandler(
const newAttributeValues = toggle( const newAttributeValues = toggle(
value, value,
attribute.value, attribute?.value ?? [],
(a, b) => a === b, (a, b) => a === b,
); );
@ -88,6 +88,7 @@ export function createFetchReferencesHandler(
) { ) {
fetchReferencePages(value); fetchReferencePages(value);
} else if ( } else if (
attribute.data?.entityType &&
[ [
AttributeEntityTypeEnum.PRODUCT, AttributeEntityTypeEnum.PRODUCT,
AttributeEntityTypeEnum.PRODUCT_VARIANT, AttributeEntityTypeEnum.PRODUCT_VARIANT,
@ -116,6 +117,7 @@ export function createFetchMoreReferencesHandler(
if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) { if (attribute.data.entityType === AttributeEntityTypeEnum.PAGE) {
return fetchMoreReferencePages; return fetchMoreReferencePages;
} else if ( } else if (
attribute.data?.entityType &&
[ [
AttributeEntityTypeEnum.PRODUCT, AttributeEntityTypeEnum.PRODUCT,
AttributeEntityTypeEnum.PRODUCT_VARIANT, AttributeEntityTypeEnum.PRODUCT_VARIANT,
@ -145,7 +147,7 @@ export function createAttributeFileChangeHandler(
addAttributeNewFileValue({ addAttributeNewFileValue({
data: null, data: null,
id: attributeId, id: attributeId,
label: null, label: "",
value, value,
}); });
} }
@ -167,8 +169,8 @@ export function createAttributeValueReorderHandler(
); );
const reorderedValues = move( const reorderedValues = move(
attribute.value[reorder.oldIndex], attribute?.value?.[reorder.oldIndex] ?? "",
attribute.value, attribute?.value ?? [],
(a, b) => a === b, (a, b) => a === b,
reorder.newIndex, reorder.newIndex,
); );
@ -194,7 +196,7 @@ function getFileInput(
} }
return { return {
file: attribute.data.selectedValues?.[0]?.file?.url, file: attribute.data.selectedValues?.[0]?.file?.url,
contentType: attribute.data.selectedValues?.[0]?.file.contentType, contentType: attribute.data.selectedValues?.[0]?.file?.contentType,
id: attribute.id, id: attribute.id,
}; };
} }
@ -318,7 +320,9 @@ export const handleDeleteMultipleAttributeValues = async (
attributes: Array< attributes: Array<
| PageSelectedAttributeFragment | PageSelectedAttributeFragment
| ProductFragment["attributes"][0] | ProductFragment["attributes"][0]
| ProductVariantDetailsQuery["productVariant"]["nonSelectionAttributes"][0] | NonNullable<
ProductVariantDetailsQuery["productVariant"]
>["nonSelectionAttributes"][0]
>, >,
deleteAttributeValue: ( deleteAttributeValue: (
variables: AttributeValueDeleteMutationVariables, variables: AttributeValueDeleteMutationVariables,

View file

@ -86,7 +86,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
const [attributeCreate, attributeCreateOpts] = useAttributeCreateMutation({ const [attributeCreate, attributeCreateOpts] = useAttributeCreateMutation({
onCompleted: data => { onCompleted: data => {
if (data.attributeCreate.errors.length === 0) { if (data?.attributeCreate?.errors.length === 0) {
notify({ notify({
status: "success", status: "success",
text: intl.formatMessage({ text: intl.formatMessage({
@ -94,7 +94,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
defaultMessage: "Successfully created attribute", defaultMessage: "Successfully created attribute",
}), }),
}); });
navigate(attributeUrl(data.attributeCreate.attribute.id)); navigate(attributeUrl(data?.attributeCreate?.attribute?.id ?? ""));
} }
}, },
}); });
@ -113,8 +113,10 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
React.useEffect(() => setValueErrors([]), [params.action]); React.useEffect(() => setValueErrors([]), [params.action]);
const handleValueDelete = () => { const handleValueDelete = () => {
if (id) {
const newValues = remove(values[id], values, areValuesEqual); const newValues = remove(values[id], values, areValuesEqual);
setValues(newValues); setValues(newValues);
}
closeModal(); closeModal();
}; };
@ -122,7 +124,9 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
if (isSelected(input, values, areValuesEqual)) { if (isSelected(input, values, areValuesEqual)) {
setValueErrors([attributeValueAlreadyExistsError]); setValueErrors([attributeValueAlreadyExistsError]);
} else { } else {
if (id) {
setValues(updateAtIndex(input, values, id)); setValues(updateAtIndex(input, values, id));
}
closeModal(); closeModal();
} }
}; };
@ -164,7 +168,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
}); });
return { return {
id: result.data.attributeCreate?.attribute?.id || null, id: result.data?.attributeCreate?.attribute?.id ?? undefined,
errors: getMutationErrors(result), errors: getMutationErrors(result),
}; };
}; };
@ -179,8 +183,8 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
<AttributePage <AttributePage
attribute={null} attribute={null}
disabled={attributeCreateOpts.loading} disabled={attributeCreateOpts.loading}
errors={attributeCreateOpts.data?.attributeCreate.errors || []} errors={attributeCreateOpts?.data?.attributeCreate?.errors || []}
onDelete={undefined} onDelete={() => undefined}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onValueAdd={() => openModal("add-value")} onValueAdd={() => openModal("add-value")}
onValueDelete={id => onValueDelete={id =>
@ -212,7 +216,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
file: value?.fileUrl file: value?.fileUrl
? { ? {
url: value.fileUrl, url: value.fileUrl,
contentType: value.contentType, contentType: value.contentType ?? "",
__typename: "File", __typename: "File",
} }
: null, : null,
@ -251,16 +255,16 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ params }) => {
{values.length > 0 && ( {values.length > 0 && (
<> <>
<AttributeValueDeleteDialog <AttributeValueDeleteDialog
attributeName={undefined} attributeName=""
open={params.action === "remove-value"} open={params.action === "remove-value"}
name={getStringOrPlaceholder(values[id]?.name)} name={getStringOrPlaceholder(id ? values[id]?.name : "")}
confirmButtonState="default" confirmButtonState="default"
onClose={closeModal} onClose={closeModal}
onConfirm={handleValueDelete} onConfirm={handleValueDelete}
/> />
<AttributeValueEditDialog <AttributeValueEditDialog
inputType={data.inputType} inputType={data.inputType}
attributeValue={values[id]} attributeValue={id ? values[id] : null}
confirmButtonState="default" confirmButtonState="default"
disabled={false} disabled={false}
errors={valueErrors} errors={valueErrors}

View file

@ -91,7 +91,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const [attributeDelete, attributeDeleteOpts] = useAttributeDeleteMutation({ const [attributeDelete, attributeDeleteOpts] = useAttributeDeleteMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeDelete.errors.length === 0) { if (data?.attributeDelete?.errors.length === 0) {
notify({ notify({
status: "success", status: "success",
text: intl.formatMessage({ text: intl.formatMessage({
@ -109,7 +109,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueDeleteOpts, attributeValueDeleteOpts,
] = useAttributeValueDeleteMutation({ ] = useAttributeValueDeleteMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeValueDelete.errors.length === 0) { if (data?.attributeValueDelete?.errors.length === 0) {
notify({ notify({
status: "success", status: "success",
text: intl.formatMessage({ text: intl.formatMessage({
@ -128,7 +128,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueUpdateOpts, attributeValueUpdateOpts,
] = useAttributeValueUpdateMutation({ ] = useAttributeValueUpdateMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeValueUpdate.errors.length === 0) { if (data?.attributeValueUpdate?.errors.length === 0) {
notifySaved(); notifySaved();
closeModal(); closeModal();
} }
@ -137,7 +137,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const [attributeUpdate, attributeUpdateOpts] = useAttributeUpdateMutation({ const [attributeUpdate, attributeUpdateOpts] = useAttributeUpdateMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeUpdate.errors.length === 0) { if (data?.attributeUpdate?.errors.length === 0) {
notifySaved(); notifySaved();
} }
}, },
@ -148,7 +148,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValueCreateOpts, attributeValueCreateOpts,
] = useAttributeValueCreateMutation({ ] = useAttributeValueCreateMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeValueCreate.errors.length === 0) { if (data?.attributeValueCreate?.errors.length === 0) {
notify({ notify({
status: "success", status: "success",
text: intl.formatMessage({ text: intl.formatMessage({
@ -164,11 +164,11 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
const [attributeValueReorder] = useAttributeValueReorderMutation({ const [attributeValueReorder] = useAttributeValueReorderMutation({
onCompleted: data => { onCompleted: data => {
if (data?.attributeReorderValues.errors.length !== 0) { if (data?.attributeReorderValues?.errors.length !== 0) {
notify({ notify({
status: "error", status: "error",
text: getAttributeErrorMessage( text: getAttributeErrorMessage(
data?.attributeReorderValues.errors[0], data?.attributeReorderValues?.errors[0],
intl, intl,
), ),
}); });
@ -185,16 +185,16 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeReorderValues: { attributeReorderValues: {
__typename: "AttributeReorderValues", __typename: "AttributeReorderValues",
attribute: { attribute: {
...data?.attribute, ...data?.attribute!,
choices: { choices: {
__typename: "AttributeValueCountableConnection", __typename: "AttributeValueCountableConnection",
pageInfo: { pageInfo: {
...data?.attribute.choices.pageInfo, ...data?.attribute?.choices?.pageInfo!,
}, },
edges: move( edges: move(
data?.attribute.choices.edges[oldIndex], data?.attribute?.choices?.edges[oldIndex]!,
data?.attribute.choices.edges, data?.attribute?.choices?.edges ?? [],
(a, b) => a.node.id === b.node.id, (a, b) => a?.node.id === b?.node.id,
newIndex, newIndex,
), ),
}, },
@ -205,7 +205,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
variables: { variables: {
id, id,
move: { move: {
id: data?.attribute.choices.edges[oldIndex].node.id, id: data?.attribute?.choices?.edges[oldIndex].node.id ?? "",
sortOrder: newIndex - oldIndex, sortOrder: newIndex - oldIndex,
}, },
firstValues: valuesPaginationState.first, firstValues: valuesPaginationState.first,
@ -237,7 +237,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
); );
const handleSubmit = createMetadataUpdateHandler( const handleSubmit = createMetadataUpdateHandler(
data?.attribute, data?.attribute!,
handleUpdate, handleUpdate,
variables => updateMetadata({ variables }), variables => updateMetadata({ variables }),
variables => updatePrivateMetadata({ variables }), variables => updatePrivateMetadata({ variables }),
@ -247,7 +247,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
<AttributePage <AttributePage
attribute={data?.attribute} attribute={data?.attribute}
disabled={loading} disabled={loading}
errors={attributeUpdateOpts.data?.attributeUpdate.errors || []} errors={attributeUpdateOpts.data?.attributeUpdate?.errors || []}
onDelete={() => openModal("remove")} onDelete={() => openModal("remove")}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onValueAdd={() => openModal("add-value")} onValueAdd={() => openModal("add-value")}
@ -266,7 +266,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
values={data?.attribute?.choices} values={data?.attribute?.choices}
settings={settings} settings={settings}
onUpdateListSettings={updateListSettings} onUpdateListSettings={updateListSettings}
pageInfo={pageInfo} pageInfo={pageInfo ?? { hasNextPage: false, hasPreviousPage: false }}
onNextPage={loadNextPage} onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage} onPreviousPage={loadPreviousPage}
> >
@ -291,7 +291,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
name={getStringOrPlaceholder( name={getStringOrPlaceholder(
data?.attribute?.choices?.edges?.find( data?.attribute?.choices?.edges?.find(
value => params.id === value.node.id, value => params.id === value.node.id,
)?.node.name, )?.node?.name ?? "",
)} )}
useName={true} useName={true}
confirmButtonState={attributeValueDeleteOpts.status} confirmButtonState={attributeValueDeleteOpts.status}
@ -299,7 +299,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
onConfirm={() => onConfirm={() =>
attributeValueDelete({ attributeValueDelete({
variables: { variables: {
id: params.id, id: params?.id ?? "",
firstValues: valuesPaginationState.first, firstValues: valuesPaginationState.first,
lastValues: valuesPaginationState.last, lastValues: valuesPaginationState.last,
afterValues: valuesPaginationState.after, afterValues: valuesPaginationState.after,
@ -314,7 +314,7 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
confirmButtonState={attributeValueCreateOpts.status} confirmButtonState={attributeValueCreateOpts.status}
disabled={loading} disabled={loading}
errors={ errors={
attributeValueCreateOpts.data?.attributeValueCreate.errors || [] attributeValueCreateOpts.data?.attributeValueCreate?.errors || []
} }
open={params.action === "add-value"} open={params.action === "add-value"}
onClose={closeModal} onClose={closeModal}
@ -336,21 +336,22 @@ const AttributeDetails: React.FC<AttributeDetailsProps> = ({ id, params }) => {
attributeValue={attributeValueFragmentToFormData( attributeValue={attributeValueFragmentToFormData(
data?.attribute?.choices?.edges?.find( data?.attribute?.choices?.edges?.find(
value => params.id === value.node.id, value => params.id === value.node.id,
)?.node, )?.node ?? null,
)} )}
confirmButtonState={attributeValueUpdateOpts.status} confirmButtonState={attributeValueUpdateOpts.status}
disabled={loading} disabled={loading}
errors={ errors={
attributeValueUpdateOpts.data?.attributeValueUpdate.errors || [] attributeValueUpdateOpts.data?.attributeValueUpdate?.errors || []
} }
open={params.action === "edit-value"} open={params.action === "edit-value"}
onClose={closeModal} onClose={closeModal}
onSubmit={input => onSubmit={input =>
attributeValueUpdate({ attributeValueUpdate({
variables: { variables: {
id: data?.attribute.choices.edges.find( id:
data?.attribute?.choices?.edges?.find(
value => params.id === value.node.id, value => params.id === value.node.id,
).node.id, )?.node?.id || "",
input, input,
firstValues: valuesPaginationState.first, firstValues: valuesPaginationState.first,
lastValues: valuesPaginationState.last, lastValues: valuesPaginationState.last,

View file

@ -73,7 +73,7 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
attributeBulkDeleteOpts, attributeBulkDeleteOpts,
] = useAttributeBulkDeleteMutation({ ] = useAttributeBulkDeleteMutation({
onCompleted: data => { onCompleted: data => {
if (data.attributeBulkDelete.errors.length === 0) { if (data.attributeBulkDelete?.errors.length === 0) {
closeModal(); closeModal();
notify({ notify({
status: "success", status: "success",
@ -132,7 +132,7 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
}; };
const paginationValues = usePaginator({ const paginationValues = usePaginator({
pageInfo: maybe(() => data.attributes.pageInfo), pageInfo: data?.attributes?.pageInfo,
paginationState, paginationState,
queryString: params, queryString: params,
}); });
@ -142,7 +142,7 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
return ( return (
<PaginatorContext.Provider value={paginationValues}> <PaginatorContext.Provider value={paginationValues}>
<AttributeListPage <AttributeListPage
attributes={mapEdgesToItems(data?.attributes)} attributes={mapEdgesToItems(data?.attributes) ?? []}
currentTab={currentTab} currentTab={currentTab}
disabled={loading || attributeBulkDeleteOpts.loading} disabled={loading || attributeBulkDeleteOpts.loading}
filterOpts={getFilterOpts(params)} filterOpts={getFilterOpts(params)}
@ -176,12 +176,14 @@ const AttributeList: React.FC<AttributeListProps> = ({ params }) => {
/> />
<AttributeBulkDeleteDialog <AttributeBulkDeleteDialog
confirmButtonState={attributeBulkDeleteOpts.status} confirmButtonState={attributeBulkDeleteOpts.status}
open={params.action === "remove" && maybe(() => params.ids.length > 0)} open={
params.action === "remove" && !!params.ids && params.ids.length > 0
}
onConfirm={() => onConfirm={() =>
attributeBulkDelete({ variables: { ids: params.ids } }) attributeBulkDelete({ variables: { ids: params?.ids ?? [] } })
} }
onClose={closeModal} onClose={closeModal}
quantity={maybe(() => params.ids.length)} quantity={params.ids?.length ?? 0}
/> />
<SaveFilterTabDialog <SaveFilterTabDialog
open={params.action === "save-search"} open={params.action === "save-search"}

View file

@ -4,7 +4,7 @@ import {
} from "@dashboard/attributes/components/AttributeListPage"; } from "@dashboard/attributes/components/AttributeListPage";
import { FilterElement } from "@dashboard/components/Filter"; import { FilterElement } from "@dashboard/components/Filter";
import { AttributeFilterInput } from "@dashboard/graphql"; import { AttributeFilterInput } from "@dashboard/graphql";
import { maybe, parseBoolean } from "@dashboard/misc"; import { parseBoolean } from "@dashboard/misc";
import { import {
createFilterTabUtils, createFilterTabUtils,
@ -25,19 +25,19 @@ export function getFilterOpts(
return { return {
filterableInStorefront: { filterableInStorefront: {
active: params.filterableInStorefront !== undefined, active: params.filterableInStorefront !== undefined,
value: maybe(() => parseBoolean(params.filterableInStorefront, true)), value: parseBoolean(params.filterableInStorefront, true),
}, },
isVariantOnly: { isVariantOnly: {
active: params.isVariantOnly !== undefined, active: params.isVariantOnly !== undefined,
value: maybe(() => parseBoolean(params.isVariantOnly, true)), value: parseBoolean(params.isVariantOnly, true),
}, },
valueRequired: { valueRequired: {
active: params.valueRequired !== undefined, active: params.valueRequired !== undefined,
value: maybe(() => parseBoolean(params.valueRequired, true)), value: parseBoolean(params.valueRequired, true),
}, },
visibleInStorefront: { visibleInStorefront: {
active: params.visibleInStorefront !== undefined, active: params.visibleInStorefront !== undefined,
value: maybe(() => parseBoolean(params.visibleInStorefront, true)), value: parseBoolean(params.visibleInStorefront, true),
}, },
}; };
} }

View file

@ -17,7 +17,7 @@ export function getSortQueryField(
case AttributeListUrlSortField.visible: case AttributeListUrlSortField.visible:
return AttributeSortField.VISIBLE_IN_STOREFRONT; return AttributeSortField.VISIBLE_IN_STOREFRONT;
default: default:
return undefined; return AttributeSortField.NAME;
} }
} }

View file

@ -431,7 +431,10 @@ export function findValueInEnum<TEnum extends {}>(
return needle as unknown as TEnum[keyof TEnum]; return needle as unknown as TEnum[keyof TEnum];
} }
export function parseBoolean(a: string, defaultValue: boolean): boolean { export function parseBoolean(
a: string | undefined,
defaultValue: boolean,
): boolean {
if (a === undefined) { if (a === undefined) {
return defaultValue; return defaultValue;
} }