-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement generative ai into search bar component
- Loading branch information
1 parent
e08e522
commit 45d43e5
Showing
19 changed files
with
604 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import * as Tooltip from "@radix-ui/react-tooltip"; | ||
|
||
import { styled } from "@/stitches.config"; | ||
|
||
/* eslint sort-keys: 0 */ | ||
|
||
const GenerativeAIToggleWrapper = styled("div", { | ||
color: "$black50", | ||
fontSize: "$gr2", | ||
display: "flex", | ||
flexShrink: "0", | ||
alignItems: "center", | ||
marginRight: "$gr1", | ||
|
||
"& label": { | ||
cursor: "pointer", | ||
flexShrink: "0", | ||
marginLeft: "3px", | ||
marginRight: "4px", | ||
}, | ||
|
||
"& svg": { | ||
position: "relative", | ||
padding: "0 0 1px", | ||
height: "$gr3", | ||
width: "$gr3", | ||
fill: "$black50", | ||
}, | ||
}); | ||
|
||
const GenerativeAIDialogMessage = styled("p", {}); | ||
|
||
const FlexBody = styled("div", { | ||
display: "flex", | ||
flexDirection: "column", | ||
justifyContent: "space-between", | ||
width: "100%", | ||
}); | ||
|
||
const DialogButtonRow = styled("div", { | ||
display: "flex", | ||
justifyContent: "flex-end", | ||
}); | ||
|
||
const TooltipTrigger = styled(Tooltip.Trigger, { | ||
background: "transparent", | ||
border: "none", | ||
}); | ||
|
||
const TooltipContent = styled(Tooltip.Content, { | ||
zIndex: 2, | ||
}); | ||
|
||
export { | ||
DialogButtonRow, | ||
FlexBody, | ||
GenerativeAIDialogMessage, | ||
GenerativeAIToggleWrapper, | ||
TooltipContent, | ||
TooltipTrigger, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { | ||
SearchProvider, | ||
defaultState as defaultSearchState, | ||
} from "@/context/search-context"; | ||
import { render, screen } from "@testing-library/react"; | ||
|
||
import GenerativeAIToggle from "./GenerativeAIToggle"; | ||
import React from "react"; | ||
import { UserContext } from "@/context/user-context"; | ||
import { UserContext as UserContextType } from "@/types/context/user"; | ||
import mockRouter from "next-router-mock"; | ||
import userEvent from "@testing-library/user-event"; | ||
|
||
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>; | ||
}; | ||
|
||
const withSearchProvider = ( | ||
Component: React.ReactNode, | ||
initialState = defaultSearchState | ||
) => { | ||
return ( | ||
<SearchProvider initialState={initialState}>{Component}</SearchProvider> | ||
); | ||
}; | ||
|
||
describe("GenerativeAIToggle", () => { | ||
it("renders the generative AI toggle UI and toggles state for a logged in user", async () => { | ||
const user = userEvent.setup(); | ||
render( | ||
withUserProvider( | ||
withSearchProvider(<GenerativeAIToggle isSearchActive={true} />) | ||
) | ||
); | ||
|
||
const label = screen.getByLabelText("Use Generative AI"); | ||
const checkbox = screen.getByRole("checkbox"); | ||
|
||
expect(label).toBeInTheDocument(); | ||
expect(checkbox).toHaveAttribute("data-state", "unchecked"); | ||
|
||
await user.click(checkbox); | ||
expect(checkbox).toHaveAttribute("data-state", "checked"); | ||
}); | ||
|
||
it("renders the generative AI tooltip", () => { | ||
render(withSearchProvider(<GenerativeAIToggle isSearchActive={true} />)); | ||
// Target the svg icon itself | ||
const tooltip = screen.getByText("Information Circle"); | ||
|
||
expect(tooltip).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders the generative AI dialog for a non-logged in user", async () => { | ||
const user = userEvent.setup(); | ||
const nonLoggedInUser = { | ||
user: { | ||
...defaultUser.user, | ||
isLoggedIn: false, | ||
}, | ||
}; | ||
|
||
render( | ||
withUserProvider( | ||
withSearchProvider(<GenerativeAIToggle isSearchActive={true} />), | ||
nonLoggedInUser | ||
) | ||
); | ||
|
||
const checkbox = screen.getByRole("checkbox"); | ||
await user.click(checkbox); | ||
|
||
const generativeAIDialog = screen.queryByText( | ||
"You must be logged in with a Northwestern NetID to use the Generative AI search feature." | ||
); | ||
const cancelButton = screen.getByText("Cancel"); | ||
|
||
expect(generativeAIDialog).toBeInTheDocument(); | ||
expect(screen.getByText("Login")).toBeInTheDocument(); | ||
expect(cancelButton).toBeInTheDocument(); | ||
|
||
await user.click(cancelButton); | ||
|
||
expect(generativeAIDialog).not.toBeInTheDocument(); | ||
}); | ||
|
||
it("renders a toggled generative ai state when a query param is set", () => { | ||
const activeSearchState = { | ||
...defaultSearchState, | ||
isGenerativeAI: true, | ||
}; | ||
|
||
mockRouter.setCurrentUrl("/search?ai=true"); | ||
render( | ||
withSearchProvider( | ||
<GenerativeAIToggle isSearchActive={true} />, | ||
activeSearchState | ||
) | ||
); | ||
|
||
const checkbox = screen.getByRole("checkbox"); | ||
expect(checkbox).toHaveAttribute("data-state", "checked"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import * as Tooltip from "@radix-ui/react-tooltip"; | ||
|
||
import { | ||
CheckboxIndicator, | ||
CheckboxRoot as CheckboxRootStyled, | ||
} from "@/components/Shared/Checkbox.styled"; | ||
import { | ||
DialogButtonRow, | ||
FlexBody, | ||
GenerativeAIDialogMessage, | ||
GenerativeAIToggleWrapper, | ||
TooltipContent, | ||
TooltipTrigger, | ||
} from "@/components/Search/GenerativeAI.styled"; | ||
import { TooltipArrow, TooltipBody } from "../Shared/Tooltip.styled"; | ||
|
||
import { Button } from "@nulib/design-system"; | ||
import GenerativeAIDialog from "@/components/Shared/Dialog"; | ||
import { IconCheck } from "@/components/Shared/SVG/Icons"; | ||
import { IconInfo } from "@/components/Shared/SVG/Icons"; | ||
import React from "react"; | ||
import { generativeAIWarning } from "@/components/Chat/components/Answer/Information"; | ||
import useGenerativeAISearchToggle from "@/hooks/useGenerativeAISearchToggle"; | ||
|
||
function GenerativeAITooltip() { | ||
return ( | ||
<Tooltip.Provider delayDuration={20}> | ||
<Tooltip.Root data-testid="tooltip"> | ||
<TooltipTrigger> | ||
<IconInfo /> | ||
</TooltipTrigger> | ||
<Tooltip.Portal> | ||
<TooltipContent side="bottom" sideOffset={3} collisionPadding={19}> | ||
<TooltipArrow /> | ||
<TooltipBody>{generativeAIWarning}</TooltipBody> | ||
</TooltipContent> | ||
</Tooltip.Portal> | ||
</Tooltip.Root> | ||
</Tooltip.Provider> | ||
); | ||
} | ||
|
||
type GenerativeAIToggleProps = { | ||
isSearchActive: boolean; | ||
}; | ||
|
||
export default function GenerativeAIToggle({ | ||
isSearchActive, | ||
}: GenerativeAIToggleProps) { | ||
const { closeDialog, dialog, isChecked, handleCheckChange, handleLogin } = | ||
useGenerativeAISearchToggle(); | ||
|
||
return ( | ||
<> | ||
<GenerativeAIToggleWrapper | ||
{...(isSearchActive ? { css: { marginRight: "$gr5" } } : {})} | ||
> | ||
<CheckboxRootStyled | ||
checked={isChecked} | ||
id="isGenerativeAI" | ||
onCheckedChange={handleCheckChange} | ||
> | ||
<CheckboxIndicator> | ||
<IconCheck /> | ||
</CheckboxIndicator> | ||
</CheckboxRootStyled> | ||
<label htmlFor="isGenerativeAI">Use Generative AI</label> | ||
<GenerativeAITooltip /> | ||
</GenerativeAIToggleWrapper> | ||
|
||
<div style={{ display: "flex" }}> | ||
<GenerativeAIDialog | ||
isOpen={dialog.isOpen} | ||
title={dialog.title} | ||
handleCloseClick={closeDialog} | ||
size="small" | ||
> | ||
<FlexBody> | ||
<GenerativeAIDialogMessage> | ||
You must be logged in with a Northwestern NetID to use the | ||
Generative AI search feature. | ||
</GenerativeAIDialogMessage> | ||
<DialogButtonRow> | ||
<Button isPrimary onClick={handleLogin}> | ||
Login | ||
</Button> | ||
<Button onClick={closeDialog}>Cancel</Button> | ||
</DialogButtonRow> | ||
</FlexBody> | ||
</GenerativeAIDialog> | ||
</div> | ||
</> | ||
); | ||
} |
Oops, something went wrong.