Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to hide specific artworks from search results [WEB-2804] #38

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
867 changes: 442 additions & 425 deletions dist/MyMuseumTourBuilder.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
null,
React.createElement(MyMuseumTourBuilder, {
apiSaveEndpoint: "http://www-dev.artic.edu/api/v1/my-museum-tour",
hideFromTours: []
}),
),
document.querySelector("#root"),
Expand Down
10 changes: 8 additions & 2 deletions src/MyMuseumTourBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const MyMuseumTourBuilder = (props) => {
// Mainly used for testing, but could be used for hydrating the app
const {
apiSaveEndpoint,
hideFromTours,
tourTitle,
tourDescription,
tourItems,
Expand All @@ -35,6 +36,10 @@ const MyMuseumTourBuilder = (props) => {
iiifBaseUrl,
};

const SearchProps = {
hideFromTours,
};

return (
<div id="my-museum-tour-builder" className="my-museum-tour">
<AppProvider {...AppProviderProps}>
Expand All @@ -55,8 +60,8 @@ const MyMuseumTourBuilder = (props) => {
</div>
<SearchProvider>
<div className="aic-ct__core">
<SearchBar />
<Themes />
<SearchBar {...SearchProps} />
<Themes {...SearchProps} />
<SearchResults />
</div>
</SearchProvider>
Expand Down Expand Up @@ -127,6 +132,7 @@ const MyMuseumTourBuilder = (props) => {

MyMuseumTourBuilder.propTypes = {
apiSaveEndpoint: PropTypes.string,
hideFromTours: PropTypes.array,
tourTitle: PropTypes.string,
tourDescription: PropTypes.string,
tourItems: PropTypes.array,
Expand Down
24 changes: 24 additions & 0 deletions src/components/search/SearchBar.cy.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,28 @@ describe("<SearchBar />", () => {
cy.get("#aic-ct-search__button").click();
cy.get("@search").its("request.url").should("include", "q=test");
});

it("Hides artwork from search", () => {
cy.intercept("GET", "https://api.artic.edu/api/v1/artworks/search*", {
fixture: "json/search.json",
delayMs: 80,
}).as("search");

cy.mount(
<AppProvider>
<SearchProvider>
<SearchBar hideFromTours={["111111"]} />
</SearchProvider>
</AppProvider>,
);
cy.get("#aic-ct-search__input").type("test");
cy.get("#aic-ct-search__button").click();
cy.get("@search").its("request.url").should("include", "q=test");
cy.get("@search")
.its("request.url")
.should(
"include",
"&query%5Bbool%5D%5Bmust_not%5D%5B%5D%5Bterm%5D%5Bid%5D%5Bvalue%5D%3D111111",
);
});
});
15 changes: 11 additions & 4 deletions src/components/search/SearchBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import useFetch from "../../hooks/useFetch";
import { createSearchUrl } from "../../utils";
import classNames from "classnames";
import PropTypes from "prop-types";

/**
* SearchBar
*/
function SearchBar() {
function SearchBar(props) {
const {
searchQuery,
setSearchQuery,
Expand All @@ -26,8 +27,10 @@
errorSetter: setSearchError,
});

const { hideFromTours } = props;

const handleSubmit = (event) => {
fetchData(createSearchUrl({ keywords: searchQuery }));
fetchData(createSearchUrl({ keywords: searchQuery }, hideFromTours));
// Deselect any active theme
setActiveTheme(null);
event.preventDefault();
Expand All @@ -45,9 +48,9 @@
useEffect(() => {
if (initialRender) {
setInitialRender(false);
fetchData(createSearchUrl({ keywords: "" }));
fetchData(createSearchUrl({ keywords: "" }, hideFromTours));
}
}, [fetchData, initialRender, setInitialRender]);

Check warning on line 53 in src/components/search/SearchBar.jsx

View workflow job for this annotation

GitHub Actions / Build

React Hook useEffect has a missing dependency: 'hideFromTours'. Either include it or remove the dependency array

return (
<form
Expand Down Expand Up @@ -94,7 +97,7 @@
setSearchResultItems(null);
setActiveTheme(null);
// Apply results for empty keyword search
fetchData(createSearchUrl({ keywords: "" }));
fetchData(createSearchUrl({ keywords: "" }, hideFromTours));
searchButtonRef.current.focus();
}}
>
Expand All @@ -107,4 +110,8 @@
);
}

SearchBar.propTypes = {
hideFromTours: PropTypes.array,
};

export default SearchBar;
7 changes: 4 additions & 3 deletions src/components/search/ThemeToggle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import PropTypes from "prop-types";
* ThemeToggle
*/
function ThemeToggle(props) {
const { id, label, thumbnailId, searchParams } = props;
const { id, label, thumbnailId, searchParams, hideFromTours } = props;
const { iiifBaseUrl } = useContext(AppContext);
const {
setSearchResultItems,
Expand All @@ -32,9 +32,9 @@ function ThemeToggle(props) {
// Deselect the theme
setActiveTheme(null);
// Apply results for empty keyword search
fetchData(createSearchUrl({ keywords: "" }));
fetchData(createSearchUrl({ keywords: "" }, hideFromTours));
} else {
fetchData(createSearchUrl(searchParams));
fetchData(createSearchUrl(searchParams, hideFromTours));
// Clicking while active removes the theme
setActiveTheme(label);
// Empty the search field
Expand Down Expand Up @@ -85,6 +85,7 @@ ThemeToggle.propTypes = {
label: PropTypes.string.isRequired,
thumbnailId: PropTypes.string.isRequired,
searchParams: PropTypes.object.isRequired,
hideFromTours: PropTypes.array,
};

export default ThemeToggle;
10 changes: 9 additions & 1 deletion src/components/search/Themes.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from "react";
import ThemeToggle from "./ThemeToggle";
import PropTypes from "prop-types";

/**
* Themes
*/
function Themes() {
function Themes(props) {
const { hideFromTours } = props;

// TODO: There's interest in having this in some way configurable
// Best way to achieve this is TBD
const themes = [
Expand Down Expand Up @@ -82,10 +85,15 @@ function Themes() {
label={theme.label}
thumbnailId={theme.thumbnailId}
searchParams={theme.searchParams}
hideFromTours={hideFromTours}
/>
))}
</ul>
);
}

Themes.propTypes = {
hideFromTours: PropTypes.array,
};

export default Themes;
27 changes: 27 additions & 0 deletions src/components/search/ThemesToggle.cy.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,31 @@ describe("<Themes />", () => {
"&query%5Bbool%5D%5Bmust%5D%5B%5D%5Bterms%5D%5Bcategory_ids%5D%5B%5D=PC-154",
);
});

it("Requests the correct URL with hiding artworks", () => {
cy.intercept("GET", "https://api.artic.edu/api/v1/artworks/search*", {
fixture: "json/search.json",
delayMs: 80,
}).as("search");

cy.mount(
<AppProvider>
<SearchProvider>
<ThemeToggle
id="0"
label="Test theme"
searchParams={{ category_ids: ["PC-154"] }}
hideFromTours={["111111"]}
/>
</SearchProvider>
</AppProvider>,
);
cy.get("#aic-ct-theme-toggle-0").click();
cy.get("@search")
.its("request.url")
.should(
"include",
"&query%5Bbool%5D%5Bmust_not%5D%5B%5D%5Bterm%5D%5Bid%5D%5Bvalue%5D%3D111111",
);
});
});
11 changes: 10 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function camelToSnakeCase(str) {
* @param {QueryParams} queryParams - QueryParams object with keywords and/or themes
* @returns {URL} - URL object for API query
*/
export function createSearchUrl(queryParams) {
export function createSearchUrl(queryParams, hideFromTours) {
// Build the query string
// I've broken this up to make it easer to reason about and manipulate
const url = new URL("https://api.artic.edu/api/v1/artworks/search");
Expand Down Expand Up @@ -91,6 +91,15 @@ export function createSearchUrl(queryParams) {
url.searchParams.set("q", queryParams.keywords);
}

if (hideFromTours) {
for (const val of Object.values(hideFromTours)) {
url.searchParams.set(
`query[bool][must_not][][term][id][value]=${val}`,
val,
);
}
}

// Uniform treatment for subject, category, and style ids
// This works for now, but we may need to tune this later
for (const [key, value] of Object.entries(queryParams)) {
Expand Down
Loading