Skip to content

Commit

Permalink
Improve backlink in order details and draft order details (#5193)
Browse files Browse the repository at this point in the history
* improve backlink in order details

* fix tests

* pass navigator opts to datagrid

* make hook generic

* fix ts

* fix test

---------

Co-authored-by: Paweł Chyła <chyla1988@gmail.com>
  • Loading branch information
Cloud11PL and poulch committed Oct 23, 2024
1 parent e466806 commit 4ef03df
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/thick-kiwis-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

When navigating to order details from the order list, the back button will now return you to the previous page with the same filters and pagination applied.
6 changes: 4 additions & 2 deletions src/components/Datagrid/Datagrid.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "@glideapps/glide-data-grid/dist/index.css";

import useNavigator from "@dashboard/hooks/useNavigator";
import useNavigator, { NavigatorOpts } from "@dashboard/hooks/useNavigator";
import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack";
import { getCellAction } from "@dashboard/products/components/ProductListDatagrid/datagrid";
import DataEditor, {
Expand Down Expand Up @@ -98,6 +98,7 @@ export interface DatagridProps {
recentlyAddedColumn?: string | null; // Enables scroll to recently added column
onClearRecentlyAddedColumn?: () => void;
renderHeader?: (props: DatagridRenderHeaderProps) => ReactNode;
navigatorOpts?: NavigatorOpts;
}

export const Datagrid: React.FC<DatagridProps> = ({
Expand Down Expand Up @@ -130,6 +131,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
onClearRecentlyAddedColumn,
rowHeight = cellHeight,
renderHeader,
navigatorOpts,
...datagridProps
}): ReactElement => {
const classes = useStyles({ actionButtonPosition });
Expand Down Expand Up @@ -531,7 +533,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
e.preventDefault();

if (e.currentTarget.dataset.reactRouterPath) {
navigate(e.currentTarget.dataset.reactRouterPath);
navigate(e.currentTarget.dataset.reactRouterPath, navigatorOpts);
}
}}
/>
Expand Down
72 changes: 72 additions & 0 deletions src/hooks/useBackLinkWithState.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { renderHook } from "@testing-library/react-hooks";
import { useLocation } from "react-router";

import { useBackLinkWithState } from "./useBackLinkWithState";

jest.mock("react-router", () => ({
useLocation: jest.fn(),
}));

describe("useBackLinkWithState", () => {
// Arrange
it("should return path if there is no previous location in state", () => {
(useLocation as jest.Mock).mockReturnValue({
state: {},
});

// Act
const { result } = renderHook(() =>
useBackLinkWithState({
path: "/orders",
}),
);

// Assert
expect(result.current).toBe("/orders");
});

it("should return the previous URL if it is an order list path", () => {
// Arrange

(useLocation as jest.Mock).mockReturnValue({
state: {
prevLocation: {
pathname: "/orders",
search: "?asc=false&after=cursor",
},
},
});

// Act
const { result } = renderHook(() =>
useBackLinkWithState({
path: "/orders",
}),
);

// Assert
expect(result.current).toBe("/orders?asc=false&after=cursor");
});

it("should return the previous URL if it is a draft order list path", () => {
// Arrange
(useLocation as jest.Mock).mockReturnValue({
state: {
prevLocation: {
pathname: "/orders/drafts",
search: "?asc=false&after=cursor",
},
},
});

// Act
const { result } = renderHook(() =>
useBackLinkWithState({
path: "/orders/drafts",
}),
);

// Assert
expect(result.current).toBe("/orders/drafts?asc=false&after=cursor");
});
});
43 changes: 43 additions & 0 deletions src/hooks/useBackLinkWithState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from "react";
import { useLocation } from "react-router";
import urljoin from "url-join";

type LocationWithState = Location & {
state?: {
prevLocation?: Location;
};
};

const getPreviousUrl = (location: LocationWithState) => {
if (!location.state?.prevLocation) {
return null;
}

const { pathname, search } = location.state.prevLocation;

return urljoin(pathname, search);
};

interface UseBackLinkWithState {
path: string;
}

export const useBackLinkWithState = ({ path }: UseBackLinkWithState) => {
const location = useLocation();
const [backLink, setBackLink] = useState<string>(path);

useEffect(() => {
if (location.state) {
const previousUrl = getPreviousUrl(location as LocationWithState);

// Prevent other links from being set as back link
const isCorrectPath = previousUrl?.includes(path);

if (isCorrectPath && previousUrl) {
setBackLink(previousUrl);
}
}
}, [location, path]);

return backLink;
};
25 changes: 14 additions & 11 deletions src/hooks/useNavigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@ import { ExitFormDialogContext } from "@dashboard/components/Form/ExitFormDialog
import { useContext } from "react";
import useRouter from "use-react-router";

export type UseNavigatorResult = (
url: string,
opts?: {
replace?: boolean;
preserveQs?: boolean;
resetScroll?: boolean;
},
) => void;
export type NavigatorOpts = {
replace?: boolean;
preserveQs?: boolean;
resetScroll?: boolean;
state?: Record<string, unknown>;
};

export type UseNavigatorResult = (url: string, opts?: NavigatorOpts) => void;
function useNavigator(): UseNavigatorResult {
const {
location: { search },
history,
} = useRouter();
const { shouldBlockNavigation } = useContext(ExitFormDialogContext);

return (url: string, { replace = false, preserveQs = false, resetScroll = false } = {}) => {
return (
url: string,
{ replace = false, preserveQs = false, resetScroll = false, state } = {},
) => {
if (shouldBlockNavigation()) {
return;
}

const targetUrl = preserveQs ? url + search : url;

if (replace) {
history.replace(targetUrl);
history.replace(targetUrl, state);
} else {
history.push(targetUrl);
history.push(targetUrl, state);
}

if (resetScroll) {
Expand Down
7 changes: 6 additions & 1 deletion src/orders/components/OrderDetailsPage/OrderDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
OrderStatus,
TransactionActionEnum,
} from "@dashboard/graphql";
import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState";
import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator";
import { defaultGraphiQLQuery } from "@dashboard/orders/queries";
Expand Down Expand Up @@ -172,14 +173,18 @@ const OrderDetailsPage: React.FC<OrderDetailsPageProps> = props => {
context.setDevModeVisibility(true);
};

const backLinkUrl = useBackLinkWithState({
path: orderListUrl(),
});

return (
<Form confirmLeave initial={initial} onSubmit={handleSubmit} mergeData={false}>
{({ set, triggerChange, data, submit }) => {
const handleChangeMetadata = createMetadataHandler(data, set, triggerChange);

return (
<DetailPageLayout>
<TopNav href={orderListUrl()} title={<Title order={order} />}>
<TopNav href={backLinkUrl} title={<Title order={order} />}>
<CardMenu
menuItems={[
...selectCardMenuItems,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Item } from "@glideapps/glide-data-grid";
import { Box } from "@saleor/macaw-ui-next";
import React, { useCallback, useMemo } from "react";
import { useIntl } from "react-intl";
import { useLocation } from "react-router";

import { createGetCellContent, orderDraftListStaticColumnsAdapter } from "./datagrid";
import { messages } from "./messages";
Expand All @@ -37,6 +38,7 @@ export const OrderDraftListDatagrid = ({
onUpdateListSettings,
onSelectOrderDraftIds,
}: OrderDraftListDatagridProps) => {
const location = useLocation();
const intl = useIntl();
const { locale } = useLocale();
const datagridState = useDatagridChangeState();
Expand Down Expand Up @@ -123,6 +125,11 @@ export const OrderDraftListDatagrid = ({
onToggle={handlers.onToggle}
/>
)}
navigatorOpts={{
state: {
prevLocation: location,
},
}}
/>

<Box paddingX={6}>
Expand Down
8 changes: 7 additions & 1 deletion src/orders/components/OrderDraftPage/OrderDraftPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
OrderLineInput,
SearchCustomersQuery,
} from "@dashboard/graphql";
import { useBackLinkWithState } from "@dashboard/hooks/useBackLinkWithState";
import { SubmitPromise } from "@dashboard/hooks/useForm";
import useNavigator from "@dashboard/hooks/useNavigator";
import OrderChannelSectionCard from "@dashboard/orders/components/OrderChannelSectionCard";
Expand Down Expand Up @@ -51,6 +52,8 @@ export interface OrderDraftPageProps extends FetchMoreProps {
onShowMetadata: (id: string) => void;
}

const draftOrderListUrl = orderDraftListUrl();

const OrderDraftPage: React.FC<OrderDraftPageProps> = props => {
const {
loading,
Expand Down Expand Up @@ -78,11 +81,14 @@ const OrderDraftPage: React.FC<OrderDraftPageProps> = props => {
} = props;
const navigate = useNavigator();
const intl = useIntl();
const backLinkUrl = useBackLinkWithState({
path: draftOrderListUrl,
});

return (
<DetailPageLayout>
<TopNav
href={orderDraftListUrl()}
href={backLinkUrl}
title={
<Box display="flex" alignItems="center" gap={3}>
<span>{order?.number ? "#" + order?.number : undefined}</span>
Expand Down
7 changes: 7 additions & 0 deletions src/orders/components/OrderListDatagrid/OrderListDatagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Item } from "@glideapps/glide-data-grid";
import { Box } from "@saleor/macaw-ui-next";
import React, { useCallback, useMemo } from "react";
import { useIntl } from "react-intl";
import { useLocation } from "react-router";

import { orderListStaticColumnAdapter, useGetCellContent } from "./datagrid";
import { messages } from "./messages";
Expand All @@ -38,6 +39,7 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
hasRowHover,
rowAnchor,
}) => {
const location = useLocation();
const intl = useIntl();
const datagrid = useDatagridChangeState();
const ordersLength = getOrdersRowsLength(orders, disabled);
Expand Down Expand Up @@ -127,6 +129,11 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
)}
onRowClick={handleRowClick}
rowAnchor={handleRowAnchor}
navigatorOpts={{
state: {
prevLocation: location,
},
}}
/>

<Box paddingX={6}>
Expand Down

0 comments on commit 4ef03df

Please sign in to comment.