-
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.
Add feedback form for chat responses.
- Loading branch information
1 parent
3556f3a
commit 3248e82
Showing
9 changed files
with
376 additions
and
74 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Button } from "@nulib/design-system"; | ||
import ChatFeedbackOptIn from "@/components/Chat/Feedback/OptIn"; | ||
import ChatFeedbackOption from "@/components/Chat/Feedback/Option"; | ||
import ChatFeedbackTextArea from "@/components/Chat/Feedback/TextArea"; | ||
import Container from "@/components/Shared/Container"; | ||
import { styled } from "@/stitches.config"; | ||
import { useState } from "react"; | ||
|
||
const ChatFeedback = () => { | ||
const [isExpanded, setIsExpanded] = useState(false); | ||
const [isSubmitted, setIsSubmitted] = useState(false); | ||
|
||
function handleSubmit() { | ||
console.log("submit feedback"); | ||
setIsSubmitted(true); | ||
} | ||
|
||
return ( | ||
<StyledChatFeedback isSubmitted={isSubmitted}> | ||
<Container> | ||
<StyledChatFeedbackActivate> | ||
<Button isLowercase isText onClick={() => setIsExpanded(true)}> | ||
<span> | ||
Uncertain about this response? Let us know why <strong>➜</strong> | ||
</span> | ||
</Button> | ||
</StyledChatFeedbackActivate> | ||
{isSubmitted ? ( | ||
<StyledChatFeedbackConfirmation> | ||
Solid. Thanks for submitting! | ||
</StyledChatFeedbackConfirmation> | ||
) : ( | ||
<StyledChatFeedbackForm isExpanded={isExpanded}> | ||
<ChatFeedbackOption | ||
name="style" | ||
label="Don't like the response style" | ||
/> | ||
<ChatFeedbackOption | ||
name="factually" | ||
label="Not factually correct" | ||
/> | ||
<ChatFeedbackOption | ||
name="instructions" | ||
label="Didn't fully follow instruction" | ||
/> | ||
<ChatFeedbackOption | ||
name="refused" | ||
label="Refused when it shouldn't have" | ||
/> | ||
<ChatFeedbackOption name="lazy" label="Being lazy" /> | ||
<ChatFeedbackOption name="unsafe" label="Unsafe or problematic" /> | ||
<ChatFeedbackOption name="other" label="Other" /> | ||
<ChatFeedbackTextArea /> | ||
<ChatFeedbackOptIn /> | ||
<Button isLowercase isPrimary onClick={handleSubmit}> | ||
Submit | ||
</Button> | ||
</StyledChatFeedbackForm> | ||
)} | ||
</Container> | ||
</StyledChatFeedback> | ||
); | ||
}; | ||
|
||
/* eslint-disable sort-keys */ | ||
const StyledChatFeedbackActivate = styled("div", { | ||
margin: "0 0 $gr2 ", | ||
|
||
button: { | ||
fontSize: "$gr3", | ||
}, | ||
|
||
strong: { | ||
fontFamily: "$northwesternSansBold", | ||
fontWeight: "400", | ||
fontSize: "$gr3", | ||
}, | ||
}); | ||
|
||
const StyledChatFeedbackConfirmation = styled("div", { | ||
fontSize: "$gr3", | ||
}); | ||
|
||
const StyledChatFeedbackForm = styled("form", { | ||
margin: "$gr3 0", | ||
transition: "200ms all ease-in-out", | ||
width: "61.8%", | ||
|
||
variants: { | ||
isExpanded: { | ||
true: { | ||
opacity: "1", | ||
height: "auto", | ||
}, | ||
false: { | ||
opacity: "0", | ||
height: "0", | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const StyledChatFeedback = styled("div", { | ||
variants: { | ||
isSubmitted: { | ||
true: { | ||
[`& ${StyledChatFeedbackActivate}`]: { | ||
display: "none", | ||
}, | ||
}, | ||
false: {}, | ||
}, | ||
}, | ||
}); | ||
|
||
export default ChatFeedback; |
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,39 @@ | ||
import { render, screen } from "@testing-library/react"; | ||
|
||
import ChatFeedbackOptIn from "@/components/Chat/Feedback/OptIn"; | ||
import React from "react"; | ||
import { UserContext } from "@/context/user-context"; | ||
|
||
const mockUserContextValue = { | ||
user: { | ||
name: "foo", | ||
email: "[email protected]", | ||
sub: "123", | ||
isLoggedIn: true, | ||
isReadingRoom: false, | ||
}, | ||
}; | ||
|
||
describe("ChatFeedbackOptIn", () => { | ||
it("renders a checkbox input with the user email value", () => { | ||
render( | ||
<UserContext.Provider value={mockUserContextValue}> | ||
<ChatFeedbackOptIn /> | ||
</UserContext.Provider> | ||
); | ||
|
||
const checkbox = screen.getByRole("checkbox"); | ||
expect(checkbox).toHaveAttribute("value", "[email protected]"); | ||
expect(checkbox).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders a label", () => { | ||
render( | ||
<UserContext.Provider value={mockUserContextValue}> | ||
<ChatFeedbackOptIn /> | ||
</UserContext.Provider> | ||
); | ||
const label = screen.getByText(/please follow up with me/i); | ||
expect(label).toBeInTheDocument(); | ||
}); | ||
}); |
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,23 @@ | ||
import { UserContext } from "@/context/user-context"; | ||
import { styled } from "@/stitches.config"; | ||
import { useContext } from "react"; | ||
|
||
const ChatFeedbackOptIn = () => { | ||
const { user } = useContext(UserContext); | ||
|
||
return ( | ||
<StyledChatFeedbackOptIn> | ||
<input name="email" type="checkbox" value={user?.email} /> Please follow | ||
up with me regarding this issue. | ||
</StyledChatFeedbackOptIn> | ||
); | ||
}; | ||
|
||
/* eslint-disable sort-keys */ | ||
const StyledChatFeedbackOptIn = styled("label", { | ||
display: "block", | ||
margin: "$gr3 0", | ||
fontSize: "$gr2", | ||
}); | ||
|
||
export default ChatFeedbackOptIn; |
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,21 @@ | ||
// test ChatFeedbackOption.tsx | ||
|
||
import { render, screen } from "@testing-library/react"; | ||
|
||
import ChatFeedbackOption from "@/components/Chat/Feedback/Option"; | ||
|
||
describe("ChatFeedbackOption", () => { | ||
it("renders a checkbox input", () => { | ||
render(<ChatFeedbackOption name="test" label="This is a test." />); | ||
const checkbox = screen.getByTestId("chat-feedback-option-test"); | ||
expect(checkbox).toHaveAttribute("aria-checked", "false"); | ||
expect(checkbox).toHaveAttribute("tabindex", "0"); | ||
expect(checkbox).toBeInTheDocument(); | ||
}); | ||
|
||
it("renders a label", () => { | ||
render(<ChatFeedbackOption name="test" label="This is a test." />); | ||
const label = screen.getByText(/this is a test/i); | ||
expect(label).toBeInTheDocument(); | ||
}); | ||
}); |
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,88 @@ | ||
import { useRef, useState } from "react"; | ||
|
||
import { styled } from "@/stitches.config"; | ||
|
||
const ChatFeedbackOption = ({ | ||
name, | ||
label, | ||
}: { | ||
name: string; | ||
label: string; | ||
}) => { | ||
const [isChecked, setIsChecked] = useState(false); | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
|
||
function handleOnChange() { | ||
setIsChecked(inputRef?.current?.checked ?? false); | ||
} | ||
|
||
const handleKeyDown = (event: React.KeyboardEvent<HTMLLabelElement>) => { | ||
if (event.key === " ") { | ||
event.preventDefault(); | ||
setIsChecked(!isChecked); | ||
} | ||
}; | ||
|
||
const handleClick = () => { | ||
setIsChecked(!isChecked); | ||
}; | ||
|
||
return ( | ||
<StyledChatFeedbackOption | ||
aria-checked={isChecked} | ||
data-testid={`chat-feedback-option-${name}`} | ||
isChecked={isChecked} | ||
onClick={handleClick} | ||
onKeyDown={handleKeyDown} | ||
role="checkbox" | ||
tabIndex={0} | ||
> | ||
<input | ||
name={name} | ||
id={`chat-feedback-option-${name}`} | ||
onChange={handleOnChange} | ||
ref={inputRef} | ||
type="checkbox" | ||
/> | ||
{label} | ||
</StyledChatFeedbackOption> | ||
); | ||
}; | ||
|
||
/* eslint-disable sort-keys */ | ||
const StyledChatFeedbackOption = styled("label", { | ||
display: "inline-flex", | ||
alignItems: "center", | ||
fontSize: "$gr2", | ||
margin: "0 $gr1 $gr1 0", | ||
borderRadius: "1rem", | ||
cursor: "pointer", | ||
transition: "$dcAll", | ||
padding: "$gr1 $gr2", | ||
gap: "3px", | ||
|
||
"&:hover": { | ||
boxShadow: "3px 3px 8px #0002", | ||
}, | ||
|
||
input: { | ||
display: "none", | ||
}, | ||
|
||
variants: { | ||
isChecked: { | ||
true: { | ||
color: "$white", | ||
border: "1px solid $black80", | ||
backgroundColor: "$black80", | ||
}, | ||
false: { | ||
color: "$black50", | ||
border: "1px solid $black20", | ||
backgroundColor: "$white", | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export default ChatFeedbackOption; |
Oops, something went wrong.