Skip to content

Commit

Permalink
feat(onyx-865): Reorganize CTAs on My Collection Artwork screen (arts…
Browse files Browse the repository at this point in the history
…y#10094)

* feat(onyx-865): Reorganize CTAs on My Collection Artwork screen

* fixes tests

* fix test
  • Loading branch information
nickskalkin authored Apr 18, 2024
1 parent 19807d8 commit 34a243d
Show file tree
Hide file tree
Showing 11 changed files with 279 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const MyCollectionArtworkArticles: React.FC<MyCollectionArtworkArticlesPr
}

return (
<Flex mb={4}>
<Flex mb={4} mt={2}>
<TouchableOpacity
onPress={() => {
trackEvent(tracks.tappedArticleGroup())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { screen } from "@testing-library/react-native"
import { PriceEstimateRequestedTestsQuery } from "__generated__/PriceEstimateRequestedTestsQuery.graphql"
import { PriceEstimateRequested } from "app/Scenes/MyCollection/Screens/Artwork/Components/ArtworkInsights/RequestForPriceEstimate/PriceEstimateRequested"
import { __globalStoreTestUtils__, GlobalStoreProvider } from "app/store/GlobalStore"
import { flushPromiseQueue } from "app/utils/tests/flushPromiseQueue"
import { renderWithWrappers } from "app/utils/tests/renderWithWrappers"
import { graphql, QueryRenderer } from "react-relay"
import { createMockEnvironment, MockPayloadGenerator } from "relay-test-utils"

describe("PriceEstimateRequested", () => {
let mockEnvironment: ReturnType<typeof createMockEnvironment>
const TestRenderer = () => (
<QueryRenderer<PriceEstimateRequestedTestsQuery>
environment={mockEnvironment}
query={graphql`
query PriceEstimateRequestedTestsQuery @relay_test_operation {
artwork(id: "foo") {
...RequestForPriceEstimateBanner_artwork
}
me {
...RequestForPriceEstimateBanner_me
}
}
`}
variables={{}}
render={({ props }) => {
if (props?.artwork && props?.me) {
return (
<GlobalStoreProvider>
<PriceEstimateRequested me={props.me} artwork={props.artwork} />
</GlobalStoreProvider>
)
}
return null
}}
/>
)

beforeEach(() => {
mockEnvironment = createMockEnvironment()
})

afterEach(() => {
jest.clearAllMocks()
__globalStoreTestUtils__?.reset()
})

const resolveData = (passedProps = {}) => {
mockEnvironment.mock.resolveMostRecentOperation((operation) =>
MockPayloadGenerator.generate(operation, passedProps)
)
}

it("renders 'requested' state if in global store without throwing an error", async () => {
renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "artwork-id",
slug: "artwork-id",
hasPriceEstimateRequest: null,
}),
})
__globalStoreTestUtils__?.injectState({
requestedPriceEstimates: {
requestedPriceEstimates: {
"artwork-id": {
artworkId: "artwork-id",
requestedAt: 1666015648950,
},
},
},
})

await flushPromiseQueue()

expect(screen.getByText("Price Estimate Request Sent")).toBeDefined()
})

it("renders 'requested' state if hasPriceEstimateRequest is true", () => {
renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "artwork-id",
slug: "artwork-id",
hasPriceEstimateRequest: true,
}),
})
expect(screen.getByText("Price Estimate Request Sent")).toBeDefined()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Box, CheckCircleIcon, Flex, Separator, Text } from "@artsy/palette-mobile"
import { RequestForPriceEstimateBanner_artwork$key } from "__generated__/RequestForPriceEstimateBanner_artwork.graphql"
import { RequestForPriceEstimateBanner_me$key } from "__generated__/RequestForPriceEstimateBanner_me.graphql"
import { artworkFragment } from "app/Scenes/MyCollection/Screens/Artwork/Components/ArtworkInsights/RequestForPriceEstimate/RequestForPriceEstimateBanner"
import { usePriceEstimateRequested } from "app/Scenes/MyCollection/Screens/Artwork/Components/ArtworkInsights/RequestForPriceEstimate/usePriceEstimateRequested"
import { useFragment } from "react-relay"

interface PriceEstimateRequestedProps {
artwork: RequestForPriceEstimateBanner_artwork$key
me: RequestForPriceEstimateBanner_me$key | null | undefined
}

export const PriceEstimateRequested: React.FC<PriceEstimateRequestedProps> = ({
...otherProps
}) => {
const artwork = useFragment(artworkFragment, otherProps.artwork)
const priceEstimateRequested = usePriceEstimateRequested(artwork)

if (!priceEstimateRequested) return null

return (
<Box>
<Flex alignItems="center" flexDirection="row">
<CheckCircleIcon />
<Text variant="sm" ml={0.5} textAlign="center">
Price Estimate Request Sent
</Text>
</Flex>

<Separator mt={2} mb={2} borderColor="black10" />
</Box>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { fireEvent } from "@testing-library/react-native"
import { fireEvent, screen } from "@testing-library/react-native"
import { RequestForPriceEstimateBannerTestsQuery } from "__generated__/RequestForPriceEstimateBannerTestsQuery.graphql"
import { __globalStoreTestUtils__, GlobalStoreProvider } from "app/store/GlobalStore"
import { __globalStoreTestUtils__ } from "app/store/GlobalStore"
import { flushPromiseQueue } from "app/utils/tests/flushPromiseQueue"
import { mockTrackEvent } from "app/utils/tests/globallyMockedStuff"
import { renderWithWrappers } from "app/utils/tests/renderWithWrappers"
Expand Down Expand Up @@ -30,13 +30,12 @@ describe("RequestForPriceEstimateBanner", () => {
render={({ props }) => {
if (props?.artwork && props?.marketPriceInsights && props?.me) {
return (
<GlobalStoreProvider>
<RequestForPriceEstimateBanner
me={props.me}
artwork={props.artwork}
marketPriceInsights={props.marketPriceInsights}
/>
</GlobalStoreProvider>
<RequestForPriceEstimateBanner
me={props.me}
artwork={props.artwork}
marketPriceInsights={props.marketPriceInsights}
contextModule="insights"
/>
)
}
return null
Expand All @@ -60,7 +59,7 @@ describe("RequestForPriceEstimateBanner", () => {
}

it("renders without throwing an error", async () => {
const { getByTestId } = renderWithWrappers(<TestRenderer />)
renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "some-internal-id",
Expand All @@ -74,13 +73,13 @@ describe("RequestForPriceEstimateBanner", () => {

await flushPromiseQueue()

expect(getByTestId("request-price-estimate-button")).toBeDefined()
expect(getByTestId("request-price-estimate-banner-title")).toBeDefined()
expect(getByTestId("request-price-estimate-banner-description")).toBeDefined()
expect(screen.getByTestId("request-price-estimate-button")).toBeDefined()
expect(screen.getByTestId("request-price-estimate-banner-title")).toBeDefined()
expect(screen.getByTestId("request-price-estimate-banner-description")).toBeDefined()
})

it("rendering nothing if the price estimate is not requestable", () => {
const { queryByTestId } = renderWithWrappers(<TestRenderer />)
renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "some-internal-id",
Expand All @@ -89,56 +88,13 @@ describe("RequestForPriceEstimateBanner", () => {
}),
})

expect(queryByTestId("request-price-estimate-button")).toBeNull()
expect(queryByTestId("request-price-estimate-banner-title")).toBeNull()
expect(queryByTestId("request-price-estimate-banner-description")).toBeNull()
})

it("renders 'requested' state if in global store without throwing an error", async () => {
const { getByText } = renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "artwork-id",
slug: "artwork-id",
hasPriceEstimateRequest: null,
}),
MarketPriceInsights: () => ({
demandRank: 7.5,
}),
})
__globalStoreTestUtils__?.injectState({
requestedPriceEstimates: {
requestedPriceEstimates: {
"artwork-id": {
artworkId: "artwork-id",
requestedAt: 1666015648950,
},
},
},
})

await flushPromiseQueue()

expect(getByText("Price Estimate Request Sent")).toBeDefined()
})

it("renders 'requested' state if hasPriceEstimateRequest is true", () => {
const { getByText } = renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "artwork-id",
slug: "artwork-id",
hasPriceEstimateRequest: true,
}),
MarketPriceInsights: () => ({
demandRank: 7.5,
}),
})
expect(getByText("Price Estimate Request Sent")).toBeDefined()
expect(screen.queryByTestId("request-price-estimate-button")).toBeNull()
expect(screen.queryByTestId("request-price-estimate-banner-title")).toBeNull()
expect(screen.queryByTestId("request-price-estimate-banner-description")).toBeNull()
})

it("tracks analytics event when RequestForEstimate button is tapped", () => {
const { getByTestId } = renderWithWrappers(<TestRenderer />)
renderWithWrappers(<TestRenderer />)
resolveData({
Artwork: () => ({
internalID: "artwork-id",
Expand All @@ -151,7 +107,7 @@ describe("RequestForPriceEstimateBanner", () => {
}),
})

const TheButton = getByTestId("request-price-estimate-button")
const TheButton = screen.getByTestId("request-price-estimate-button")

fireEvent.press(TheButton)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,43 @@
import { ActionType, ContextModule, OwnerType, TappedRequestPriceEstimate } from "@artsy/cohesion"
import { CheckCircleIcon, Flex, Box, Text, Separator, Button } from "@artsy/palette-mobile"
import { Box, Text, Separator, Button, Spacer } from "@artsy/palette-mobile"
import { RequestForPriceEstimateBanner_artwork$key } from "__generated__/RequestForPriceEstimateBanner_artwork.graphql"
import { RequestForPriceEstimateBanner_marketPriceInsights$key } from "__generated__/RequestForPriceEstimateBanner_marketPriceInsights.graphql"
import { RequestForPriceEstimateBanner_me$key } from "__generated__/RequestForPriceEstimateBanner_me.graphql"
import { Toast } from "app/Components/Toast/Toast"
import { GlobalStore } from "app/store/GlobalStore"
import { usePriceEstimateRequested } from "app/Scenes/MyCollection/Screens/Artwork/Components/ArtworkInsights/RequestForPriceEstimate/usePriceEstimateRequested"
import { navigate } from "app/system/navigation/navigate"
import { graphql, useFragment } from "react-relay"
import { useTracking } from "react-tracking"

interface RequestForPriceEstimateProps {
artwork: RequestForPriceEstimateBanner_artwork$key
marketPriceInsights: RequestForPriceEstimateBanner_marketPriceInsights$key | null | undefined
me: RequestForPriceEstimateBanner_me$key | null | undefined
contextModule: "insights" | "about" | "oldAbout"
}

export const RequestForPriceEstimateBanner: React.FC<RequestForPriceEstimateProps> = ({
contextModule,
...otherProps
}) => {
const { trackEvent } = useTracking()

const artwork = useFragment(artworkFragment, otherProps.artwork)
const priceEstimateRequested = usePriceEstimateRequested(artwork)

const marketPriceInsights = useFragment(
marketPriceInsightsFragment,
otherProps.marketPriceInsights
)

const me = useFragment(meFragment, otherProps.me)

const localRequestedPriceEstimates = GlobalStore.useAppState(
(state) => state.requestedPriceEstimates.requestedPriceEstimates
)

const priceEstimateRequested =
artwork.hasPriceEstimateRequest || !!localRequestedPriceEstimates[artwork.internalID]

if (priceEstimateRequested) {
return (
<Box>
<Flex alignItems="center" flexDirection="row">
<CheckCircleIcon />
<Text variant="sm" ml={0.5} textAlign="center">
Price Estimate Request Sent
</Text>
</Flex>

<Separator mt={2} mb={4} borderColor="black10" />
</Box>
)
}

if (!artwork.isPriceEstimateRequestable) {
return null
}
if (priceEstimateRequested) return null
if (!artwork.isPriceEstimateRequestable) return null

return (
<Box>
<Separator mt={2} mb={2} borderColor="black10" />
<Text variant="sm" testID="request-price-estimate-banner-title">
Get a Free Price Estimate
</Text>
Expand Down Expand Up @@ -91,17 +75,17 @@ export const RequestForPriceEstimateBanner: React.FC<RequestForPriceEstimateProp
})
}}
block
variant="fillDark"
variant="outline"
>
Request a Price Estimate
</Button>

<Separator mt={2} mb={4} borderColor="black10" />
{contextModule === "about" && <Spacer y={2} />}
</Box>
)
}

const artworkFragment = graphql`
export const artworkFragment = graphql`
fragment RequestForPriceEstimateBanner_artwork on Artwork {
internalID
slug
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { RequestForPriceEstimateBanner_artwork$data } from "__generated__/RequestForPriceEstimateBanner_artwork.graphql"
import { GlobalStore } from "app/store/GlobalStore"

export const usePriceEstimateRequested = (artwork: RequestForPriceEstimateBanner_artwork$data) => {
const localRequestedPriceEstimates = GlobalStore.useAppState(
(state) => state.requestedPriceEstimates.requestedPriceEstimates
)

const priceEstimateRequested =
artwork.hasPriceEstimateRequest || !!localRequestedPriceEstimates[artwork.internalID]

return priceEstimateRequested
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const MyCollectionWhySell: React.FC<MyCollectionWhySellProps> = (props) =

return (
<Flex>
<Separator mb={2} />
{contextModule === "about" && <Separator mb={2} borderColor="black10" />}
<Text variant="sm-display" testID="SWA-banner-in-MC">
Interested in Selling This Work?
</Text>
Expand Down Expand Up @@ -80,6 +80,7 @@ export const MyCollectionWhySell: React.FC<MyCollectionWhySellProps> = (props) =
</Text>
</Text>
</>
{contextModule === "insights" && <Separator mt={2} mb={2} borderColor="black10" />}
</Flex>
)
}
Expand Down
Loading

0 comments on commit 34a243d

Please sign in to comment.