-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix bulk gift card re-rendering #5755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "saleor-dashboard": patch | ||
| --- | ||
|
|
||
| Fixed bulk gift card issue modal re-rendering issues. After this change modal shouldn't rerender itself. |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| import { useApolloClient } from "@apollo/client"; | ||
| import { renderHook } from "@testing-library/react-hooks"; | ||
|
|
||
| import { useChannelCurrenciesWithCache } from "./useChannelCurrenciesWithCache"; | ||
|
|
||
| jest.mock("@apollo/client", () => ({ | ||
| useApolloClient: jest.fn(), | ||
| })); | ||
|
|
||
| jest.mock("@dashboard/graphql", () => ({ | ||
| useChannelCurrenciesQuery: jest.fn(), | ||
| ChannelCurrenciesDocument: {}, | ||
| })); | ||
|
|
||
| const { useChannelCurrenciesQuery } = jest.requireMock("@dashboard/graphql"); | ||
|
|
||
| describe("useChannelCurrenciesWithCache", () => { | ||
| const mockClient = { | ||
| readQuery: jest.fn(), | ||
| }; | ||
|
|
||
| const mockQueryResult = { | ||
| data: { shop: { channelCurrencies: ["USD", "EUR"] } }, | ||
| loading: false, | ||
| error: undefined, | ||
| }; | ||
|
|
||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| (useApolloClient as jest.Mock).mockReturnValue(mockClient); | ||
| useChannelCurrenciesQuery.mockReturnValue(mockQueryResult); | ||
| }); | ||
|
|
||
| it("should use cached data when available and skip query", () => { | ||
| const cachedData = { shop: { channelCurrencies: ["USD", "EUR", "GBP"] } }; | ||
|
|
||
| mockClient.readQuery.mockReturnValue(cachedData); | ||
|
|
||
| const { result } = renderHook(() => useChannelCurrenciesWithCache()); | ||
|
|
||
| expect(mockClient.readQuery).toHaveBeenCalledWith({ | ||
| query: {}, | ||
| variables: {}, | ||
| }); | ||
|
|
||
| expect(useChannelCurrenciesQuery).toHaveBeenCalledWith({ | ||
| skip: true, | ||
| }); | ||
|
|
||
| expect(result.current.loadingChannelCurrencies).toBe(false); | ||
| expect(result.current.channelCurrencies).toEqual(["USD", "EUR", "GBP"]); | ||
| }); | ||
|
|
||
| it("should execute query when no cached data is available", () => { | ||
| mockClient.readQuery.mockImplementation(() => { | ||
| throw new Error("Cache miss"); | ||
| }); | ||
|
|
||
| renderHook(() => useChannelCurrenciesWithCache()); | ||
|
|
||
| expect(useChannelCurrenciesQuery).toHaveBeenCalledWith({ | ||
| skip: false, | ||
| }); | ||
| }); | ||
|
|
||
| it("should handle loading state for gift card forms", () => { | ||
| mockClient.readQuery.mockImplementation(() => { | ||
| throw new Error("Cache miss"); | ||
| }); | ||
|
|
||
| useChannelCurrenciesQuery.mockReturnValue({ | ||
| ...mockQueryResult, | ||
| loading: true, | ||
| data: undefined, | ||
| }); | ||
|
|
||
| const { result } = renderHook(() => useChannelCurrenciesWithCache()); | ||
|
|
||
| expect(result.current.loadingChannelCurrencies).toBe(true); | ||
| expect(result.current.channelCurrencies).toEqual([]); | ||
| }); | ||
|
|
||
| it("should handle error state appropriately", () => { | ||
| mockClient.readQuery.mockImplementation(() => { | ||
| throw new Error("Cache miss"); | ||
| }); | ||
|
|
||
| const errorResult = { | ||
| ...mockQueryResult, | ||
| loading: false, | ||
| error: new Error("Network error"), | ||
| data: undefined, | ||
| }; | ||
|
|
||
| useChannelCurrenciesQuery.mockReturnValue(errorResult); | ||
|
|
||
| const { result } = renderHook(() => useChannelCurrenciesWithCache()); | ||
|
|
||
| expect(result.current.loadingChannelCurrencies).toBe(false); | ||
| expect(result.current.channelCurrencies).toEqual([]); | ||
| }); | ||
|
|
||
| it("should return query data when cache miss occurs", () => { | ||
| mockClient.readQuery.mockImplementation(() => { | ||
| throw new Error("Cache miss"); | ||
| }); | ||
|
|
||
| const { result } = renderHook(() => useChannelCurrenciesWithCache()); | ||
|
|
||
| expect(result.current.loadingChannelCurrencies).toBe(false); | ||
| expect(result.current.channelCurrencies).toEqual(["USD", "EUR"]); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { useApolloClient } from "@apollo/client"; | ||
| import { | ||
| ChannelCurrenciesDocument, | ||
| ChannelCurrenciesQuery, | ||
| ChannelCurrenciesQueryVariables, | ||
| useChannelCurrenciesQuery, | ||
| } from "@dashboard/graphql"; | ||
| import { useMemo } from "react"; | ||
|
|
||
| /** | ||
| * Custom hook that uses useChannelCurrenciesQuery only if the data isn't already in cache. | ||
| * This helps avoid unnecessary network requests when the channel currencies data is already available. | ||
| */ | ||
| export function useChannelCurrenciesWithCache() { | ||
| const client = useApolloClient(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why this is not cached with apollo? I mean we can't create a wrapper for every object and appollo supports that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH I don't know but if I didn't used custom hook |
||
|
|
||
| const cachedData = useMemo(() => { | ||
| try { | ||
| return client.readQuery<ChannelCurrenciesQuery, ChannelCurrenciesQueryVariables>({ | ||
| query: ChannelCurrenciesDocument, | ||
| variables: {}, | ||
| }); | ||
| } catch { | ||
| // Query not in cache or cache miss | ||
| return null; | ||
| } | ||
| }, [client]); | ||
|
|
||
| const shouldSkip = cachedData !== null; | ||
|
|
||
| const queryResult = useChannelCurrenciesQuery({ | ||
| skip: shouldSkip, | ||
| }); | ||
|
|
||
| if (shouldSkip && cachedData) { | ||
| return { | ||
| loadingChannelCurrencies: false, | ||
| channelCurrencies: cachedData.shop.channelCurrencies, | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| loadingChannelCurrencies: queryResult.loading, | ||
| channelCurrencies: queryResult.data?.shop.channelCurrencies ?? [], | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldnt it incldue prop
loading={loadingChannelCurrencies}?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope - it turns out loading was only for
GiftCardCreateMoneyInput- now this component hasloadinginside.