From 6f85a3e977d31fc3741d524ace810275817cafa3 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 11:13:20 +0200 Subject: [PATCH 01/30] feat: create new artwork filter store --- .../NewArtworkFilterStore.tests.tsx | 53 +++++++++++ .../NewArtworkFilterStore.tsx | 89 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx new file mode 100644 index 00000000000..af792c905a8 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx @@ -0,0 +1,53 @@ +import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" +import { + NewArtworkFilterStoreModel, + NewFilterData, + getNewArtworkFilterStoreModel, +} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { createStore } from "easy-peasy" + +const createFilterArtworksStore = (state?: Partial) => + createStore({ ...getNewArtworkFilterStoreModel(), ...state }) + +describe("NewArtworkFilterStore", () => { + describe("initialState", () => { + it("is correct when no injections are made", () => { + const store = createFilterArtworksStore() + expect(store.getState().appliedFilters).toEqual([]) + expect(store.getState().allFilters).toEqual([]) + expect(store.getState().aggregations).toEqual([]) + expect(store.getState().notAppliedFilters).toEqual([]) + }) + it("is correct when injections are made", () => { + const store = createFilterArtworksStore({ + appliedFilters, + aggregations, + }) + expect(store.getState().appliedFilters).toEqual(appliedFilters) + expect(store.getState().allFilters).toEqual(appliedFilters) + expect(store.getState().aggregations).toEqual(aggregations) + expect(store.getState().notAppliedFilters).toEqual([]) + }) + }) +}) + +const appliedFilters: NewFilterData[] = [ + { + displayText: "Medium", + paramName: "medium", + paramValue: "painting", + }, +] + +const aggregations: Aggregations = [ + { + slice: "COLOR", + counts: [ + { + count: 10, + value: "blue", + name: "Blue", + }, + ], + }, +] diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx new file mode 100644 index 00000000000..88d13154d19 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx @@ -0,0 +1,89 @@ +import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" +import { assignDeep } from "app/store/persistence" +import { Action, Computed, action, computed, createContextStore } from "easy-peasy" + +export interface NewFilterData { + displayText: string + // TODO: change to an enum + paramName: string + // TODO: change accordingly when we have more than one type + // NOTE: make sure this type is compatible with the type of the filter + paramValue: string +} + +export interface NewArtworkFilterStoreModel { + /********************************************************/ + /** State **/ + /********************************************************/ + + // A list of filters that are already applied to the current filter state + appliedFilters: NewFilterData[] + // A list of filters that have been selected but not yet applied + notAppliedFilters: NewFilterData[] + // A combination of applied and not applied filters + allFilters: Computed + + // An aray representing the available aggregations with the initial set of applied filters + aggregations: Aggregations + + /********************************************************/ + /** Actions **/ + /********************************************************/ + + // Add non applied filters to the list of applied filters + + applyFiltersAction: Action + // Remove all applied and non applied filters + clearAllFiltersAction: Action + // Add a new filter to the list of not applied filters + selectFilterAction: Action + // Set filter aggregations + setAggregationsAction: Action +} + +export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ({ + /********************************************************/ + /** State **/ + /********************************************************/ + + appliedFilters: [], + notAppliedFilters: [], + allFilters: computed((state) => [...state.appliedFilters, ...state.notAppliedFilters]), + aggregations: [], + + /********************************************************/ + /** Actions **/ + /********************************************************/ + + applyFiltersAction: action((state) => { + state.appliedFilters = [...state.appliedFilters, ...state.notAppliedFilters] + state.notAppliedFilters = [] + }), + clearAllFiltersAction: action((state) => { + state.appliedFilters = [] + state.notAppliedFilters = [] + }), + selectFilterAction: action((state, filter) => { + state.notAppliedFilters = [...state.notAppliedFilters, filter] + }), + setAggregationsAction: action((state, aggregations) => { + state.aggregations = aggregations + }), +}) + +export function createArtworkFiltersStore() { + if (__TEST__) { + ;(getNewArtworkFilterStoreModel() as any).__injectState = action((state, injectedState) => { + assignDeep(state, injectedState) + }) + } + const store = createContextStore((initialData) => ({ + ...getNewArtworkFilterStoreModel(), + ...initialData, + })) + return store +} + +export const NewArtworksFiltersStore = createArtworkFiltersStore() + +export const ArtworkFiltersStoreProvider = NewArtworksFiltersStore.Provider From 4eb5975f41cd1b5ae0088728bc8d2421812ee298 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 13:24:29 +0200 Subject: [PATCH 02/30] minor: separate filter data type --- .../NewArtworkFilter/NewArtworkFilterStore.tsx | 10 +--------- src/app/Components/NewArtworkFilter/helpers.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 src/app/Components/NewArtworkFilter/helpers.ts diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx index 88d13154d19..c8ff4af5635 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx @@ -1,16 +1,8 @@ import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" +import { NewFilterData } from "app/Components/NewArtworkFilter/helpers" import { assignDeep } from "app/store/persistence" import { Action, Computed, action, computed, createContextStore } from "easy-peasy" -export interface NewFilterData { - displayText: string - // TODO: change to an enum - paramName: string - // TODO: change accordingly when we have more than one type - // NOTE: make sure this type is compatible with the type of the filter - paramValue: string -} - export interface NewArtworkFilterStoreModel { /********************************************************/ /** State **/ diff --git a/src/app/Components/NewArtworkFilter/helpers.ts b/src/app/Components/NewArtworkFilter/helpers.ts new file mode 100644 index 00000000000..762ec280520 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/helpers.ts @@ -0,0 +1,8 @@ +export enum NewFilterParamName { + attributionClass = "Rarity", +} + +export type NewFilterData = { + paramName: "attributionClass" + paramValue: string[] +} From f1cfd9d52b183a1ba88f7465f9cb18fe79f1e35a Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 14:09:44 +0200 Subject: [PATCH 03/30] feat: add filters screen wrapper --- .../CreateSavedSearchAlert.tsx | 1 + .../screens/AddFiltersScreen.tests.tsx | 22 ++++++++ .../screens/AddFiltersScreen.tsx | 55 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx diff --git a/src/app/Scenes/SavedSearchAlert/CreateSavedSearchAlert.tsx b/src/app/Scenes/SavedSearchAlert/CreateSavedSearchAlert.tsx index 2236e8964a7..e53aaa10186 100644 --- a/src/app/Scenes/SavedSearchAlert/CreateSavedSearchAlert.tsx +++ b/src/app/Scenes/SavedSearchAlert/CreateSavedSearchAlert.tsx @@ -8,6 +8,7 @@ import { SavedSearchStoreProvider, } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { CreateSavedSearchAlertContentQueryRenderer } from "app/Scenes/SavedSearchAlert/containers/CreateSavedSearchContentContainer" +import { AddFiltersScreen } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreen" import { AlertPriceRangeScreenQueryRenderer } from "app/Scenes/SavedSearchAlert/screens/AlertPriceRangeScreen" import { ConfirmationScreen } from "app/Scenes/SavedSearchAlert/screens/ConfirmationScreen" import { SavedSearchFilterScreen } from "app/Scenes/SavedSearchAlert/screens/SavedSearchFilterScreen" diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx new file mode 100644 index 00000000000..7c191285e81 --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx @@ -0,0 +1,22 @@ +import { fireEvent } from "@testing-library/react-native" +import { ClearAllButton } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreen" +import { flushPromiseQueue } from "app/utils/tests/flushPromiseQueue" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" +import { Alert } from "react-native" + +jest.spyOn(Alert, "alert") + +describe("ClearAllButton", () => { + it("Shows an alert when pressed", async () => { + const { getByText } = renderWithWrappers() + + fireEvent(getByText("Clear All"), "onPress") + + await flushPromiseQueue() + + expect(Alert.alert).toHaveBeenCalled() + }) + + // TODO: Implement feature and test + it("Is disabled when there are no filters", () => {}) +}) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx new file mode 100644 index 00000000000..796646ba919 --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -0,0 +1,55 @@ +import { Flex, Text } from "@artsy/palette-mobile" +import { useNavigation } from "@react-navigation/native" +import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" +import { Alert, TouchableOpacity } from "react-native" + +export const AddFiltersScreen: React.FC<{}> = () => { + const navigation = useNavigation() + return ( + + {}} + > + Filters + + + ) +} + +export const ClearAllButton = () => { + const disabled = false + + return ( + { + Alert.alert("Are you sure you want to clear all filters?", undefined, [ + { + text: "Cancel", + onPress() { + // Do nothing + }, + }, + + { + text: "Clear All", + onPress() { + // Trigger action to clear all filters + }, + style: "destructive", + }, + ]) + }} + > + + Clear All + + + ) +} From cbfd7ce9eb3aac1260cacaf8523e59d4320aa6ec Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 15:29:59 +0200 Subject: [PATCH 04/30] feat: prepare your filters section --- .../NewArtworkFilter/NewArtworkFilter.tsx | 10 ++++ .../NewArtworkFilterAppliedFilters.tsx | 60 +++++++++++++++++++ .../NewArtworkFilterStore.tests.tsx | 10 ++-- .../Components/NewArtworkFilter/helpers.ts | 26 ++++++-- .../screens/AddFiltersScreen.tsx | 2 + 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx new file mode 100644 index 00000000000..dd0d54daa34 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx @@ -0,0 +1,10 @@ +import { Join, Spacer } from "@artsy/palette-mobile" +import { AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" + +export const NewArtworkFilter: React.FC<{}> = () => { + return ( + }> + + + ) +} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx new file mode 100644 index 00000000000..c8d82b01d49 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -0,0 +1,60 @@ +import { Flex, Pill, Text } from "@artsy/palette-mobile" +import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" +import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" + +export const AppliedFilters: React.FC<{}> = () => { + const entity = SavedSearchStore.useStoreState((state) => state.entity) + + const artistsPills = entity.artists.map((artist) => { + return { + paramName: NewFilterParamName.artistIDs, + paramValue: { + value: artist.id, + displayLabel: artist.name, + }, + } + }) + + return ( + + + Your Filters + + + + {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} + {[...artistsPills, ...pillsData].map((pill, index) => ( + onRemovePill(pill)} + > + {pill.paramValue.displayLabel} + + ))} + + + ) +} + +const pillsData: NewFilterData[] = [ + { + paramName: NewFilterParamName.categories, + paramValue: { + value: "painting", + displayLabel: "Painting", + }, + }, + { + paramName: NewFilterParamName.attributionClass, + paramValue: { + value: "unique", + displayLabel: "Unique", + }, + }, +] diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx index af792c905a8..9aab3469ffa 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx @@ -1,9 +1,9 @@ import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" import { NewArtworkFilterStoreModel, - NewFilterData, getNewArtworkFilterStoreModel, } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { createStore } from "easy-peasy" const createFilterArtworksStore = (state?: Partial) => @@ -33,9 +33,11 @@ describe("NewArtworkFilterStore", () => { const appliedFilters: NewFilterData[] = [ { - displayText: "Medium", - paramName: "medium", - paramValue: "painting", + paramName: NewFilterParamName.categories, + paramValue: { + value: "painting", + displayLabel: "Painting", + }, }, ] diff --git a/src/app/Components/NewArtworkFilter/helpers.ts b/src/app/Components/NewArtworkFilter/helpers.ts index 762ec280520..8d06ff1bee0 100644 --- a/src/app/Components/NewArtworkFilter/helpers.ts +++ b/src/app/Components/NewArtworkFilter/helpers.ts @@ -1,8 +1,26 @@ export enum NewFilterParamName { - attributionClass = "Rarity", + artistIDs = "artistIDs", + attributionClass = "rarity", + categories = "categories", } -export type NewFilterData = { - paramName: "attributionClass" - paramValue: string[] +export type NewFilterData = + | { + paramName: NewFilterParamName.attributionClass + paramValue: { + value: string + displayLabel: string + } + } + | { + paramName: NewFilterParamName.categories + paramValue: { + value: string + displayLabel: string + } + } + +export const paramNameToDisplayLabelMap = { + attributionClass: "Rarity", + categories: "Categories", } diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 796646ba919..089bf278e98 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,6 +1,7 @@ import { Flex, Text } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" +import { NewArtworkFilter } from "app/Components/NewArtworkFilter/NewArtworkFilter" import { Alert, TouchableOpacity } from "react-native" export const AddFiltersScreen: React.FC<{}> = () => { @@ -18,6 +19,7 @@ export const AddFiltersScreen: React.FC<{}> = () => { > Filters + ) } From d5319b99ed5747322d63a81064483a1183f9978d Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 16:33:34 +0200 Subject: [PATCH 05/30] refactor: move screen to savesearches directory --- .../Components/NewArtworkFilter/NewArtworkFilter.tsx | 10 ---------- .../NewArtworkFilterAppliedFilters.tsx | 2 +- .../SavedSearchAlert/screens/AddFiltersScreen.tsx | 9 ++++++--- 3 files changed, 7 insertions(+), 14 deletions(-) delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx deleted file mode 100644 index dd0d54daa34..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilter.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Join, Spacer } from "@artsy/palette-mobile" -import { AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" - -export const NewArtworkFilter: React.FC<{}> = () => { - return ( - }> - - - ) -} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index c8d82b01d49..bd74f517dfa 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -2,7 +2,7 @@ import { Flex, Pill, Text } from "@artsy/palette-mobile" import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -export const AppliedFilters: React.FC<{}> = () => { +export const NewArtworkFilterAppliedFilters: React.FC<{}> = () => { const entity = SavedSearchStore.useStoreState((state) => state.entity) const artistsPills = entity.artists.map((artist) => { diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 089bf278e98..b6c9dc23f72 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,11 +1,12 @@ -import { Flex, Text } from "@artsy/palette-mobile" +import { Flex, Join, Spacer, Text } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" -import { NewArtworkFilter } from "app/Components/NewArtworkFilter/NewArtworkFilter" +import { NewArtworkFilterAppliedFilters as AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" import { Alert, TouchableOpacity } from "react-native" export const AddFiltersScreen: React.FC<{}> = () => { const navigation = useNavigation() + return ( = () => { > Filters - + }> + + ) } From 432c702ec8fc767deab0c329284d7a94b1aba701 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 16:49:54 +0200 Subject: [PATCH 06/30] feat: wrap the screen with the new artwork filters store --- .../NewArtworkFilterAppliedFilters.tsx | 32 ++++++++++++------- .../NewArtworkFilterStore.tsx | 2 +- .../Components/NewArtworkFilter/helpers.ts | 7 ++++ .../CreateSavedSearchAlert.tsx | 1 - .../screens/AddFiltersScreen.tsx | 22 ++++++++++++- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index bd74f517dfa..c2e1c15b4c4 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -2,18 +2,26 @@ import { Flex, Pill, Text } from "@artsy/palette-mobile" import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -export const NewArtworkFilterAppliedFilters: React.FC<{}> = () => { - const entity = SavedSearchStore.useStoreState((state) => state.entity) +export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: boolean }> = ({ + includeArtistNames, +}) => { + const entity = SavedSearchStore?.useStoreState((state) => state.entity) - const artistsPills = entity.artists.map((artist) => { - return { - paramName: NewFilterParamName.artistIDs, - paramValue: { - value: artist.id, - displayLabel: artist.name, - }, - } - }) + const activePills: NewFilterData[] = [] + + if (entity && includeArtistNames) { + entity.artists.forEach((artist) => { + activePills.push({ + paramName: NewFilterParamName.artistIDs, + paramValue: { + value: artist.id, + displayLabel: artist.name, + }, + }) + }) + } + + activePills.push(...pillsData) return ( @@ -23,7 +31,7 @@ export const NewArtworkFilterAppliedFilters: React.FC<{}> = () => { {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} - {[...artistsPills, ...pillsData].map((pill, index) => ( + {activePills.map((pill, index) => ( = () => { @@ -21,12 +26,27 @@ export const AddFiltersScreen: React.FC<{}> = () => { Filters }> - + ) } +export const AddFiltersScreenWrapper: React.FC<{}> = () => { + const aggregations = SavedSearchStore.useStoreState((state) => state.aggregations) + + return ( + + + + ) +} + export const ClearAllButton = () => { const disabled = false From c6df76bd575f65a2bc22dca49ded527083a8611a Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 5 Oct 2023 18:09:14 +0200 Subject: [PATCH 07/30] feat: add rarity filter functionality --- .../NewArtworkFilterRarity.tsx | 87 +++++++++++++++++++ .../NewArtworkFilterStore.tests.tsx | 14 ++- .../NewArtworkFilterStore.tsx | 50 ++++++++--- .../Components/NewArtworkFilter/helpers.ts | 20 +++++ .../screens/AddFiltersScreen.tsx | 6 +- 5 files changed, 153 insertions(+), 24 deletions(-) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx new file mode 100644 index 00000000000..386fc4d759d --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx @@ -0,0 +1,87 @@ +import { Flex, Pill, Spacer, Text } from "@artsy/palette-mobile" +import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { + NewFilterData, + NewFilterParamName, + isFilterSelected, + useSelectedFiltersByParamName, +} from "app/Components/NewArtworkFilter/helpers" +import { debounce } from "lodash" + +export const NewArtworkFilterRarity = () => { + const selectedFilters = useSelectedFiltersByParamName(NewFilterParamName.attributionClass) + + const selectFilterAction = NewArtworksFiltersStore.useStoreActions( + (actions) => actions.selectFilterAction + ) + const removeFilterAction = NewArtworksFiltersStore.useStoreActions( + (actions) => actions.removeFilterAction + ) + + const handlePress = (value: Omit["paramValue"]) => { + const selectedFilterData = { + paramName: NewFilterParamName.attributionClass, + paramValue: value, + } + + if (isFilterSelected(selectedFilters, value)) { + removeFilterAction(selectedFilterData) + } else { + selectFilterAction(selectedFilterData) + } + } + + return ( + + + Rarity + + + + {ATTRIBUTION_CLASS_OPTIONS.map((option) => { + return ( + { + handlePress(option.paramValue) + }, 200)} + > + {option.paramValue.displayLabel} + + ) + })} + + + ) +} + +export const ATTRIBUTION_CLASS_OPTIONS: Omit[] = [ + { + paramValue: { + value: "unique", + displayLabel: "Unique", + }, + }, + { + paramValue: { + value: "limited edition", + displayLabel: "Limited Edition", + }, + }, + { + paramValue: { + value: "open edition", + displayLabel: "Open Edition", + }, + }, + { + paramValue: { + value: "unknown edition", + displayLabel: "Unknown Edition", + }, + }, +] diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx index 9aab3469ffa..608db67c312 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx @@ -13,25 +13,23 @@ describe("NewArtworkFilterStore", () => { describe("initialState", () => { it("is correct when no injections are made", () => { const store = createFilterArtworksStore() - expect(store.getState().appliedFilters).toEqual([]) - expect(store.getState().allFilters).toEqual([]) + expect(store.getState().previouslyAppliedFilters).toEqual([]) expect(store.getState().aggregations).toEqual([]) - expect(store.getState().notAppliedFilters).toEqual([]) + expect(store.getState().selectedFilters).toEqual([]) }) it("is correct when injections are made", () => { const store = createFilterArtworksStore({ - appliedFilters, + previouslyAppliedFilters, aggregations, }) - expect(store.getState().appliedFilters).toEqual(appliedFilters) - expect(store.getState().allFilters).toEqual(appliedFilters) + expect(store.getState().previouslyAppliedFilters).toEqual(previouslyAppliedFilters) expect(store.getState().aggregations).toEqual(aggregations) - expect(store.getState().notAppliedFilters).toEqual([]) + expect(store.getState().selectedFilters).toEqual([]) }) }) }) -const appliedFilters: NewFilterData[] = [ +const previouslyAppliedFilters: NewFilterData[] = [ { paramName: NewFilterParamName.categories, paramValue: { diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx index ecd65c02a63..0037d1db41b 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx @@ -1,7 +1,8 @@ import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" import { NewFilterData } from "app/Components/NewArtworkFilter/helpers" import { assignDeep } from "app/store/persistence" -import { Action, Computed, action, computed, createContextStore } from "easy-peasy" +import { Action, action, createContextStore } from "easy-peasy" +import { isEqual } from "lodash" export interface NewArtworkFilterStoreModel { /********************************************************/ @@ -9,11 +10,9 @@ export interface NewArtworkFilterStoreModel { /********************************************************/ // A list of filters that are already applied to the current filter state - appliedFilters: NewFilterData[] - // A list of filters that have been selected but not yet applied - notAppliedFilters: NewFilterData[] - // A combination of applied and not applied filters - allFilters: Computed + previouslyAppliedFilters: NewFilterData[] + // A list of filters that should be selected + selectedFilters: NewFilterData[] // An aray representing the available aggregations with the initial set of applied filters aggregations: Aggregations @@ -31,6 +30,8 @@ export interface NewArtworkFilterStoreModel { selectFilterAction: Action // Set filter aggregations setAggregationsAction: Action + // Remoove a filter from the list of not applied filters + removeFilterAction: Action } export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ({ @@ -38,9 +39,8 @@ export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ( /** State **/ /********************************************************/ - appliedFilters: [], - notAppliedFilters: [], - allFilters: computed((state) => [...state.appliedFilters, ...state.notAppliedFilters]), + previouslyAppliedFilters: [], + selectedFilters: [], aggregations: [], /********************************************************/ @@ -48,19 +48,41 @@ export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ( /********************************************************/ applyFiltersAction: action((state) => { - state.appliedFilters = [...state.appliedFilters, ...state.notAppliedFilters] - state.notAppliedFilters = [] + state.previouslyAppliedFilters = [...state.selectedFilters] + state.selectedFilters = [] }), clearAllFiltersAction: action((state) => { - state.appliedFilters = [] - state.notAppliedFilters = [] + state.selectedFilters = [] }), selectFilterAction: action((state, filter) => { - state.notAppliedFilters = [...state.notAppliedFilters, filter] + const alreadySelected = state.selectedFilters.find((appliedFilter) => { + if (isEqual(appliedFilter, filter)) { + return true + } + return false + }) + + // No need to add a filter that is already selected or applied + if (!alreadySelected) { + state.selectedFilters = [...state.selectedFilters, filter] + } }), setAggregationsAction: action((state, aggregations) => { state.aggregations = aggregations }), + removeFilterAction: action((state, filter) => { + const valueToBeRemovedIndex = state.selectedFilters.findIndex((appliedFilter) => { + if (isEqual(appliedFilter, filter)) { + return true + } + return false + }) + + if (valueToBeRemovedIndex > -1) { + // only splice array when item is found + state.selectedFilters.splice(valueToBeRemovedIndex, 1) // 2nd parameter means remove one item only + } + }), }) export function createArtworkFiltersStore() { diff --git a/src/app/Components/NewArtworkFilter/helpers.ts b/src/app/Components/NewArtworkFilter/helpers.ts index 5bee1bbe09f..a289a042417 100644 --- a/src/app/Components/NewArtworkFilter/helpers.ts +++ b/src/app/Components/NewArtworkFilter/helpers.ts @@ -1,3 +1,6 @@ +import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { isEqual } from "lodash" + export enum NewFilterParamName { artistIDs = "artistIDs", attributionClass = "rarity", @@ -31,3 +34,20 @@ export const paramNameToDisplayLabelMap = { attributionClass: "Rarity", categories: "Categories", } + +// A hook that returns the applied filters for a given paramName +export const useSelectedFiltersByParamName = (paramName: NewFilterParamName) => { + const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) + + return selectedFilters.filter((filter) => filter.paramName === paramName) +} + +export const isFilterSelected = ( + selectedFilters: NewFilterData[], + filterValue: NewFilterData["paramValue"] +) => { + if (selectedFilters.find((selectedFilter) => isEqual(selectedFilter.paramValue, filterValue))) { + return true + } + return false +} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 3d64b7aad89..3a644dee138 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,7 +1,8 @@ -import { Flex, Join, Spacer, Text } from "@artsy/palette-mobile" +import { Flex, Join, Separator, Text } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" import { NewArtworkFilterAppliedFilters as AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" +import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" import { NewArtworkFiltersStoreProvider, getNewArtworkFilterStoreModel, @@ -25,8 +26,9 @@ export const AddFiltersScreen: React.FC<{}> = () => { > Filters - }> + }> + ) From a4dacd1fc596f8a77f293632360f0d8db3453904 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 10:50:15 +0200 Subject: [PATCH 08/30] =?UTF-8?q?chore:=20improve=20UX=20of=20selecting=20?= =?UTF-8?q?pills=20=F0=9F=91=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NewArtworkFilter/NewArtworkFilterPill.tsx | 19 +++++++ .../NewArtworkFilterRarity.tsx | 50 ++++++++----------- 2 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx new file mode 100644 index 00000000000..80676661000 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx @@ -0,0 +1,19 @@ +import { Pill, PillProps } from "@artsy/palette-mobile" +import { useState } from "react" +import useDebounce from "react-use/lib/useDebounce" + +export const FillPill: React.FC = (props) => { + const [selected, setSelected] = useState(props.selected) + + useDebounce( + () => { + props.onPress?.() + }, + 200, + [props.selected] + ) + + return ( + setSelected(!selected)} /> + ) +} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx index 386fc4d759d..43cb9f3efec 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx @@ -1,4 +1,5 @@ -import { Flex, Pill, Spacer, Text } from "@artsy/palette-mobile" +import { Flex, Spacer, Text } from "@artsy/palette-mobile" +import { FillPill } from "app/Components/NewArtworkFilter/NewArtworkFilterPill" import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { NewFilterData, @@ -6,7 +7,6 @@ import { isFilterSelected, useSelectedFiltersByParamName, } from "app/Components/NewArtworkFilter/helpers" -import { debounce } from "lodash" export const NewArtworkFilterRarity = () => { const selectedFilters = useSelectedFiltersByParamName(NewFilterParamName.attributionClass) @@ -40,18 +40,16 @@ export const NewArtworkFilterRarity = () => { {ATTRIBUTION_CLASS_OPTIONS.map((option) => { return ( - { - handlePress(option.paramValue) - }, 200)} + { + handlePress(option) + }} > - {option.paramValue.displayLabel} - + {option.displayLabel} + ) })} @@ -59,29 +57,21 @@ export const NewArtworkFilterRarity = () => { ) } -export const ATTRIBUTION_CLASS_OPTIONS: Omit[] = [ +export const ATTRIBUTION_CLASS_OPTIONS: NewFilterData["paramValue"][] = [ { - paramValue: { - value: "unique", - displayLabel: "Unique", - }, + value: "unique", + displayLabel: "Unique", }, { - paramValue: { - value: "limited edition", - displayLabel: "Limited Edition", - }, + value: "limited edition", + displayLabel: "Limited Edition", }, { - paramValue: { - value: "open edition", - displayLabel: "Open Edition", - }, + value: "open edition", + displayLabel: "Open Edition", }, { - paramValue: { - value: "unknown edition", - displayLabel: "Unknown Edition", - }, + value: "unknown edition", + displayLabel: "Unknown Edition", }, ] From 9c18b0f397cdde92e6b93312875681084db3d195 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 11:13:54 +0200 Subject: [PATCH 09/30] chore: add rarity options tests --- .../NewArtworkFilterRarity.tests.tsx | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx new file mode 100644 index 00000000000..da2d2a63b94 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx @@ -0,0 +1,68 @@ +import { fireEvent, waitFor } from "@testing-library/react-native" +import { + ATTRIBUTION_CLASS_OPTIONS, + NewArtworkFilterRarity, +} from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" +import { + NewArtworkFiltersStoreProvider, + getNewArtworkFilterStoreModel, +} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" + +describe("NewArtworkFilterRarity", () => { + it("shows all available rarity options unselected", () => { + const { getByText } = renderWithWrappers( + + + + ) + + ATTRIBUTION_CLASS_OPTIONS.forEach((option) => { + expect(getByText(option.displayLabel)).toBeDefined() + expect(getByText(option.displayLabel).props.color).toEqual("black100") + }) + }) + + it("shows the right selected state with the right colors", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Unique").props.color).not.toEqual("black100") + expect(getByText("Limited Edition").props.color).toEqual("black100") + expect(getByText("Open Edition").props.color).toEqual("black100") + expect(getByText("Unknown Edition").props.color).toEqual("black100") + }) + + it("Updates selected filters on press", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Unique").props.color).toEqual("black100") + + fireEvent(getByText("Unique"), "onPress") + + waitFor(() => { + expect(getByText("Unique").props.color).not.toEqual("black100") + }) + }) +}) From 4a157498130deb3fd921c42f989df9436f30f4e4 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 11:19:27 +0200 Subject: [PATCH 10/30] chore: minor integrity test improvement --- .../NewArtworkFilterRarity.tests.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx index da2d2a63b94..11ac1ffb877 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx @@ -10,6 +10,8 @@ import { import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" +const black100Hex = "#000000" + describe("NewArtworkFilterRarity", () => { it("shows all available rarity options unselected", () => { const { getByText } = renderWithWrappers( @@ -20,7 +22,9 @@ describe("NewArtworkFilterRarity", () => { ATTRIBUTION_CLASS_OPTIONS.forEach((option) => { expect(getByText(option.displayLabel)).toBeDefined() - expect(getByText(option.displayLabel).props.color).toEqual("black100") + expect(getByText(option.displayLabel)).toHaveStyle({ + color: black100Hex, + }) }) }) @@ -44,10 +48,10 @@ describe("NewArtworkFilterRarity", () => { ) - expect(getByText("Unique").props.color).not.toEqual("black100") - expect(getByText("Limited Edition").props.color).toEqual("black100") - expect(getByText("Open Edition").props.color).toEqual("black100") - expect(getByText("Unknown Edition").props.color).toEqual("black100") + expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) + expect(getByText("Limited Edition")).toHaveStyle({ color: black100Hex }) + expect(getByText("Open Edition")).toHaveStyle({ color: black100Hex }) + expect(getByText("Unknown Edition")).toHaveStyle({ color: black100Hex }) }) it("Updates selected filters on press", () => { @@ -57,12 +61,12 @@ describe("NewArtworkFilterRarity", () => { ) - expect(getByText("Unique").props.color).toEqual("black100") + expect(getByText("Unique")).toHaveStyle({ color: black100Hex }) fireEvent(getByText("Unique"), "onPress") waitFor(() => { - expect(getByText("Unique").props.color).not.toEqual("black100") + expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) }) }) }) From dbf0169850eac9b688c7523751c6cff1dbabedc7 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 11:33:46 +0200 Subject: [PATCH 11/30] fix: useDebounce logic in pill --- .../Components/NewArtworkFilter/NewArtworkFilterPill.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx index 80676661000..66ac4fd9616 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx @@ -7,10 +7,12 @@ export const FillPill: React.FC = (props) => { useDebounce( () => { - props.onPress?.() + if (selected !== props.selected) { + props.onPress?.() + } }, 200, - [props.selected] + [selected] ) return ( From e041a4c322bda51f5f3c6fe1e303b4b1184f15de Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 11:57:51 +0200 Subject: [PATCH 12/30] feat: display the applied filters on your filters --- .../NewArtworkFilterAppliedFilters.tsx | 59 ++++++++----------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index c2e1c15b4c4..ed595231e1e 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -1,27 +1,32 @@ import { Flex, Pill, Text } from "@artsy/palette-mobile" -import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" +import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { useMemo } from "react" export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: boolean }> = ({ includeArtistNames, }) => { - const entity = SavedSearchStore?.useStoreState((state) => state.entity) + const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) - const activePills: NewFilterData[] = [] + const entity = SavedSearchStore?.useStoreState((state) => state.entity) - if (entity && includeArtistNames) { - entity.artists.forEach((artist) => { - activePills.push({ - paramName: NewFilterParamName.artistIDs, - paramValue: { - value: artist.id, - displayLabel: artist.name, - }, - }) - }) - } + const artistPills = useMemo(() => { + return entity?.artists.map((artist) => ({ + paramName: NewFilterParamName.artistIDs, + paramValue: { + value: artist.id, + displayLabel: artist.name, + }, + })) + }, [entity]) - activePills.push(...pillsData) + const allPills = useMemo(() => { + if (includeArtistNames) return [...artistPills, ...selectedFilters] + else { + return [...selectedFilters] + } + }, [selectedFilters, entity]) return ( @@ -31,7 +36,7 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: bool {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} - {activePills.map((pill, index) => ( + {allPills.map((pill, index) => ( onRemovePill(pill)} + onPress={() => { + // Add remove + }} > {pill.paramValue.displayLabel} @@ -49,20 +55,3 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: bool ) } - -const pillsData: NewFilterData[] = [ - { - paramName: NewFilterParamName.categories, - paramValue: { - value: "painting", - displayLabel: "Painting", - }, - }, - { - paramName: NewFilterParamName.attributionClass, - paramValue: { - value: "unique", - displayLabel: "Unique", - }, - }, -] From 48278656076c3b8cb17fe4399dbf16f52b2bfe5a Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 12:06:02 +0200 Subject: [PATCH 13/30] feat: add remove filter action --- .../NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx | 4 ++++ .../Components/NewArtworkFilter/NewArtworkFilterPill.tsx | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index ed595231e1e..fdfd3e9838c 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -8,6 +8,9 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: bool includeArtistNames, }) => { const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) + const removeFilterAction = NewArtworksFiltersStore.useStoreActions( + (state) => state.removeFilterAction + ) const entity = SavedSearchStore?.useStoreState((state) => state.entity) @@ -46,6 +49,7 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: bool key={`filter-label-${index}`} onPress={() => { // Add remove + removeFilterAction(pill) }} > {pill.paramValue.displayLabel} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx index 66ac4fd9616..b88c2c571a0 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx @@ -1,5 +1,5 @@ import { Pill, PillProps } from "@artsy/palette-mobile" -import { useState } from "react" +import { useEffect, useState } from "react" import useDebounce from "react-use/lib/useDebounce" export const FillPill: React.FC = (props) => { @@ -15,6 +15,13 @@ export const FillPill: React.FC = (props) => { [selected] ) + // Make sure that the selected state is in sync with the state in the fitler store + useEffect(() => { + if (props.selected !== selected) { + setSelected(props.selected) + } + }, [props.selected]) + return ( setSelected(!selected)} /> ) From 5a83528409777268af6a752ee2762afb98a4bc15 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 12:06:50 +0200 Subject: [PATCH 14/30] chore: add note to make sure to implement clear all --- src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 3a644dee138..51b9674b9bf 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -56,7 +56,7 @@ export const ClearAllButton = () => { { - Alert.alert("Are you sure you want to clear all filters?", undefined, [ + Alert.alert("Are you sure you want to clear all filters?", "Not yet supported", [ { text: "Cancel", onPress() { From ba5bb99ca925afb4bf602ff73951274b9437d445 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 12:22:00 +0200 Subject: [PATCH 15/30] feat: add your filters tests --- .../NewArtworkFilterAppliedFilters.tests.tsx | 123 ++++++++++++++++++ .../NewArtworkFilterAppliedFilters.tsx | 8 +- 2 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx new file mode 100644 index 00000000000..7424c2eb042 --- /dev/null +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx @@ -0,0 +1,123 @@ +import { OwnerType } from "@artsy/cohesion" +import { fireEvent } from "@testing-library/react-native" +import { SavedSearchEntity } from "app/Components/ArtworkFilter/SavedSearch/types" +import { NewArtworkFilterAppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" +import { + NewArtworkFiltersStoreProvider, + getNewArtworkFilterStoreModel, +} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" +import { + SavedSearchStoreProvider, + savedSearchModel, +} from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" + +describe("NewArtworkFilterAppliedFilters", () => { + it("shows all selected filters", () => { + const { getByText } = renderWithWrappers( + + + + + + ) + + expect(getByText("Unique")).toBeDefined() + expect(getByText("Limited Edition")).toBeDefined() + expect(getByText("Banksy")).toBeDefined() + }) + + it("doesn't show the artist name when includeArtistNames is not specified", () => { + const { getByText } = renderWithWrappers( + + + + + + ) + + expect(() => getByText("Banksy")).toThrow() + }) + + it("removes filter when tapped", () => { + const { getByText } = renderWithWrappers( + + + + + + ) + + expect(getByText("Unique")).toBeDefined() + + fireEvent.press(getByText("Unique"), "onPress") + + expect(() => getByText("Unique")).toThrow() + }) + + it("can't remove artist pill", () => { + const { getByText } = renderWithWrappers( + + + + + + ) + + expect(getByText("Banksy")).toBeDefined() + + fireEvent.press(getByText("Unique"), "onPress") + + expect(getByText("Banksy")).toBeDefined() + }) +}) + +const initialData = { + ...getNewArtworkFilterStoreModel(), + selectedFilters: [ + { + paramName: NewFilterParamName.attributionClass, + paramValue: { + value: "unique", + displayLabel: "Unique", + }, + }, + { + paramName: NewFilterParamName.attributionClass, + paramValue: { + value: "limited edition", + displayLabel: "Limited Edition", + }, + }, + ], +} + +const savedSearchEntity: SavedSearchEntity = { + artists: [{ id: "artistID", name: "Banksy" }], + owner: { + type: OwnerType.artist, + id: "ownerId", + slug: "ownerSlug", + }, +} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index fdfd3e9838c..2bc5fefa045 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -4,15 +4,15 @@ import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { useMemo } from "react" -export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: boolean }> = ({ - includeArtistNames, +export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames?: boolean }> = ({ + includeArtistNames = false, }) => { const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) const removeFilterAction = NewArtworksFiltersStore.useStoreActions( (state) => state.removeFilterAction ) - const entity = SavedSearchStore?.useStoreState((state) => state.entity) + const entity = SavedSearchStore.useStoreState((state) => state.entity) const artistPills = useMemo(() => { return entity?.artists.map((artist) => ({ @@ -41,7 +41,7 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames: bool {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} {allPills.map((pill, index) => ( Date: Fri, 6 Oct 2023 12:33:39 +0200 Subject: [PATCH 16/30] feat: add clear all button --- .../screens/AddFiltersScreen.tests.tsx | 57 ++++++++++++++++--- .../screens/AddFiltersScreen.tsx | 24 +++++--- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx index 7c191285e81..8d734394146 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx @@ -1,22 +1,61 @@ -import { fireEvent } from "@testing-library/react-native" +import { fireEvent, waitFor } from "@testing-library/react-native" +import { + NewArtworkFiltersStoreProvider, + getNewArtworkFilterStoreModel, +} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { ClearAllButton } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreen" -import { flushPromiseQueue } from "app/utils/tests/flushPromiseQueue" import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" import { Alert } from "react-native" jest.spyOn(Alert, "alert") describe("ClearAllButton", () => { - it("Shows an alert when pressed", async () => { - const { getByText } = renderWithWrappers() + it("Is enabled when there are active filters", () => { + const { getByText } = renderWithWrappers( + + + + ) fireEvent(getByText("Clear All"), "onPress") - await flushPromiseQueue() - - expect(Alert.alert).toHaveBeenCalled() + waitFor(() => { + expect(Alert.alert).toHaveBeenCalled() + }) }) - // TODO: Implement feature and test - it("Is disabled when there are no filters", () => {}) + it("Is disabled when there are no filters", async () => { + const { getByText } = renderWithWrappers( + + + + ) + + fireEvent(getByText("Clear All"), "onPress") + + waitFor(() => { + expect(Alert.alert).not.toHaveBeenCalled() + }) + }) }) + +const initialData = { + ...getNewArtworkFilterStoreModel(), + selectedFilters: [ + { + paramName: NewFilterParamName.attributionClass, + paramValue: { + value: "unique", + displayLabel: "Unique", + }, + }, + { + paramName: NewFilterParamName.attributionClass, + paramValue: { + value: "limited edition", + displayLabel: "Limited Edition", + }, + }, + ], +} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 51b9674b9bf..2c83cc1345e 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,14 +1,15 @@ -import { Flex, Join, Separator, Text } from "@artsy/palette-mobile" +import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" import { NewArtworkFilterAppliedFilters as AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" import { NewArtworkFiltersStoreProvider, + NewArtworksFiltersStore, getNewArtworkFilterStoreModel, } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { Alert, TouchableOpacity } from "react-native" +import { Alert } from "react-native" export const AddFiltersScreen: React.FC<{}> = () => { const navigation = useNavigation() @@ -50,24 +51,29 @@ export const AddFiltersScreenWrapper: React.FC<{}> = () => { } export const ClearAllButton = () => { - const disabled = false + const clearAllFiltersAction = NewArtworksFiltersStore.useStoreActions( + (state) => state.clearAllFiltersAction + ) + const disabled = + NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters).length === 0 return ( - { - Alert.alert("Are you sure you want to clear all filters?", "Not yet supported", [ + Alert.alert("Are you sure you want to clear all filters?", undefined, [ { text: "Cancel", - onPress() { - // Do nothing - }, + style: "cancel", }, { text: "Clear All", onPress() { // Trigger action to clear all filters + clearAllFiltersAction() }, style: "destructive", }, @@ -77,6 +83,6 @@ export const ClearAllButton = () => { Clear All - + ) } From 95a8b62f49032b960debcf28383030cdb48237dc Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 12:51:09 +0200 Subject: [PATCH 17/30] chore: add subtle animations: bring the screen to life --- .../NewArtworkFilterAppliedFilters.tsx | 31 ++++++++++++------- .../screens/AddFiltersScreen.tsx | 13 ++++++-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx index 2bc5fefa045..962cee73d91 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx @@ -2,6 +2,7 @@ import { Flex, Pill, Text } from "@artsy/palette-mobile" import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { MotiView } from "moti" import { useMemo } from "react" export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames?: boolean }> = ({ @@ -40,20 +41,26 @@ export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames?: boo {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} {allPills.map((pill, index) => ( - { - // Add remove - removeFilterAction(pill) - }} > - {pill.paramValue.displayLabel} - + { + // Add remove + removeFilterAction(pill) + }} + > + {pill.paramValue.displayLabel} + + ))} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 2c83cc1345e..94f77f805cd 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -9,6 +9,7 @@ import { getNewArtworkFilterStoreModel, } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { MotiView } from "moti" import { Alert } from "react-native" export const AddFiltersScreen: React.FC<{}> = () => { @@ -80,9 +81,15 @@ export const ClearAllButton = () => { ]) }} > - - Clear All - + + + Clear All + + ) } From b22ddb47d03089a5c4aa92f829b28f927ad76327 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 12:54:04 +0200 Subject: [PATCH 18/30] fix: revert disabled logic in clear all button --- src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 94f77f805cd..71c7369f010 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -83,7 +83,7 @@ export const ClearAllButton = () => { > From 2591c0f0c2eb018ae16d46e735daeeb1285c7550 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 13:42:36 +0200 Subject: [PATCH 19/30] chore: remove commented line --- src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 71c7369f010..e8d8208f7f3 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -21,7 +21,6 @@ export const AddFiltersScreen: React.FC<{}> = () => { hideBottomDivider onLeftButtonPress={navigation.goBack} renderRightButton={ClearAllButton} - // rightButtonText="Clear All" // TODO: Improve fancy modal header logic not to rely on this prop // in case renderRightButton is present onRightButtonPress={() => {}} From 322ed695908d919d6bb2be738828d44a6c97988c Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 6 Oct 2023 14:48:14 +0200 Subject: [PATCH 20/30] chore: remove unused fields from store --- .../NewArtworkFilterStore.tests.tsx | 17 ----------------- .../NewArtworkFilter/NewArtworkFilterStore.tsx | 18 +----------------- .../screens/AddFiltersScreen.tsx | 11 +---------- 3 files changed, 2 insertions(+), 44 deletions(-) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx index 608db67c312..0bbd58f4db6 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx @@ -1,4 +1,3 @@ -import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" import { NewArtworkFilterStoreModel, getNewArtworkFilterStoreModel, @@ -14,16 +13,13 @@ describe("NewArtworkFilterStore", () => { it("is correct when no injections are made", () => { const store = createFilterArtworksStore() expect(store.getState().previouslyAppliedFilters).toEqual([]) - expect(store.getState().aggregations).toEqual([]) expect(store.getState().selectedFilters).toEqual([]) }) it("is correct when injections are made", () => { const store = createFilterArtworksStore({ previouslyAppliedFilters, - aggregations, }) expect(store.getState().previouslyAppliedFilters).toEqual(previouslyAppliedFilters) - expect(store.getState().aggregations).toEqual(aggregations) expect(store.getState().selectedFilters).toEqual([]) }) }) @@ -38,16 +34,3 @@ const previouslyAppliedFilters: NewFilterData[] = [ }, }, ] - -const aggregations: Aggregations = [ - { - slice: "COLOR", - counts: [ - { - count: 10, - value: "blue", - name: "Blue", - }, - ], - }, -] diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx index 0037d1db41b..d7fcaaf2e7e 100644 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx +++ b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx @@ -1,4 +1,3 @@ -import { Aggregations } from "app/Components/ArtworkFilter/ArtworkFilterHelpers" import { NewFilterData } from "app/Components/NewArtworkFilter/helpers" import { assignDeep } from "app/store/persistence" import { Action, action, createContextStore } from "easy-peasy" @@ -14,22 +13,15 @@ export interface NewArtworkFilterStoreModel { // A list of filters that should be selected selectedFilters: NewFilterData[] - // An aray representing the available aggregations with the initial set of applied filters - aggregations: Aggregations - /********************************************************/ /** Actions **/ /********************************************************/ - // Add non applied filters to the list of applied filters - - applyFiltersAction: Action // Remove all applied and non applied filters clearAllFiltersAction: Action // Add a new filter to the list of not applied filters selectFilterAction: Action - // Set filter aggregations - setAggregationsAction: Action + // Remoove a filter from the list of not applied filters removeFilterAction: Action } @@ -41,16 +33,11 @@ export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ( previouslyAppliedFilters: [], selectedFilters: [], - aggregations: [], /********************************************************/ /** Actions **/ /********************************************************/ - applyFiltersAction: action((state) => { - state.previouslyAppliedFilters = [...state.selectedFilters] - state.selectedFilters = [] - }), clearAllFiltersAction: action((state) => { state.selectedFilters = [] }), @@ -67,9 +54,6 @@ export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ( state.selectedFilters = [...state.selectedFilters, filter] } }), - setAggregationsAction: action((state, aggregations) => { - state.aggregations = aggregations - }), removeFilterAction: action((state, filter) => { const valueToBeRemovedIndex = state.selectedFilters.findIndex((appliedFilter) => { if (isEqual(appliedFilter, filter)) { diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index e8d8208f7f3..44643e2fae7 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -6,9 +6,7 @@ import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilte import { NewArtworkFiltersStoreProvider, NewArtworksFiltersStore, - getNewArtworkFilterStoreModel, } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { MotiView } from "moti" import { Alert } from "react-native" @@ -36,15 +34,8 @@ export const AddFiltersScreen: React.FC<{}> = () => { } export const AddFiltersScreenWrapper: React.FC<{}> = () => { - const aggregations = SavedSearchStore.useStoreState((state) => state.aggregations) - return ( - + ) From c4038add0f3268a9958a939b4dec24ddee74774d Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 11:09:17 +0200 Subject: [PATCH 21/30] refact: use SavedSearchStore for ClearAllButton --- .../SavedSearchAlert/screens/AddFiltersScreen.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 44643e2fae7..6499b7f0258 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -3,10 +3,8 @@ import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" import { NewArtworkFilterAppliedFilters as AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" -import { - NewArtworkFiltersStoreProvider, - NewArtworksFiltersStore, -} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { NewArtworkFiltersStoreProvider } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" +import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { MotiView } from "moti" import { Alert } from "react-native" @@ -42,11 +40,11 @@ export const AddFiltersScreenWrapper: React.FC<{}> = () => { } export const ClearAllButton = () => { - const clearAllFiltersAction = NewArtworksFiltersStore.useStoreActions( - (state) => state.clearAllFiltersAction + const clearAllFiltersAction = SavedSearchStore.useStoreActions( + (state) => state.clearAllAttributesAction ) - const disabled = - NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters).length === 0 + const attributes = SavedSearchStore.useStoreState((state) => state.attributes) + const disabled = Object.keys(attributes).length === 0 return ( Date: Thu, 12 Oct 2023 11:35:09 +0200 Subject: [PATCH 22/30] refactor: update Applied fitlers to use SavedSearchStore --- .../NewArtworkFilterAppliedFilters.tests.tsx | 123 ------------------ .../NewArtworkFilterAppliedFilters.tsx | 68 ---------- .../screens/AddFiltersScreen.tsx | 4 +- .../AddFiltersScreenAppliedFilters.tests.tsx | 67 ++++++++++ .../AddFiltersScreenAppliedFilters.tsx | 47 +++++++ 5 files changed, 116 insertions(+), 193 deletions(-) delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx deleted file mode 100644 index 7424c2eb042..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tests.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { OwnerType } from "@artsy/cohesion" -import { fireEvent } from "@testing-library/react-native" -import { SavedSearchEntity } from "app/Components/ArtworkFilter/SavedSearch/types" -import { NewArtworkFilterAppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" -import { - NewArtworkFiltersStoreProvider, - getNewArtworkFilterStoreModel, -} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" -import { - SavedSearchStoreProvider, - savedSearchModel, -} from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" - -describe("NewArtworkFilterAppliedFilters", () => { - it("shows all selected filters", () => { - const { getByText } = renderWithWrappers( - - - - - - ) - - expect(getByText("Unique")).toBeDefined() - expect(getByText("Limited Edition")).toBeDefined() - expect(getByText("Banksy")).toBeDefined() - }) - - it("doesn't show the artist name when includeArtistNames is not specified", () => { - const { getByText } = renderWithWrappers( - - - - - - ) - - expect(() => getByText("Banksy")).toThrow() - }) - - it("removes filter when tapped", () => { - const { getByText } = renderWithWrappers( - - - - - - ) - - expect(getByText("Unique")).toBeDefined() - - fireEvent.press(getByText("Unique"), "onPress") - - expect(() => getByText("Unique")).toThrow() - }) - - it("can't remove artist pill", () => { - const { getByText } = renderWithWrappers( - - - - - - ) - - expect(getByText("Banksy")).toBeDefined() - - fireEvent.press(getByText("Unique"), "onPress") - - expect(getByText("Banksy")).toBeDefined() - }) -}) - -const initialData = { - ...getNewArtworkFilterStoreModel(), - selectedFilters: [ - { - paramName: NewFilterParamName.attributionClass, - paramValue: { - value: "unique", - displayLabel: "Unique", - }, - }, - { - paramName: NewFilterParamName.attributionClass, - paramValue: { - value: "limited edition", - displayLabel: "Limited Edition", - }, - }, - ], -} - -const savedSearchEntity: SavedSearchEntity = { - artists: [{ id: "artistID", name: "Banksy" }], - owner: { - type: OwnerType.artist, - id: "ownerId", - slug: "ownerSlug", - }, -} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx deleted file mode 100644 index 962cee73d91..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Flex, Pill, Text } from "@artsy/palette-mobile" -import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" -import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { MotiView } from "moti" -import { useMemo } from "react" - -export const NewArtworkFilterAppliedFilters: React.FC<{ includeArtistNames?: boolean }> = ({ - includeArtistNames = false, -}) => { - const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) - const removeFilterAction = NewArtworksFiltersStore.useStoreActions( - (state) => state.removeFilterAction - ) - - const entity = SavedSearchStore.useStoreState((state) => state.entity) - - const artistPills = useMemo(() => { - return entity?.artists.map((artist) => ({ - paramName: NewFilterParamName.artistIDs, - paramValue: { - value: artist.id, - displayLabel: artist.name, - }, - })) - }, [entity]) - - const allPills = useMemo(() => { - if (includeArtistNames) return [...artistPills, ...selectedFilters] - else { - return [...selectedFilters] - } - }, [selectedFilters, entity]) - - return ( - - - Your Filters - - - - {/* TODO: Adapt useSavedSearchPills to work here with little coupling from saved searches */} - {allPills.map((pill, index) => ( - - { - // Add remove - removeFilterAction(pill) - }} - > - {pill.paramValue.displayLabel} - - - ))} - - - ) -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 6499b7f0258..147056fa4b2 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,10 +1,10 @@ import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" -import { NewArtworkFilterAppliedFilters as AppliedFilters } from "app/Components/NewArtworkFilter/NewArtworkFilterAppliedFilters" import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" import { NewArtworkFiltersStoreProvider } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" import { MotiView } from "moti" import { Alert } from "react-native" @@ -24,7 +24,7 @@ export const AddFiltersScreen: React.FC<{}> = () => { Filters }> - + diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx new file mode 100644 index 00000000000..4794d5312a4 --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx @@ -0,0 +1,67 @@ +import { OwnerType } from "@artsy/cohesion" +import { fireEvent } from "@testing-library/react-native" + +import { + SavedSearchModel, + SavedSearchStoreProvider, + savedSearchModel, +} from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" + +describe("AddFiltersScreenAppliedFilters", () => { + it("shows all selected filters", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Open Edition")).toBeDefined() + expect(getByText("Banksy")).toBeDefined() + }) + + it("removes filter when tapped", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Open Edition")).toBeDefined() + + fireEvent.press(getByText("Open Edition"), "onPress") + + expect(() => getByText("Open Edition")).toThrow() + }) + + it("can't remove artist pill", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Banksy")).toBeDefined() + + fireEvent.press(getByText("Banksy"), "onPress") + + expect(getByText("Banksy")).toBeDefined() + }) +}) + +const initialData: SavedSearchModel = { + ...savedSearchModel, + attributes: { + attributionClass: ["open edition"], + atAuction: true, + }, + entity: { + artists: [{ id: "artistID", name: "Banksy" }], + owner: { + type: OwnerType.artist, + id: "ownerId", + slug: "ownerSlug", + }, + }, +} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx new file mode 100644 index 00000000000..1ff91f53a42 --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx @@ -0,0 +1,47 @@ +import { Flex, Pill, Text } from "@artsy/palette-mobile" +import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { useSavedSearchPills } from "app/Scenes/SavedSearchAlert/useSavedSearchPills" +import { MotiView } from "moti" + +export const AddFiltersScreenAppliedFilters: React.FC<{}> = ({}) => { + const removeValueFromAttributesByKeyAction = SavedSearchStore.useStoreActions( + (state) => state.removeValueFromAttributesByKeyAction + ) + + const pills = useSavedSearchPills() + + return ( + + + Your Filters + + + + {pills.map((pill) => ( + + { + // Add remove + removeValueFromAttributesByKeyAction({ + key: pill.paramName, + value: pill.value, + }) + }} + > + {pill.label} + + + ))} + + + ) +} From cd400b07b8892874bd1d7e4d10dac971fa5a2d65 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 11:38:51 +0200 Subject: [PATCH 23/30] chore: do not allow artistIDs pills removal --- .../screens/AddFiltersScreenAppliedFilters.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx index 1ff91f53a42..00da937bd9f 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx @@ -1,4 +1,5 @@ import { Flex, Pill, Text } from "@artsy/palette-mobile" +import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { useSavedSearchPills } from "app/Scenes/SavedSearchAlert/useSavedSearchPills" import { MotiView } from "moti" @@ -28,7 +29,10 @@ export const AddFiltersScreenAppliedFilters: React.FC<{}> = ({}) => { m={0.5} variant="filter" accessibilityLabel={pill.label} - disabled={pill.paramName === "artistID"} + disabled={ + pill.paramName === SearchCriteria.artistID || + pill.paramName === SearchCriteria.artistIDs + } onPress={() => { // Add remove removeValueFromAttributesByKeyAction({ From bcd561f7ca3d6f0449c2a73891a58c2833151dc5 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 11:42:50 +0200 Subject: [PATCH 24/30] chore: do not count artistIDs for clear all button counts --- .../Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 147056fa4b2..6f288760b07 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -1,5 +1,6 @@ import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" +import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" import { NewArtworkFiltersStoreProvider } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" @@ -44,7 +45,10 @@ export const ClearAllButton = () => { (state) => state.clearAllAttributesAction ) const attributes = SavedSearchStore.useStoreState((state) => state.attributes) - const disabled = Object.keys(attributes).length === 0 + const disabled = + Object.keys(attributes).filter( + (key) => key !== SearchCriteria.artistID && key !== SearchCriteria.artistIDs + ).length === 0 return ( Date: Thu, 12 Oct 2023 13:11:55 +0200 Subject: [PATCH 25/30] refactor: use SavedSearchStoreProvider for rarity --- .../NewArtworkFilterRarity.tests.tsx | 72 ----------------- .../NewArtworkFilterRarity.tsx | 77 ------------------- .../screens/AddFiltersScreen.tsx | 20 +++-- .../screens/NewArtworkFilterRarity.tests.tsx | 74 ++++++++++++++++++ .../screens/NewArtworkFilterRarity.tsx | 68 ++++++++++++++++ 5 files changed, 155 insertions(+), 156 deletions(-) delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tests.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx deleted file mode 100644 index 11ac1ffb877..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tests.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { fireEvent, waitFor } from "@testing-library/react-native" -import { - ATTRIBUTION_CLASS_OPTIONS, - NewArtworkFilterRarity, -} from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" -import { - NewArtworkFiltersStoreProvider, - getNewArtworkFilterStoreModel, -} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" -import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" - -const black100Hex = "#000000" - -describe("NewArtworkFilterRarity", () => { - it("shows all available rarity options unselected", () => { - const { getByText } = renderWithWrappers( - - - - ) - - ATTRIBUTION_CLASS_OPTIONS.forEach((option) => { - expect(getByText(option.displayLabel)).toBeDefined() - expect(getByText(option.displayLabel)).toHaveStyle({ - color: black100Hex, - }) - }) - }) - - it("shows the right selected state with the right colors", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) - expect(getByText("Limited Edition")).toHaveStyle({ color: black100Hex }) - expect(getByText("Open Edition")).toHaveStyle({ color: black100Hex }) - expect(getByText("Unknown Edition")).toHaveStyle({ color: black100Hex }) - }) - - it("Updates selected filters on press", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Unique")).toHaveStyle({ color: black100Hex }) - - fireEvent(getByText("Unique"), "onPress") - - waitFor(() => { - expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) - }) - }) -}) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx deleted file mode 100644 index 43cb9f3efec..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterRarity.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Flex, Spacer, Text } from "@artsy/palette-mobile" -import { FillPill } from "app/Components/NewArtworkFilter/NewArtworkFilterPill" -import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { - NewFilterData, - NewFilterParamName, - isFilterSelected, - useSelectedFiltersByParamName, -} from "app/Components/NewArtworkFilter/helpers" - -export const NewArtworkFilterRarity = () => { - const selectedFilters = useSelectedFiltersByParamName(NewFilterParamName.attributionClass) - - const selectFilterAction = NewArtworksFiltersStore.useStoreActions( - (actions) => actions.selectFilterAction - ) - const removeFilterAction = NewArtworksFiltersStore.useStoreActions( - (actions) => actions.removeFilterAction - ) - - const handlePress = (value: Omit["paramValue"]) => { - const selectedFilterData = { - paramName: NewFilterParamName.attributionClass, - paramValue: value, - } - - if (isFilterSelected(selectedFilters, value)) { - removeFilterAction(selectedFilterData) - } else { - selectFilterAction(selectedFilterData) - } - } - - return ( - - - Rarity - - - - {ATTRIBUTION_CLASS_OPTIONS.map((option) => { - return ( - { - handlePress(option) - }} - > - {option.displayLabel} - - ) - })} - - - ) -} - -export const ATTRIBUTION_CLASS_OPTIONS: NewFilterData["paramValue"][] = [ - { - value: "unique", - displayLabel: "Unique", - }, - { - value: "limited edition", - displayLabel: "Limited Edition", - }, - { - value: "open edition", - displayLabel: "Open Edition", - }, - { - value: "unknown edition", - displayLabel: "Unknown Edition", - }, -] diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index 6f288760b07..f24ba247a2e 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -2,10 +2,10 @@ import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" -import { NewArtworkFilterRarity as Rarity } from "app/Components/NewArtworkFilter/NewArtworkFilterRarity" import { NewArtworkFiltersStoreProvider } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" +import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity" import { MotiView } from "moti" import { Alert } from "react-native" @@ -18,15 +18,13 @@ export const AddFiltersScreen: React.FC<{}> = () => { hideBottomDivider onLeftButtonPress={navigation.goBack} renderRightButton={ClearAllButton} - // TODO: Improve fancy modal header logic not to rely on this prop - // in case renderRightButton is present onRightButtonPress={() => {}} > Filters }> - + ) @@ -46,9 +44,17 @@ export const ClearAllButton = () => { ) const attributes = SavedSearchStore.useStoreState((state) => state.attributes) const disabled = - Object.keys(attributes).filter( - (key) => key !== SearchCriteria.artistID && key !== SearchCriteria.artistIDs - ).length === 0 + Object.entries(attributes).filter((keyValue) => { + const key = keyValue[0] + const value = keyValue[1] + if (key !== SearchCriteria.artistID && key !== SearchCriteria.artistIDs) { + // Values might be empty arrays + if (Array.isArray(value)) { + return value.length > 0 + } + return true + } + }).length === 0 return ( { + it("shows all available rarity options unselected", () => { + const { getByText } = renderWithWrappers( + + + + ) + + ATTRIBUTION_CLASS_OPTIONS.forEach((option) => { + expect(getByText(option.displayText)).toBeDefined() + expect(getByText(option.displayText)).toHaveStyle({ + color: black100Hex, + }) + }) + }) + + it("shows the right selected state with the right colors", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) + expect(getByText("Limited Edition")).toHaveStyle({ color: black100Hex }) + expect(getByText("Open Edition")).toHaveStyle({ color: black100Hex }) + expect(getByText("Unknown Edition")).toHaveStyle({ color: black100Hex }) + }) + + it("Updates selected filters on press", () => { + const { getByText } = renderWithWrappers( + + + + ) + + expect(getByText("Unique")).toHaveStyle({ color: black100Hex }) + + fireEvent(getByText("Unique"), "onPress") + + waitFor(() => { + expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) + }) + }) +}) + +const initialData: SavedSearchModel = { + ...savedSearchModel, + attributes: { + attributionClass: ["open edition"], + atAuction: true, + }, + entity: { + artists: [{ id: "artistID", name: "Banksy" }], + owner: { + type: OwnerType.artist, + id: "ownerId", + slug: "ownerSlug", + }, + }, +} diff --git a/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx b/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx new file mode 100644 index 00000000000..624af1bc18b --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx @@ -0,0 +1,68 @@ +import { Flex, Spacer, Text } from "@artsy/palette-mobile" +import { ATTRIBUTION_CLASS_OPTIONS } from "app/Components/ArtworkFilter/Filters/AttributionClassOptions" +import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" +import { FillPill } from "app/Components/NewArtworkFilter/NewArtworkFilterPill" +import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { isValueSelected, useSearchCriteriaAttributes } from "app/Scenes/SavedSearchAlert/helpers" + +export const AddFiltersScreenRarity = () => { + const selectedAttributes = useSearchCriteriaAttributes( + SearchCriteria.attributionClass + ) as string[] + + const setValueToAttributesByKeyAction = SavedSearchStore.useStoreActions( + (actions) => actions.setValueToAttributesByKeyAction + ) + const removeValueFromAttributesByKeyAction = SavedSearchStore.useStoreActions( + (actions) => actions.removeValueFromAttributesByKeyAction + ) + + const handlePress = (value: string) => { + if ( + isValueSelected({ + selectedAttributes, + value: value, + }) + ) { + removeValueFromAttributesByKeyAction({ + key: SearchCriteria.attributionClass, + value: value, + }) + } else { + const newValues = (selectedAttributes || []).concat(value) + + setValueToAttributesByKeyAction({ + key: SearchCriteria.attributionClass, + value: newValues, + }) + } + } + + return ( + + + Rarity + + + + {ATTRIBUTION_CLASS_OPTIONS.map((option) => { + return ( + { + handlePress(option.paramValue as string) + }} + > + {option.displayText} + + ) + })} + + + ) +} From f73a374709cc20f9bb636206634a644145926343 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 13:21:45 +0200 Subject: [PATCH 26/30] refactor: remove new filters store --- .../NewArtworkFilterStore.tests.tsx | 36 -------- .../NewArtworkFilterStore.tsx | 87 ------------------- .../Components/NewArtworkFilter/helpers.ts | 53 ----------- .../screens/AddFilterPill.tsx} | 0 .../screens/AddFiltersScreen.tests.tsx | 72 +++++++++------ .../screens/AddFiltersScreen.tsx | 11 +-- ...s.tsx => AddFiltersScreenRarity.tests.tsx} | 2 +- ...rRarity.tsx => AddFiltersScreenRarity.tsx} | 2 +- 8 files changed, 49 insertions(+), 214 deletions(-) delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx delete mode 100644 src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx delete mode 100644 src/app/Components/NewArtworkFilter/helpers.ts rename src/app/{Components/NewArtworkFilter/NewArtworkFilterPill.tsx => Scenes/SavedSearchAlert/screens/AddFilterPill.tsx} (100%) rename src/app/Scenes/SavedSearchAlert/screens/{NewArtworkFilterRarity.tests.tsx => AddFiltersScreenRarity.tests.tsx} (98%) rename src/app/Scenes/SavedSearchAlert/screens/{NewArtworkFilterRarity.tsx => AddFiltersScreenRarity.tsx} (96%) diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx deleted file mode 100644 index 0bbd58f4db6..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tests.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { - NewArtworkFilterStoreModel, - getNewArtworkFilterStoreModel, -} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { NewFilterData, NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" -import { createStore } from "easy-peasy" - -const createFilterArtworksStore = (state?: Partial) => - createStore({ ...getNewArtworkFilterStoreModel(), ...state }) - -describe("NewArtworkFilterStore", () => { - describe("initialState", () => { - it("is correct when no injections are made", () => { - const store = createFilterArtworksStore() - expect(store.getState().previouslyAppliedFilters).toEqual([]) - expect(store.getState().selectedFilters).toEqual([]) - }) - it("is correct when injections are made", () => { - const store = createFilterArtworksStore({ - previouslyAppliedFilters, - }) - expect(store.getState().previouslyAppliedFilters).toEqual(previouslyAppliedFilters) - expect(store.getState().selectedFilters).toEqual([]) - }) - }) -}) - -const previouslyAppliedFilters: NewFilterData[] = [ - { - paramName: NewFilterParamName.categories, - paramValue: { - value: "painting", - displayLabel: "Painting", - }, - }, -] diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx b/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx deleted file mode 100644 index d7fcaaf2e7e..00000000000 --- a/src/app/Components/NewArtworkFilter/NewArtworkFilterStore.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { NewFilterData } from "app/Components/NewArtworkFilter/helpers" -import { assignDeep } from "app/store/persistence" -import { Action, action, createContextStore } from "easy-peasy" -import { isEqual } from "lodash" - -export interface NewArtworkFilterStoreModel { - /********************************************************/ - /** State **/ - /********************************************************/ - - // A list of filters that are already applied to the current filter state - previouslyAppliedFilters: NewFilterData[] - // A list of filters that should be selected - selectedFilters: NewFilterData[] - - /********************************************************/ - /** Actions **/ - /********************************************************/ - - // Remove all applied and non applied filters - clearAllFiltersAction: Action - // Add a new filter to the list of not applied filters - selectFilterAction: Action - - // Remoove a filter from the list of not applied filters - removeFilterAction: Action -} - -export const getNewArtworkFilterStoreModel = (): NewArtworkFilterStoreModel => ({ - /********************************************************/ - /** State **/ - /********************************************************/ - - previouslyAppliedFilters: [], - selectedFilters: [], - - /********************************************************/ - /** Actions **/ - /********************************************************/ - - clearAllFiltersAction: action((state) => { - state.selectedFilters = [] - }), - selectFilterAction: action((state, filter) => { - const alreadySelected = state.selectedFilters.find((appliedFilter) => { - if (isEqual(appliedFilter, filter)) { - return true - } - return false - }) - - // No need to add a filter that is already selected or applied - if (!alreadySelected) { - state.selectedFilters = [...state.selectedFilters, filter] - } - }), - removeFilterAction: action((state, filter) => { - const valueToBeRemovedIndex = state.selectedFilters.findIndex((appliedFilter) => { - if (isEqual(appliedFilter, filter)) { - return true - } - return false - }) - - if (valueToBeRemovedIndex > -1) { - // only splice array when item is found - state.selectedFilters.splice(valueToBeRemovedIndex, 1) // 2nd parameter means remove one item only - } - }), -}) - -export function createArtworkFiltersStore() { - if (__TEST__) { - ;(getNewArtworkFilterStoreModel() as any).__injectState = action((state, injectedState) => { - assignDeep(state, injectedState) - }) - } - const store = createContextStore((initialData) => ({ - ...getNewArtworkFilterStoreModel(), - ...initialData, - })) - return store -} - -export const NewArtworksFiltersStore = createArtworkFiltersStore() - -export const NewArtworkFiltersStoreProvider = NewArtworksFiltersStore.Provider diff --git a/src/app/Components/NewArtworkFilter/helpers.ts b/src/app/Components/NewArtworkFilter/helpers.ts deleted file mode 100644 index a289a042417..00000000000 --- a/src/app/Components/NewArtworkFilter/helpers.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { NewArtworksFiltersStore } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { isEqual } from "lodash" - -export enum NewFilterParamName { - artistIDs = "artistIDs", - attributionClass = "rarity", - categories = "categories", -} - -export type NewFilterData = - | { - paramName: NewFilterParamName.artistIDs - paramValue: { - value: string - displayLabel: string - } - } - | { - paramName: NewFilterParamName.attributionClass - paramValue: { - value: string - displayLabel: string - } - } - | { - paramName: NewFilterParamName.categories - paramValue: { - value: string - displayLabel: string - } - } - -export const paramNameToDisplayLabelMap = { - attributionClass: "Rarity", - categories: "Categories", -} - -// A hook that returns the applied filters for a given paramName -export const useSelectedFiltersByParamName = (paramName: NewFilterParamName) => { - const selectedFilters = NewArtworksFiltersStore.useStoreState((state) => state.selectedFilters) - - return selectedFilters.filter((filter) => filter.paramName === paramName) -} - -export const isFilterSelected = ( - selectedFilters: NewFilterData[], - filterValue: NewFilterData["paramValue"] -) => { - if (selectedFilters.find((selectedFilter) => isEqual(selectedFilter.paramValue, filterValue))) { - return true - } - return false -} diff --git a/src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx similarity index 100% rename from src/app/Components/NewArtworkFilter/NewArtworkFilterPill.tsx rename to src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx index 8d734394146..f272e507468 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx @@ -1,9 +1,10 @@ +import { OwnerType } from "@artsy/cohesion" import { fireEvent, waitFor } from "@testing-library/react-native" import { - NewArtworkFiltersStoreProvider, - getNewArtworkFilterStoreModel, -} from "app/Components/NewArtworkFilter/NewArtworkFilterStore" -import { NewFilterParamName } from "app/Components/NewArtworkFilter/helpers" + SavedSearchModel, + SavedSearchStoreProvider, + savedSearchModel, +} from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { ClearAllButton } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreen" import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" import { Alert } from "react-native" @@ -13,9 +14,9 @@ jest.spyOn(Alert, "alert") describe("ClearAllButton", () => { it("Is enabled when there are active filters", () => { const { getByText } = renderWithWrappers( - + - + ) fireEvent(getByText("Clear All"), "onPress") @@ -25,11 +26,37 @@ describe("ClearAllButton", () => { }) }) - it("Is disabled when there are no filters", async () => { + it("Is disabled on load", async () => { const { getByText } = renderWithWrappers( - + - + + ) + + fireEvent(getByText("Clear All"), "onPress") + + waitFor(() => { + expect(Alert.alert).not.toHaveBeenCalled() + }) + }) + + it("Is disabled when array attrbutes are empty", async () => { + const { getByText } = renderWithWrappers( + + + ) fireEvent(getByText("Clear All"), "onPress") @@ -40,22 +67,15 @@ describe("ClearAllButton", () => { }) }) -const initialData = { - ...getNewArtworkFilterStoreModel(), - selectedFilters: [ - { - paramName: NewFilterParamName.attributionClass, - paramValue: { - value: "unique", - displayLabel: "Unique", - }, - }, - { - paramName: NewFilterParamName.attributionClass, - paramValue: { - value: "limited edition", - displayLabel: "Limited Edition", - }, +const initialData: SavedSearchModel = { + ...savedSearchModel, + attributes: {}, + entity: { + artists: [{ id: "artistID", name: "Banksy" }], + owner: { + type: OwnerType.artist, + id: "ownerId", + slug: "ownerSlug", }, - ], + }, } diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx index f24ba247a2e..681a34679de 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx @@ -2,10 +2,9 @@ import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" import { useNavigation } from "@react-navigation/native" import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" -import { NewArtworkFiltersStoreProvider } from "app/Components/NewArtworkFilter/NewArtworkFilterStore" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" -import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity" +import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity" import { MotiView } from "moti" import { Alert } from "react-native" @@ -30,14 +29,6 @@ export const AddFiltersScreen: React.FC<{}> = () => { ) } -export const AddFiltersScreenWrapper: React.FC<{}> = () => { - return ( - - - - ) -} - export const ClearAllButton = () => { const clearAllFiltersAction = SavedSearchStore.useStoreActions( (state) => state.clearAllAttributesAction diff --git a/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx similarity index 98% rename from src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tests.tsx rename to src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx index c9002a61f5f..0686ff91e49 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx @@ -6,7 +6,7 @@ import { SavedSearchStoreProvider, savedSearchModel, } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity" +import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity" import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" const black100Hex = "#000000" diff --git a/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx similarity index 96% rename from src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx rename to src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx index 624af1bc18b..090ac9f2533 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/NewArtworkFilterRarity.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx @@ -1,9 +1,9 @@ import { Flex, Spacer, Text } from "@artsy/palette-mobile" import { ATTRIBUTION_CLASS_OPTIONS } from "app/Components/ArtworkFilter/Filters/AttributionClassOptions" import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" -import { FillPill } from "app/Components/NewArtworkFilter/NewArtworkFilterPill" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { isValueSelected, useSearchCriteriaAttributes } from "app/Scenes/SavedSearchAlert/helpers" +import { FillPill } from "app/Scenes/SavedSearchAlert/screens/AddFilterPill" export const AddFiltersScreenRarity = () => { const selectedAttributes = useSearchCriteriaAttributes( From 34491213150f1c3a744db012a4dd05cdc44af50f Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 13:29:00 +0200 Subject: [PATCH 27/30] fix: rarity filter test --- .../screens/AddFiltersScreenRarity.tests.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx index 0686ff91e49..5beea6b6131 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx @@ -29,7 +29,9 @@ describe("AddFiltersScreenRarity", () => { it("shows the right selected state with the right colors", () => { const { getByText } = renderWithWrappers( - + ) @@ -60,7 +62,6 @@ describe("AddFiltersScreenRarity", () => { const initialData: SavedSearchModel = { ...savedSearchModel, attributes: { - attributionClass: ["open edition"], atAuction: true, }, entity: { From 76dd082af24381666a2d7d2651147d20a1825b7f Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 14:45:58 +0200 Subject: [PATCH 28/30] chore: address review comments --- .../screens/AddFilterPill.tsx | 28 ------- .../screens/AddFiltersScreen.tests.tsx | 81 ------------------ .../screens/AddFiltersScreen.tsx | 84 ------------------- .../AddFiltersScreenAppliedFilters.tests.tsx | 67 --------------- .../AddFiltersScreenAppliedFilters.tsx | 51 ----------- .../screens/AddFiltersScreenRarity.tests.tsx | 75 ----------------- .../screens/AddFiltersScreenRarity.tsx | 68 --------------- 7 files changed, 454 deletions(-) delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx delete mode 100644 src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx deleted file mode 100644 index b88c2c571a0..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFilterPill.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Pill, PillProps } from "@artsy/palette-mobile" -import { useEffect, useState } from "react" -import useDebounce from "react-use/lib/useDebounce" - -export const FillPill: React.FC = (props) => { - const [selected, setSelected] = useState(props.selected) - - useDebounce( - () => { - if (selected !== props.selected) { - props.onPress?.() - } - }, - 200, - [selected] - ) - - // Make sure that the selected state is in sync with the state in the fitler store - useEffect(() => { - if (props.selected !== selected) { - setSelected(props.selected) - } - }, [props.selected]) - - return ( - setSelected(!selected)} /> - ) -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx deleted file mode 100644 index f272e507468..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tests.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { OwnerType } from "@artsy/cohesion" -import { fireEvent, waitFor } from "@testing-library/react-native" -import { - SavedSearchModel, - SavedSearchStoreProvider, - savedSearchModel, -} from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { ClearAllButton } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreen" -import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" -import { Alert } from "react-native" - -jest.spyOn(Alert, "alert") - -describe("ClearAllButton", () => { - it("Is enabled when there are active filters", () => { - const { getByText } = renderWithWrappers( - - - - ) - - fireEvent(getByText("Clear All"), "onPress") - - waitFor(() => { - expect(Alert.alert).toHaveBeenCalled() - }) - }) - - it("Is disabled on load", async () => { - const { getByText } = renderWithWrappers( - - - - ) - - fireEvent(getByText("Clear All"), "onPress") - - waitFor(() => { - expect(Alert.alert).not.toHaveBeenCalled() - }) - }) - - it("Is disabled when array attrbutes are empty", async () => { - const { getByText } = renderWithWrappers( - - - - ) - - fireEvent(getByText("Clear All"), "onPress") - - waitFor(() => { - expect(Alert.alert).not.toHaveBeenCalled() - }) - }) -}) - -const initialData: SavedSearchModel = { - ...savedSearchModel, - attributes: {}, - entity: { - artists: [{ id: "artistID", name: "Banksy" }], - owner: { - type: OwnerType.artist, - id: "ownerId", - slug: "ownerSlug", - }, - }, -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx deleted file mode 100644 index 681a34679de..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreen.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { Flex, Join, Separator, Text, Touchable } from "@artsy/palette-mobile" -import { useNavigation } from "@react-navigation/native" -import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" -import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" -import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" -import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity" -import { MotiView } from "moti" -import { Alert } from "react-native" - -export const AddFiltersScreen: React.FC<{}> = () => { - const navigation = useNavigation() - - return ( - - {}} - > - Filters - - }> - - - - - ) -} - -export const ClearAllButton = () => { - const clearAllFiltersAction = SavedSearchStore.useStoreActions( - (state) => state.clearAllAttributesAction - ) - const attributes = SavedSearchStore.useStoreState((state) => state.attributes) - const disabled = - Object.entries(attributes).filter((keyValue) => { - const key = keyValue[0] - const value = keyValue[1] - if (key !== SearchCriteria.artistID && key !== SearchCriteria.artistIDs) { - // Values might be empty arrays - if (Array.isArray(value)) { - return value.length > 0 - } - return true - } - }).length === 0 - - return ( - { - Alert.alert("Are you sure you want to clear all filters?", undefined, [ - { - text: "Cancel", - style: "cancel", - }, - - { - text: "Clear All", - onPress() { - // Trigger action to clear all filters - clearAllFiltersAction() - }, - style: "destructive", - }, - ]) - }} - > - - - Clear All - - - - ) -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx deleted file mode 100644 index 4794d5312a4..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tests.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { OwnerType } from "@artsy/cohesion" -import { fireEvent } from "@testing-library/react-native" - -import { - SavedSearchModel, - SavedSearchStoreProvider, - savedSearchModel, -} from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { AddFiltersScreenAppliedFilters } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters" -import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" - -describe("AddFiltersScreenAppliedFilters", () => { - it("shows all selected filters", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Open Edition")).toBeDefined() - expect(getByText("Banksy")).toBeDefined() - }) - - it("removes filter when tapped", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Open Edition")).toBeDefined() - - fireEvent.press(getByText("Open Edition"), "onPress") - - expect(() => getByText("Open Edition")).toThrow() - }) - - it("can't remove artist pill", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Banksy")).toBeDefined() - - fireEvent.press(getByText("Banksy"), "onPress") - - expect(getByText("Banksy")).toBeDefined() - }) -}) - -const initialData: SavedSearchModel = { - ...savedSearchModel, - attributes: { - attributionClass: ["open edition"], - atAuction: true, - }, - entity: { - artists: [{ id: "artistID", name: "Banksy" }], - owner: { - type: OwnerType.artist, - id: "ownerId", - slug: "ownerSlug", - }, - }, -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx deleted file mode 100644 index 00da937bd9f..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenAppliedFilters.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Flex, Pill, Text } from "@artsy/palette-mobile" -import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" -import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { useSavedSearchPills } from "app/Scenes/SavedSearchAlert/useSavedSearchPills" -import { MotiView } from "moti" - -export const AddFiltersScreenAppliedFilters: React.FC<{}> = ({}) => { - const removeValueFromAttributesByKeyAction = SavedSearchStore.useStoreActions( - (state) => state.removeValueFromAttributesByKeyAction - ) - - const pills = useSavedSearchPills() - - return ( - - - Your Filters - - - - {pills.map((pill) => ( - - { - // Add remove - removeValueFromAttributesByKeyAction({ - key: pill.paramName, - value: pill.value, - }) - }} - > - {pill.label} - - - ))} - - - ) -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx deleted file mode 100644 index 5beea6b6131..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tests.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { OwnerType } from "@artsy/cohesion" -import { fireEvent, waitFor } from "@testing-library/react-native" -import { ATTRIBUTION_CLASS_OPTIONS } from "app/Components/ArtworkFilter/Filters/AttributionClassOptions" -import { - SavedSearchModel, - SavedSearchStoreProvider, - savedSearchModel, -} from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { AddFiltersScreenRarity } from "app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity" -import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" - -const black100Hex = "#000000" - -describe("AddFiltersScreenRarity", () => { - it("shows all available rarity options unselected", () => { - const { getByText } = renderWithWrappers( - - - - ) - - ATTRIBUTION_CLASS_OPTIONS.forEach((option) => { - expect(getByText(option.displayText)).toBeDefined() - expect(getByText(option.displayText)).toHaveStyle({ - color: black100Hex, - }) - }) - }) - - it("shows the right selected state with the right colors", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) - expect(getByText("Limited Edition")).toHaveStyle({ color: black100Hex }) - expect(getByText("Open Edition")).toHaveStyle({ color: black100Hex }) - expect(getByText("Unknown Edition")).toHaveStyle({ color: black100Hex }) - }) - - it("Updates selected filters on press", () => { - const { getByText } = renderWithWrappers( - - - - ) - - expect(getByText("Unique")).toHaveStyle({ color: black100Hex }) - - fireEvent(getByText("Unique"), "onPress") - - waitFor(() => { - expect(getByText("Unique")).not.toHaveStyle({ color: black100Hex }) - }) - }) -}) - -const initialData: SavedSearchModel = { - ...savedSearchModel, - attributes: { - atAuction: true, - }, - entity: { - artists: [{ id: "artistID", name: "Banksy" }], - owner: { - type: OwnerType.artist, - id: "ownerId", - slug: "ownerSlug", - }, - }, -} diff --git a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx b/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx deleted file mode 100644 index 090ac9f2533..00000000000 --- a/src/app/Scenes/SavedSearchAlert/screens/AddFiltersScreenRarity.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Flex, Spacer, Text } from "@artsy/palette-mobile" -import { ATTRIBUTION_CLASS_OPTIONS } from "app/Components/ArtworkFilter/Filters/AttributionClassOptions" -import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" -import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" -import { isValueSelected, useSearchCriteriaAttributes } from "app/Scenes/SavedSearchAlert/helpers" -import { FillPill } from "app/Scenes/SavedSearchAlert/screens/AddFilterPill" - -export const AddFiltersScreenRarity = () => { - const selectedAttributes = useSearchCriteriaAttributes( - SearchCriteria.attributionClass - ) as string[] - - const setValueToAttributesByKeyAction = SavedSearchStore.useStoreActions( - (actions) => actions.setValueToAttributesByKeyAction - ) - const removeValueFromAttributesByKeyAction = SavedSearchStore.useStoreActions( - (actions) => actions.removeValueFromAttributesByKeyAction - ) - - const handlePress = (value: string) => { - if ( - isValueSelected({ - selectedAttributes, - value: value, - }) - ) { - removeValueFromAttributesByKeyAction({ - key: SearchCriteria.attributionClass, - value: value, - }) - } else { - const newValues = (selectedAttributes || []).concat(value) - - setValueToAttributesByKeyAction({ - key: SearchCriteria.attributionClass, - value: newValues, - }) - } - } - - return ( - - - Rarity - - - - {ATTRIBUTION_CLASS_OPTIONS.map((option) => { - return ( - { - handlePress(option.paramValue as string) - }} - > - {option.displayText} - - ) - })} - - - ) -} From 9c6756782fa208c9e898c64e10b21553c8b14bb2 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 15:55:20 +0200 Subject: [PATCH 29/30] feat: add color filtering --- .../ArtworkFilter/Filters/ColorsOptions.tsx | 11 ++- .../ArtworkFilter/Filters/ColorsSwatch.tsx | 1 + .../SavedSearchFilterColour.tests.tsx | 78 +++++++++++++++++++ .../Components/SavedSearchFilterColour.tsx | 75 ++++++++++++++++++ .../screens/SavedSearchFilterScreen.tsx | 2 + 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx create mode 100644 src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx diff --git a/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx b/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx index 91c3872b17d..3854372e784 100644 --- a/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx +++ b/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx @@ -38,7 +38,16 @@ export const COLORS_INDEXED_BY_VALUE = COLORS.reduce( {} ) -const SWATCHES_PER_ROW = 4 +export const COLORS_OPTIONS: FilterData[] = COLORS.map((color) => { + return { + // names returned by Metaphysics are actually the slugs + displayText: color.name, + paramValue: color.value, + paramName: FilterParamName.colors, + } +}) + +export const SWATCHES_PER_ROW = 4 type ColorsOptionsScreenProps = StackScreenProps< ArtworkFilterNavigationStack, diff --git a/src/app/Components/ArtworkFilter/Filters/ColorsSwatch.tsx b/src/app/Components/ArtworkFilter/Filters/ColorsSwatch.tsx index fd31179ab35..90744b1f72a 100644 --- a/src/app/Components/ArtworkFilter/Filters/ColorsSwatch.tsx +++ b/src/app/Components/ArtworkFilter/Filters/ColorsSwatch.tsx @@ -37,6 +37,7 @@ export const ColorsSwatch: React.FC = ({ height="18px" marginTop="-9px" marginLeft="-9px" + testID={`check-icon-${name}`} fill={foregroundColor as any} // Annoying /> )} diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx new file mode 100644 index 00000000000..5ff49d64659 --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx @@ -0,0 +1,78 @@ +import { OwnerType } from "@artsy/cohesion" +import { fireEvent, waitFor } from "@testing-library/react-native" +import { + COLORS_INDEXED_BY_VALUE, + COLORS_OPTIONS, +} from "app/Components/ArtworkFilter/Filters/ColorsOptions" +import { SavedSearchFilterColour } from "app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour" +import { + SavedSearchModel, + SavedSearchStoreProvider, + savedSearchModel, +} from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { renderWithWrappers } from "app/utils/tests/renderWithWrappers" + +describe("SavedSearchFilterColour", () => { + it("shows all available color options unselected", () => { + const { getByTestId } = renderWithWrappers( + + + + ) + + COLORS_OPTIONS.forEach((option) => { + expect(() => + getByTestId(`check-icon-${COLORS_INDEXED_BY_VALUE[option.paramValue as string].name}`) + ).toThrow() + }) + }) + + it("shows the right selected state", () => { + const { getByTestId } = renderWithWrappers( + + + + ) + + COLORS_OPTIONS.forEach((option) => { + if (option.paramValue !== "red") { + expect(() => + getByTestId(`check-icon-${COLORS_INDEXED_BY_VALUE[option.paramValue as string].name}`) + ).toThrow() + } else { + expect( + getByTestId(`check-icon-${COLORS_INDEXED_BY_VALUE[option.paramValue as string].name}`) + ).toBeDefined() + } + }) + }) + + it("Updates selected filters on press", () => { + const { getByTestId, getByText } = renderWithWrappers( + + + + ) + + fireEvent(getByText("Red"), "onPress") + + waitFor(() => { + expect(getByTestId("check-icon-Red")).toBeDefined() + }) + }) +}) + +const initialData: SavedSearchModel = { + ...savedSearchModel, + attributes: { + atAuction: true, + }, + entity: { + artists: [{ id: "artistID", name: "Banksy" }], + owner: { + type: OwnerType.artist, + id: "ownerId", + slug: "ownerSlug", + }, + }, +} diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx new file mode 100644 index 00000000000..92cc3426d1f --- /dev/null +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx @@ -0,0 +1,75 @@ +import { Flex, Spacer, Text, useScreenDimensions, useSpace } from "@artsy/palette-mobile" +import { + COLORS_INDEXED_BY_VALUE, + COLORS_OPTIONS, + SWATCHES_PER_ROW, +} from "app/Components/ArtworkFilter/Filters/ColorsOptions" +import { ColorsSwatch } from "app/Components/ArtworkFilter/Filters/ColorsSwatch" +import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" +import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" +import { isValueSelected, useSearchCriteriaAttributes } from "app/Scenes/SavedSearchAlert/helpers" + +export const SavedSearchFilterColour = () => { + const selectedAttributes = useSearchCriteriaAttributes(SearchCriteria.colors) as string[] + + const { width } = useScreenDimensions() + const space = useSpace() + + const setValueToAttributesByKeyAction = SavedSearchStore.useStoreActions( + (actions) => actions.setValueToAttributesByKeyAction + ) + const removeValueFromAttributesByKeyAction = SavedSearchStore.useStoreActions( + (actions) => actions.removeValueFromAttributesByKeyAction + ) + + const handlePress = (value: string) => { + const isSelected = isValueSelected({ + selectedAttributes, + value: value, + }) + + if (isSelected) { + removeValueFromAttributesByKeyAction({ + key: SearchCriteria.colors, + value: value, + }) + } else { + const newValues = (selectedAttributes || []).concat(value) + setValueToAttributesByKeyAction({ + key: SearchCriteria.colors, + value: newValues, + }) + } + } + + return ( + + + Colour + + + + {COLORS_OPTIONS.map((option, i) => { + const color = COLORS_INDEXED_BY_VALUE[String(option.paramValue)] + + return ( + { + handlePress(option.paramValue as string) + }} + /> + ) + })} + + + ) +} diff --git a/src/app/Scenes/SavedSearchAlert/screens/SavedSearchFilterScreen.tsx b/src/app/Scenes/SavedSearchAlert/screens/SavedSearchFilterScreen.tsx index 9fa67afd738..70f04f711b9 100644 --- a/src/app/Scenes/SavedSearchAlert/screens/SavedSearchFilterScreen.tsx +++ b/src/app/Scenes/SavedSearchAlert/screens/SavedSearchFilterScreen.tsx @@ -3,6 +3,7 @@ import { useNavigation } from "@react-navigation/native" import { SearchCriteria } from "app/Components/ArtworkFilter/SavedSearch/types" import { FancyModalHeader } from "app/Components/FancyModal/FancyModalHeader" import { SavedSearchAppliedFilters } from "app/Scenes/SavedSearchAlert/Components/SavedSearchFilterAppliedFilters" +import { SavedSearchFilterColour } from "app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour" import { SavedSearchRarity } from "app/Scenes/SavedSearchAlert/Components/SavedSearchFilterRarity" import { SavedSearchStore } from "app/Scenes/SavedSearchAlert/SavedSearchStore" import { MotiView } from "moti" @@ -24,6 +25,7 @@ export const SavedSearchFilterScreen: React.FC<{}> = () => { }> + ) From 2d4cce06411cebb6631993d600b4a412d93e29d5 Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 12 Oct 2023 17:26:31 +0200 Subject: [PATCH 30/30] chore: address review comments --- src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx | 2 +- .../Components/SavedSearchFilterColour.tests.tsx | 6 +++--- .../SavedSearchAlert/Components/SavedSearchFilterColour.tsx | 6 ++++-- .../SavedSearchAlert/Components/SavedSearchFilterRarity.tsx | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx b/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx index 3854372e784..9460cc0a361 100644 --- a/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx +++ b/src/app/Components/ArtworkFilter/Filters/ColorsOptions.tsx @@ -38,7 +38,7 @@ export const COLORS_INDEXED_BY_VALUE = COLORS.reduce( {} ) -export const COLORS_OPTIONS: FilterData[] = COLORS.map((color) => { +export const COLOR_OPTIONS: FilterData[] = COLORS.map((color) => { return { // names returned by Metaphysics are actually the slugs displayText: color.name, diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx index 5ff49d64659..0df7844bbf6 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tests.tsx @@ -2,7 +2,7 @@ import { OwnerType } from "@artsy/cohesion" import { fireEvent, waitFor } from "@testing-library/react-native" import { COLORS_INDEXED_BY_VALUE, - COLORS_OPTIONS, + COLOR_OPTIONS, } from "app/Components/ArtworkFilter/Filters/ColorsOptions" import { SavedSearchFilterColour } from "app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour" import { @@ -20,7 +20,7 @@ describe("SavedSearchFilterColour", () => { ) - COLORS_OPTIONS.forEach((option) => { + COLOR_OPTIONS.forEach((option) => { expect(() => getByTestId(`check-icon-${COLORS_INDEXED_BY_VALUE[option.paramValue as string].name}`) ).toThrow() @@ -34,7 +34,7 @@ describe("SavedSearchFilterColour", () => { ) - COLORS_OPTIONS.forEach((option) => { + COLOR_OPTIONS.forEach((option) => { if (option.paramValue !== "red") { expect(() => getByTestId(`check-icon-${COLORS_INDEXED_BY_VALUE[option.paramValue as string].name}`) diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx index 92cc3426d1f..36a8fd5e4d0 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterColour.tsx @@ -1,7 +1,7 @@ import { Flex, Spacer, Text, useScreenDimensions, useSpace } from "@artsy/palette-mobile" import { COLORS_INDEXED_BY_VALUE, - COLORS_OPTIONS, + COLOR_OPTIONS, SWATCHES_PER_ROW, } from "app/Components/ArtworkFilter/Filters/ColorsOptions" import { ColorsSwatch } from "app/Components/ArtworkFilter/Filters/ColorsSwatch" @@ -47,9 +47,11 @@ export const SavedSearchFilterColour = () => { Colour + + - {COLORS_OPTIONS.map((option, i) => { + {COLOR_OPTIONS.map((option, i) => { const color = COLORS_INDEXED_BY_VALUE[String(option.paramValue)] return ( diff --git a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterRarity.tsx b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterRarity.tsx index 2ca8b051a3f..b791a082ba3 100644 --- a/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterRarity.tsx +++ b/src/app/Scenes/SavedSearchAlert/Components/SavedSearchFilterRarity.tsx @@ -42,7 +42,9 @@ export const SavedSearchRarity = () => { Rarity + + {ATTRIBUTION_CLASS_OPTIONS.map((option) => { return (