Skip to content

Commit

Permalink
Move tab state for Search Results into localStorage and out of browse…
Browse files Browse the repository at this point in the history
…r query params
  • Loading branch information
adamjarling committed Jul 2, 2024
1 parent 48361d4 commit bb9b42a
Show file tree
Hide file tree
Showing 19 changed files with 2,784 additions and 1,462 deletions.
9 changes: 4 additions & 5 deletions components/Chat/Chat.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ jest.mock("@/hooks/useChatSocket");
}));

describe("Chat component", () => {
it("renders default ai response placeholder text", () => {
mockRouter.setCurrentUrl("/search?ai=true");
it("renders default placeholder text when no search term is present", () => {
render(<Chat />);

const wrapper = screen.getByText(
Expand All @@ -64,7 +63,7 @@ describe("Chat component", () => {
});

it("renders the chat response component when search term is present", () => {
mockRouter.setCurrentUrl("/search?q=tell+me+about+boats&ai=true");
mockRouter.setCurrentUrl("/search?q=tell+me+about+boats");

render(
<SearchProvider>
Expand Down Expand Up @@ -94,7 +93,7 @@ describe("Chat component", () => {
sendMessage: mockMessage,
}));

mockRouter.setCurrentUrl("/search?q=boats&ai=true");
mockRouter.setCurrentUrl("/search?q=boats");

render(
<SearchProvider>
Expand All @@ -112,7 +111,7 @@ describe("Chat component", () => {
});

it("doesn't send a websocket message if the search term is empty", () => {
mockRouter.setCurrentUrl("/search?ai=true");
mockRouter.setCurrentUrl("/search");
render(
<SearchProvider>
<Chat />
Expand Down
22 changes: 2 additions & 20 deletions components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,6 @@ const Chat = ({ totalResults }: { totalResults?: number }) => {
}
}, [message, searchTerm, sourceDocuments, searchDispatch]);

function handleResultsTab() {
if (window.scrollY === 0) {
searchDispatch({ activeTab: "results", type: "updateActiveTab" });
return;
}

window.scrollTo({ behavior: "instant", top: 0 });

const checkScroll = () => {
if (window.scrollY === 0) {
searchDispatch({ activeTab: "results", type: "updateActiveTab" });
window.removeEventListener("scroll", checkScroll);
}
};

window.addEventListener("scroll", checkScroll);
}

function handleNewQuestion() {
const input = document.getElementById("dc-search") as HTMLInputElement;
if (input) {
Expand All @@ -99,7 +81,7 @@ const Chat = ({ totalResults }: { totalResults?: number }) => {
<Container>
<p>
What can I help you find? Try searching for `john cage scrapbooks` or
`famous speeches`
`who played at the Berkeley Folk Music Festival in 1965?`
</p>
</Container>
);
Expand All @@ -116,7 +98,7 @@ const Chat = ({ totalResults }: { totalResults?: number }) => {
<>
<Container>
<StyledResponseActions>
<Button isPrimary isLowercase onClick={handleResultsTab}>
<Button isPrimary isLowercase>
View {pluralize("Result", totalResults || 0)}
</Button>
<Button isLowercase onClick={handleNewQuestion}>
Expand Down
3 changes: 1 addition & 2 deletions components/Facets/Filter/Submit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const FacetsFilterSubmit: React.FC<FacetsFilterSubmitProps> = ({
const {
query: { q },
} = router;
const { ai, urlFacets } = useQueryParams();
const { urlFacets } = useQueryParams();

const {
filterDispatch,
Expand All @@ -34,7 +34,6 @@ const FacetsFilterSubmit: React.FC<FacetsFilterSubmitProps> = ({
...(q && { q }),
...urlFacets,
...userFacetsUnsubmitted,
...(ai && { ai }),
};

router.push({
Expand Down
6 changes: 4 additions & 2 deletions components/Facets/UserFacets/UserFacets.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { SearchProvider } from "@/context/search-context";
import UserFacets from "./UserFacets";

const searchStateDefault: SearchContextStore = {
activeTab: "results",
aggregations: {},
chat: {
answer: "",
Expand All @@ -19,7 +18,6 @@ const searchStateDefault: SearchContextStore = {
};

const searchState: SearchContextStore = {
activeTab: "results",
aggregations: {},
chat: {
answer: "",
Expand Down Expand Up @@ -87,9 +85,11 @@ describe("UserFacet UI component", () => {
);
const userFacets = await screen.findByTestId(`facet-user-component`);
expect(userFacets).toBeInTheDocument();

const toggle = screen.getByTestId(`facet-user-component-popover-toggle`);
expect(toggle).toBeInTheDocument();
expect(toggle).toHaveTextContent("1");

const content = screen.queryByText(`facet-user-component-popover-content`);
expect(content).toBeNull();
});
Expand All @@ -114,8 +114,10 @@ describe("UserFacet UI component", () => {
</FilterProvider>
</SearchProvider>,
);

const userFacets = screen.getByTestId(`facet-user-component`);
expect(userFacets).toBeInTheDocument();

const values = screen.getAllByTestId(`facet-user-value-component`);
expect(values.length).toBe(3);
});
Expand Down
9 changes: 9 additions & 0 deletions components/Header/Header.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ const User = styled("span", {
},
});

const Logout = styled("button", {
border: "none",
background: "none",
color: "$white",
cursor: "pointer",
paddingLeft: "$gr2",
});

const HeaderStyled = styled("header", {
flexDirection: "column",

Expand Down Expand Up @@ -230,6 +238,7 @@ export type HeaderVariants = VariantProps<typeof HeaderStyled>;

export {
Lockup,
Logout,
Menu,
MenuToggle,
Primary,
Expand Down
19 changes: 9 additions & 10 deletions components/Header/Super.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IconClear, IconMenu } from "../Shared/SVG/Icons";
import {
Logout,
Menu,
MenuToggle,
Super,
Expand All @@ -14,6 +15,7 @@ import { NavResponsiveOnly } from "@/components/Nav/Nav.styled";
import { NorthwesternWordmark } from "@/components/Shared/SVG/Northwestern";
import React from "react";
import { UserContext } from "@/context/user-context";
import useLocalStorage from "@/hooks/useLocalStorage";

const nav = [
{
Expand All @@ -33,6 +35,7 @@ const nav = [
export default function HeaderSuper() {
const [isLoaded, setIsLoaded] = React.useState(false);
const [isExpanded, setIsExpanded] = React.useState(false);
const [ai, setAI] = useLocalStorage("ai", "false");

React.useEffect(() => {
setIsLoaded(true);
Expand All @@ -41,6 +44,11 @@ export default function HeaderSuper() {
const userAuthContext = React.useContext(UserContext);
const handleMenu = () => setIsExpanded(!isExpanded);

const handleLogout = () => {
if (ai === "true") setAI("false");
window.location.href = `${DCAPI_ENDPOINT}/auth/logout`;
};

return (
<Super>
<Container>
Expand Down Expand Up @@ -68,16 +76,7 @@ export default function HeaderSuper() {
{userAuthContext?.user?.isLoggedIn && (
<>
<User>{userAuthContext.user.name}</User>
<a
href={`${DCAPI_ENDPOINT}/auth/logout`}
style={{
cursor: "pointer",
paddingLeft: "8px",
textDecoration: "underline",
}}
>
Logout
</a>
<Logout onClick={handleLogout}>Logout</Logout>
</>
)}
</Nav>
Expand Down
39 changes: 21 additions & 18 deletions components/Search/GenerativeAIToggle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ const defaultUser = {

const withUserProvider = (
Component: React.ReactNode,
user: UserContextType = defaultUser
user: UserContextType = defaultUser,
) => {
return <UserContext.Provider value={user}>{Component}</UserContext.Provider>;
};

const withSearchProvider = (
Component: React.ReactNode,
initialState = defaultSearchState
initialState = defaultSearchState,
) => {
return (
<SearchProvider initialState={initialState}>{Component}</SearchProvider>
Expand All @@ -40,6 +40,9 @@ const withSearchProvider = (
describe("GenerativeAIToggle", () => {
beforeEach(() => {
mockRouter.setCurrentUrl("/");

// Note: localStorage mocked in "jest.setup.js"
localStorage.clear();
});

it("renders the generative AI toggle UI and toggles state for a logged in user", async () => {
Expand All @@ -54,6 +57,7 @@ describe("GenerativeAIToggle", () => {

await user.click(checkbox);
expect(checkbox).toHaveAttribute("data-state", "checked");
expect(localStorage.getItem("ai")).toEqual(JSON.stringify("true"));
});

it("renders the generative AI tooltip", () => {
Expand All @@ -76,54 +80,53 @@ describe("GenerativeAIToggle", () => {
render(
withUserProvider(
withSearchProvider(<GenerativeAIToggle />),
nonLoggedInUser
)
nonLoggedInUser,
),
);

const checkbox = await screen.findByRole("checkbox");
await user.click(checkbox);

const generativeAIDialog = await screen.findByText(
"You must be logged in with a Northwestern NetID to use the Generative AI search feature."
"You must be logged in with a Northwestern NetID to use the Generative AI search feature.",
);

expect(generativeAIDialog).toBeInTheDocument();
});

it("renders a toggled generative ai state when a query param is set and user is logged in", () => {
it("renders a toggled generative ai state when localStorage variable is set and user is logged in", () => {
const activeSearchState = {
...defaultSearchState,
isGenerativeAI: true,
};

mockRouter.setCurrentUrl("/search?ai=true");
localStorage.setItem("ai", JSON.stringify("true"));

mockRouter.setCurrentUrl("/search");
render(
withUserProvider(
withSearchProvider(<GenerativeAIToggle />, activeSearchState)
)
withSearchProvider(<GenerativeAIToggle />, activeSearchState),
),
);

const checkbox = screen.getByRole("checkbox");
expect(checkbox).toHaveAttribute("data-state", "checked");
});

it("sets a query param in the URL when generative AI checkbox is clicked", async () => {
it("updates localStorage ai variable when generative AI checkbox is clicked", async () => {
const user = userEvent.setup();

mockRouter.setCurrentUrl("/");

localStorage.setItem("ai", JSON.stringify("false"));

render(
withUserProvider(
withSearchProvider(<GenerativeAIToggle />, defaultSearchState)
)
withSearchProvider(<GenerativeAIToggle />, defaultSearchState),
),
);

await user.click(screen.getByRole("checkbox"));

expect(mockRouter).toMatchObject({
asPath: "/?ai=true",
pathname: "/",
query: { ai: "true" },
});
expect(localStorage.getItem("ai")).toEqual(JSON.stringify("true"));
});
});
33 changes: 28 additions & 5 deletions components/Search/Search.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import { render, screen } from "@/test-utils";

import Search from "./Search";
import { UserContext } from "@/context/user-context";
import { UserContext as UserContextType } from "@/types/context/user";
import mockRouter from "next-router-mock";
import { renderHook } from "@testing-library/react";
import { useRouter } from "next/router";
import userEvent from "@testing-library/user-event";

const mockIsSearchActive = jest.fn();

const defaultUser = {
user: {
email: "[email protected]",
isLoggedIn: true,
isReadingRoom: false,
name: "Ace Frehley",
sub: "xyz123",
},
};

const withUserProvider = (
Component: React.ReactNode,
user: UserContextType = defaultUser,
) => {
return <UserContext.Provider value={user}>{Component}</UserContext.Provider>;
};

describe("Search component", () => {
beforeEach(() => {
localStorage.clear();
});

it("renders the search ui component", () => {
render(<Search isSearchActive={() => ({})} />);
const wrapper = screen.getByTestId("search-ui-component");
Expand Down Expand Up @@ -71,19 +94,19 @@ describe("Search component", () => {
it("renders standard placeholder text for non AI search", () => {
render(<Search isSearchActive={mockIsSearchActive} />);
const input = screen.getByPlaceholderText(
"Search by keyword or phrase, ex: Berkeley Music Festival"
"Search by keyword or phrase, ex: Berkeley Music Festival",
);
expect(input).toBeInTheDocument();
});

it("renders generative AI placeholder text when AI search is active", () => {
mockRouter.setCurrentUrl("/search?ai=true");
localStorage.setItem("ai", JSON.stringify("true"));

render(withUserProvider(<Search isSearchActive={mockIsSearchActive} />));

render(<Search isSearchActive={mockIsSearchActive} />);
const input = screen.getByPlaceholderText(
"What can I show you from our collections?"
"What can I show you from our collections?",
);

expect(input).toBeInTheDocument();
});
});
Loading

0 comments on commit bb9b42a

Please sign in to comment.