Skip to content

Commit

Permalink
Fix "view products" link when deleting type (#5186)
Browse files Browse the repository at this point in the history
* fix 'view products' link

* fix 'view products' link

* loading state and type fixes

* fix type

* fix tests

* small fixes

* remove unnecessary 'slug' from page type

* remove usage of legacy filtering url

* change test descriptions

---------

Co-authored-by: Paweł Chyła <[email protected]>
  • Loading branch information
Cloud11PL and poulch authored Oct 14, 2024
1 parent 4f432e5 commit 5b9c5fe
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changeset/large-coins-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

When deleting product type with products the "View products" link now navigates to product list with correctly applied product type filter
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,7 @@ import {
FetchingParams,
OrderFetchingParams,
} from "./TokenArray/fetchingParams";
import { UrlEntry } from "./UrlToken";

type Structure = Array<string | UrlEntry | Structure>;

const prepareStructure = (filterValue: FilterContainer): Structure =>
filterValue.map(f => {
if (typeof f === "string") {
return f;
}

if (Array.isArray(f)) {
return prepareStructure(f);
}

return f.asUrlEntry();
});
import { prepareStructure } from "./utils";

export const useUrlValueProvider = (
locationSearch: string,
Expand Down
17 changes: 17 additions & 0 deletions src/components/ConditionalFilter/ValueProvider/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FilterContainer } from "../FilterElement";
import { UrlEntry } from "./UrlToken";

type Structure = Array<string | UrlEntry | Structure>;

export const prepareStructure = (filterValue: FilterContainer): Structure =>
filterValue.map(f => {
if (typeof f === "string") {
return f;
}

if (Array.isArray(f)) {
return prepareStructure(f);
}

return f.asUrlEntry();
});
32 changes: 13 additions & 19 deletions src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
// @ts-strict-ignore
import { ConfirmButton, ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
import useNavigator from "@dashboard/hooks/useNavigator";
import { buttonMessages } from "@dashboard/intl";
import { getById } from "@dashboard/misc";
import { Box, Spinner } from "@saleor/macaw-ui-next";
import React, { useState } from "react";
import { useIntl } from "react-intl";
import { Link } from "react-router-dom";

import DeleteButton from "../DeleteButton";
import { DashboardModal } from "../Modal";
import DeleteWarningDialogConsentContent from "./DeleteWarningDialogConsentContent";
import { CommonTypeDeleteWarningMessages, TypeDeleteWarningMessages } from "./types";

export interface TypeBaseData {
id: string;
name: string;
}
import { CommonTypeDeleteWarningMessages, TypeBaseData, TypeDeleteWarningMessages } from "./types";

export interface TypeDeleteMessages {
baseMessages: CommonTypeDeleteWarningMessages;
Expand All @@ -30,13 +25,11 @@ export interface TypeDeleteWarningDialogProps<T extends TypeBaseData> extends Ty
deleteButtonState: ConfirmButtonTransitionState;
onClose: () => void;
onDelete: () => void;
viewAssignedItemsUrl: string;
viewAssignedItemsUrl: string | null;
typesToDelete: string[];
assignedItemsCount: number | undefined;
isLoading?: boolean;
typesData: T[];
// temporary, until we add filters to pages list - SALEOR-3279
showViewAssignedItemsButton?: boolean;
isLoading?: boolean;
}

function TypeDeleteWarningDialog<T extends TypeBaseData>({
Expand All @@ -53,10 +46,8 @@ function TypeDeleteWarningDialog<T extends TypeBaseData>({
viewAssignedItemsUrl,
typesToDelete,
typesData,
showViewAssignedItemsButton = true,
}: TypeDeleteWarningDialogProps<T>) {
const intl = useIntl();
const navigate = useNavigator();
const [isConsentChecked, setIsConsentChecked] = useState(false);

const showMultiple = typesToDelete.length > 1;
Expand All @@ -79,9 +70,10 @@ function TypeDeleteWarningDialog<T extends TypeBaseData>({
};
};
const { description, consentLabel } = selectMessages();

const singleItemSelectedId = typesToDelete[0];
const singleItemSelectedName = typesData.find(getById(singleItemSelectedId))?.name;
const shouldShowViewAssignedItemsButton = showViewAssignedItemsButton && hasAssignedItems;
const shouldShowViewAssignedItemsButton = hasAssignedItems;

return (
<DashboardModal open={isOpen} onChange={onClose}>
Expand Down Expand Up @@ -111,12 +103,14 @@ function TypeDeleteWarningDialog<T extends TypeBaseData>({

<DashboardModal.Actions>
{shouldShowViewAssignedItemsButton && (
<ConfirmButton
onClick={() => navigate(viewAssignedItemsUrl)}
transitionState="default"
<Link
to={viewAssignedItemsUrl}
style={{ pointerEvents: viewAssignedItemsUrl ? undefined : "none" }}
>
{intl.formatMessage(baseMessages.viewAssignedItemsButtonLabel)}
</ConfirmButton>
<ConfirmButton transitionState="default" disabled={!viewAssignedItemsUrl}>
{intl.formatMessage(baseMessages.viewAssignedItemsButtonLabel)}
</ConfirmButton>
</Link>
)}
<DeleteButton
onClick={onDelete}
Expand Down
6 changes: 6 additions & 0 deletions src/components/TypeDeleteWarningDialog/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ export type CommonTypeDeleteWarningMessages = Record<
export type TypeDeleteWarningMessages = Partial<
Record<"description" | "consentLabel", MessageDescriptor>
>;

export interface TypeBaseData {
id: string;
name: string;
slug?: string;
}
52 changes: 52 additions & 0 deletions src/components/TypeDeleteWarningDialog/useViewProducts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { renderHook } from "@testing-library/react-hooks";

import { useViewProducts } from "./useViewProducts";

describe("useViewProducts", () => {
const productTypeBaseData = [
{
id: "123",
name: "Audiobooks",
slug: "audiobooks",
},
];

it("should return URL with product type filtered", () => {
// Arrange & Act
const { result } = renderHook(() => useViewProducts({ productTypeBaseData }));

// Assert
expect(result.current).not.toBeNull();

const expectedQuery = "0[s0.productType][0]=audiobooks";
const receivedQuery = decodeURIComponent(result.current!.split("?")[1]);

expect(receivedQuery).toBe(expectedQuery);
});

it("should return URL with product type filtered when multiple product types are selected", () => {
// Arrange
const multipleProductTypeBaseData = [
...productTypeBaseData,
{
id: "456",
name: "Shirts",
slug: "shirts",
},
];

// Act
const { result } = renderHook(() =>
useViewProducts({ productTypeBaseData: multipleProductTypeBaseData }),
);

// Assert
expect(result.current).not.toBeNull();

const expectedQuery = "0[s2.productType][0]=audiobooks&0[s2.productType][1]=shirts";

const receivedQuery = decodeURIComponent(result.current!.split("?")[1]);

expect(receivedQuery).toBe(expectedQuery);
});
});
49 changes: 49 additions & 0 deletions src/components/TypeDeleteWarningDialog/useViewProducts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { productListPath } from "@dashboard/products/urls";
import { stringify } from "qs";
import { useMemo } from "react";
import urljoin from "url-join";

import { FilterElement } from "../ConditionalFilter/FilterElement";
import { prepareStructure } from "../ConditionalFilter/ValueProvider/utils";
import { TypeBaseData } from "./types";

export interface ProductTypeBaseData extends TypeBaseData {
slug: string;
}

interface ViewProductsProps {
productTypeBaseData: ProductTypeBaseData[] | undefined;
}

const baseDataToCondition = (baseData: ProductTypeBaseData) => ({
label: baseData.name,
slug: baseData.slug,
value: baseData.id,
});

export const useViewProducts = ({ productTypeBaseData }: ViewProductsProps) => {
const viewProductsUrl = useMemo(() => {
if (!productTypeBaseData) {
return null;
}

const productFilterElement = FilterElement.createStaticBySlug("productType");

const condition =
productTypeBaseData.length > 1
? productFilterElement.condition.options[1]
: productFilterElement.condition.options[0]; // "in" or "is"

productFilterElement.updateCondition(condition);

productFilterElement.updateRightOperator(productTypeBaseData.map(baseDataToCondition));

const url = stringify({
...prepareStructure([productFilterElement]),
});

return urljoin(productListPath, `?${url}`);
}, [productTypeBaseData]);

return viewProductsUrl;
};
1 change: 1 addition & 0 deletions src/fragments/productTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const productTypeFragment = gql`
fragment ProductType on ProductType {
id
name
slug
kind
hasVariants
isShippingRequired
Expand Down
1 change: 1 addition & 0 deletions src/graphql/hooks.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2460,6 +2460,7 @@ export const ProductTypeFragmentDoc = gql`
fragment ProductType on ProductType {
id
name
slug
kind
hasVariants
isShippingRequired
Expand Down
Loading

0 comments on commit 5b9c5fe

Please sign in to comment.