Skip to content

Commit

Permalink
(DOCSP-32694) Dark mode: use LG palette & provider + cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
nlarew committed Sep 5, 2023
1 parent 820229c commit a95f5c5
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 126 deletions.
10 changes: 10 additions & 0 deletions chat-ui/src/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,18 @@ body {
}

.background_dark {
/* palette.black */
background: #001e2b;
}

.controls_container {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
max-width: 450px;
}

.main_content {
max-width: 775px;
min-width: 315px;
Expand All @@ -33,4 +42,5 @@ body {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
}
74 changes: 36 additions & 38 deletions chat-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,65 +24,63 @@ function App() {
<div className={styles.main_content}>
<Chatbot shouldStream={shouldStream} darkMode={darkMode} />
</div>
<StreamingToggle
checked={shouldStream}
toggle={() => setShouldStream((s) => !s)}
/>
<DarkModeToggle
darkMode={darkMode}
toggle={() => setDarkMode(!darkMode)}
/>
<GitCommitLink />
<Controls>
<ToggleControl
checked={shouldStream}
labelId="streaming"
text="Stream Responses"
toggle={() => setShouldStream((s) => !s)}
/>
<ToggleControl
checked={darkMode}
labelId="darkMode"
text="Dark Mode"
toggle={() => setDarkMode(!darkMode)}
/>
<GitCommitLink />
</Controls>
</div>
);
}

function StreamingToggle(props: { checked: boolean; toggle: () => void }) {
const { contextDarkMode: darkMode = false } = useDarkModeContext();
function Controls(props: { children: React.ReactNode }) {
return (
<div className={styles.streaming_toggle}>
<Toggle
darkMode={darkMode}
size="default"
aria-labelledby="streaming-toggle-label"
checked={props.checked}
onChange={() => {
props.toggle();
}}
/>
<Overline
role="label"
id="streaming-toggle-label"
style={{
color: darkMode ? "white" : "black",
}}
>
Stream Responses
</Overline>
<div className={styles.controls_container}>
{props.children}
</div>
);
)
}

function DarkModeToggle(props: { darkMode: boolean; toggle: () => void }) {
type ToggleControlProps = {
checked: boolean;
labelId: string;
text: string;
toggle: () => void;
darkMode?: boolean;
};

function ToggleControl(props: ToggleControlProps) {
const { contextDarkMode: darkMode = props.darkMode ?? false } = useDarkModeContext();
const label = `${props.labelId}-toggle-control-label`;
return (
<div className={styles.streaming_toggle}>
<Toggle
darkMode={props.darkMode}
darkMode={darkMode}
size="default"
aria-labelledby="darkMode-toggle-label"
checked={props.darkMode}
aria-labelledby={label}
checked={props.checked}
onChange={() => {
props.toggle();
}}
/>
<Overline
role="label"
id="streaming-toggle-label"
id={label}
style={{
color: props.darkMode ? "white" : "black",
color: darkMode ? "white" : "black",
}}
>
Dark Mode
{props.text}
</Overline>
</div>
);
Expand Down
170 changes: 87 additions & 83 deletions chat-ui/src/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import { TitleBar } from "@lg-chat/title-bar";
import { Role } from "./services/conversations";
import { palette } from "@leafygreen-ui/palette";
import { css } from "@emotion/css";

interface StylesProps {
darkMode?: boolean;
}
import { type StylesProps } from "./utils";
import LeafyGreenProvider, {
useDarkModeContext,
} from "@leafygreen-ui/leafygreen-provider";

const styles = {
disclosure: ({ darkMode }: StylesProps) => css`
Expand Down Expand Up @@ -75,7 +75,7 @@ const styles = {
& div[role="dialog"] {
padding-top: 8px;
background: ${darkMode ? "#001e2b" : palette.gray.light3};
background: ${darkMode ? palette.black : palette.gray.light3};
}
@media screen and (max-width: 1024px) {
Expand Down Expand Up @@ -188,7 +188,8 @@ export function Chatbot(props: ChatbotProps) {
serverBaseUrl: props.serverBaseUrl,
shouldStream: props.shouldStream,
});
const darkMode = props.darkMode;
const { contextDarkMode = false } = useDarkModeContext();
const darkMode = props.darkMode ?? contextDarkMode;
const [initialInputFocused, setInitialInputFocused] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
Expand Down Expand Up @@ -270,90 +271,93 @@ export function Chatbot(props: ChatbotProps) {
}, [initialInputFocused, promptFocused]);

return (
<div className={styles.chatbot_container}>
<div className={styles.chatbot_input}>
<InputBar
key={"initialInput"}
badgeText="Experimental"
textareaProps={{
value: !modalOpen ? inputText : "",
onChange: (e) => {
if (!modalOpen) setInputText(e.target.value);
},
placeholder: conversation.error
? "Something went wrong. Try reloading the page and starting a new conversation."
: awaitingReply
? "MongoDB AI is answering..."
: "Ask MongoDB AI a Question",
}}
onMessageSend={async (messageContent) => {
const canSubmit =
inputTextError.length === 0 && !conversation.error;
if (canSubmit) {
await handleSubmit(messageContent);
}
}}
onFocus={async () => {
setInitialInputFocused(true);
if (conversation.messages.length > 0) {
openModal();
} else {
setMenuOpen(true);
}
if (!conversation.conversationId) {
await conversation.createConversation();
}
}}
onBlur={() => {
setInitialInputFocused(false);
}}
/>

{inputTextError ? <ErrorText>{inputTextError}</ErrorText> : null}

{showSuggestedPrompts ? (
<InputMenu
className={styles.chatbot_input_menu}
heading="SUGGESTED AI PROMPTS"
headingBadgeText=""
poweredByText="Powered by Atlas Vector Search"
poweredByCTA="Learn More"
poweredByLink="https://www.mongodb.com/products/platform/atlas-vector-search"
prompts={suggestedPrompts}
onFocused={(i) => {
setPromptFocused(i);
<LeafyGreenProvider darkMode={darkMode}>
<div className={styles.chatbot_container}>
<div className={styles.chatbot_input}>
<InputBar
key={"initialInput"}
badgeText="Experimental"
textareaProps={{
value: !modalOpen ? inputText : "",
onChange: (e) => {
if (!modalOpen) setInputText(e.target.value);
},
placeholder: conversation.error
? "Something went wrong. Try reloading the page and starting a new conversation."
: awaitingReply
? "MongoDB AI is answering..."
: "Ask MongoDB AI a Question",
}}
onBlurred={(i) => {
if (i === 0 || i === suggestedPrompts.length) {
setPromptFocused(null);
onMessageSend={async (messageContent) => {
const canSubmit =
inputTextError.length === 0 && !conversation.error;
if (canSubmit) {
await handleSubmit(messageContent);
}
}}
onPromptSelected={({ text }) => {
handleSubmit(text);
onFocus={async () => {
setInitialInputFocused(true);
if (conversation.messages.length > 0) {
openModal();
} else {
setMenuOpen(true);
}
if (!conversation.conversationId) {
await conversation.createConversation();
}
}}
onBlur={() => {
setInitialInputFocused(false);
}}
/>
) : null}

<Disclosure tabIndex={0} darkMode={darkMode} />
{inputTextError ? <ErrorText>{inputTextError}</ErrorText> : null}

{showSuggestedPrompts ? (
<InputMenu
className={styles.chatbot_input_menu}
darkMode={darkMode}
heading="SUGGESTED AI PROMPTS"
headingBadgeText=""
poweredByText="Powered by Atlas Vector Search"
poweredByCTA="Learn More"
poweredByLink="https://www.mongodb.com/products/platform/atlas-vector-search"
prompts={suggestedPrompts}
onFocused={(i) => {
setPromptFocused(i);
}}
onBlurred={(i) => {
if (i === 0 || i === suggestedPrompts.length) {
setPromptFocused(null);
}
}}
onPromptSelected={({ text }) => {
handleSubmit(text);
}}
/>
) : null}

<Disclosure tabIndex={0} darkMode={darkMode} />
</div>
<ChatbotModal
inputBarRef={inputBarRef}
active={modalOpen}
conversation={conversation}
inputText={inputText}
setInputText={setInputText}
inputTextError={inputTextError}
handleSubmit={handleSubmit}
awaitingReply={awaitingReply}
open={modalOpen}
shouldClose={() => {
setMenuOpen(false);
closeModal();
return true;
}}
darkMode={darkMode}
/>
</div>
<ChatbotModal
inputBarRef={inputBarRef}
active={modalOpen}
conversation={conversation}
inputText={inputText}
setInputText={setInputText}
inputTextError={inputTextError}
handleSubmit={handleSubmit}
awaitingReply={awaitingReply}
open={modalOpen}
shouldClose={() => {
setMenuOpen(false);
closeModal();
return true;
}}
darkMode={darkMode}
/>
</div>
</LeafyGreenProvider>
);
}

Expand Down
12 changes: 7 additions & 5 deletions chat-ui/src/InputMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Overline, Body, Link } from "@leafygreen-ui/typography";
import { palette } from "@leafygreen-ui/palette";
import { css, cx } from "@emotion/css";
import { addPeriodIfMissing } from "./utils";
import { type StylesProps } from "./utils";

const styles = {
menu_card: css`
Expand All @@ -24,16 +25,16 @@ const styles = {
align-items: left;
margin-bottom: 0.5rem;
`,
prompt: css`
prompt: ({ darkMode }: StylesProps = {}) => css`
padding: 0.25rem 0.5rem;
border-radius: 6px;
background: #ffffff;
color: #000000;
background: ${darkMode ? palette.black : palette.white};
color: ${darkMode ? palette.white : palette.black};
&:hover,
&:focus {
cursor: pointer;
background: #e1f7ff;
background: ${darkMode ? palette.green.dark3 : palette.blue.light3};
}
`,
powered_by_footer: css`
Expand All @@ -50,6 +51,7 @@ export type MenuPrompt = {
};

type InputMenuProps = {
darkMode?: boolean;
heading?: string;
headingBadgeText?: string;
poweredByText: string;
Expand Down Expand Up @@ -92,7 +94,7 @@ export const InputMenu = forwardRef(function InputMenu(
key={prompt.key}
role="menuitem"
aria-label={prompt.text}
className={styles.prompt}
className={styles.prompt({ darkMode: props.darkMode })}
tabIndex={0}
onMouseDown={(e) => {
// By default, onMouseDown fires a blur event, which
Expand Down
8 changes: 8 additions & 0 deletions chat-ui/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,11 @@ export function addPeriodIfMissing(str: string) {
* @see https://caniuse.com/eventsource
*/
export const canUseServerSentEvents = () => typeof EventSource !== "undefined";

/**
* A shared interface for Emotion CSS tag constructors.
* @see https://emotion.sh/docs/typescript#css-tag
*/
export interface StylesProps {
darkMode?: boolean;
}

0 comments on commit a95f5c5

Please sign in to comment.