From a95f5c515b853f1fabf4e85f82f4c6b8c82df162 Mon Sep 17 00:00:00 2001 From: Nick Larew Date: Tue, 5 Sep 2023 15:42:26 -0400 Subject: [PATCH] (DOCSP-32694) Dark mode: use LG palette & provider + cleanup --- chat-ui/src/App.module.css | 10 +++ chat-ui/src/App.tsx | 74 ++++++++-------- chat-ui/src/Chatbot.tsx | 170 +++++++++++++++++++------------------ chat-ui/src/InputMenu.tsx | 12 +-- chat-ui/src/utils.ts | 8 ++ 5 files changed, 148 insertions(+), 126 deletions(-) diff --git a/chat-ui/src/App.module.css b/chat-ui/src/App.module.css index f0d603c47..9110040db 100644 --- a/chat-ui/src/App.module.css +++ b/chat-ui/src/App.module.css @@ -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; @@ -33,4 +42,5 @@ body { display: flex; align-items: center; gap: 0.5rem; + width: 100%; } diff --git a/chat-ui/src/App.tsx b/chat-ui/src/App.tsx index 55bda7d53..40a521e37 100644 --- a/chat-ui/src/App.tsx +++ b/chat-ui/src/App.tsx @@ -24,65 +24,63 @@ function App() {
- setShouldStream((s) => !s)} - /> - setDarkMode(!darkMode)} - /> - + + setShouldStream((s) => !s)} + /> + setDarkMode(!darkMode)} + /> + + ); } -function StreamingToggle(props: { checked: boolean; toggle: () => void }) { - const { contextDarkMode: darkMode = false } = useDarkModeContext(); +function Controls(props: { children: React.ReactNode }) { return ( -
- { - props.toggle(); - }} - /> - - Stream Responses - +
+ {props.children}
- ); + ) } -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 (
{ props.toggle(); }} /> - Dark Mode + {props.text}
); diff --git a/chat-ui/src/Chatbot.tsx b/chat-ui/src/Chatbot.tsx index 576cc5812..b219832c6 100644 --- a/chat-ui/src/Chatbot.tsx +++ b/chat-ui/src/Chatbot.tsx @@ -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` @@ -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) { @@ -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); @@ -270,90 +271,93 @@ export function Chatbot(props: ChatbotProps) { }, [initialInputFocused, promptFocused]); return ( -
-
- { - 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 ? {inputTextError} : null} - - {showSuggestedPrompts ? ( - { - setPromptFocused(i); + +
+
+ { + 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} - + {inputTextError ? {inputTextError} : null} + + {showSuggestedPrompts ? ( + { + setPromptFocused(i); + }} + onBlurred={(i) => { + if (i === 0 || i === suggestedPrompts.length) { + setPromptFocused(null); + } + }} + onPromptSelected={({ text }) => { + handleSubmit(text); + }} + /> + ) : null} + + +
+ { + setMenuOpen(false); + closeModal(); + return true; + }} + darkMode={darkMode} + />
- { - setMenuOpen(false); - closeModal(); - return true; - }} - darkMode={darkMode} - /> -
+ ); } diff --git a/chat-ui/src/InputMenu.tsx b/chat-ui/src/InputMenu.tsx index 337b9e8b6..e14bd6e38 100644 --- a/chat-ui/src/InputMenu.tsx +++ b/chat-ui/src/InputMenu.tsx @@ -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` @@ -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` @@ -50,6 +51,7 @@ export type MenuPrompt = { }; type InputMenuProps = { + darkMode?: boolean; heading?: string; headingBadgeText?: string; poweredByText: string; @@ -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 diff --git a/chat-ui/src/utils.ts b/chat-ui/src/utils.ts index 98b82f256..3e216d6a0 100644 --- a/chat-ui/src/utils.ts +++ b/chat-ui/src/utils.ts @@ -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; +}