Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(DEVHUB-1898, UP-6913) Add Hotkey Functionality #506

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/docs/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The `<InputBarTrigger />` component opens a view component (like `<ModalView />`
The `<ModalView />` component renders a chat message feed in a modal window. It accepts the following props:

| Prop | Type | Description | Default |
| -------------------------------- | ------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| -------------------------------- | -------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `className` | `string?` | A custom class name for the view container. Use this to apply custom css styles. | |
| `darkMode` | `boolean?` | If `true`, this renders in dark mode. This overrides any theme or provider `darkMode` setting. | The user's OS preference or theme value of `darkMode`. |
| `disclaimer` | `ReactNode?` | A disclaimer message shown at the top of the message feed. Can include terms of service, etc. | If not specified, no disclaimer is shown. |
Expand All @@ -124,4 +124,5 @@ The `<ModalView />` component renders a chat message feed in a modal window. It
| `initialMessageText` | `string?` | The text content of an initial assistant message at the top of the message feed. | If no text is specified, then no message is shown. |
| `inputBarPlaceholder` | `string?` | The placeholder text shown when the input bar is empty. | If not specified, the input bar uses default placeholders. |
| `inputBottomText` | `string?` | Text that appears immediately below the input bar. | If not specified, no bottom text is shown. |
| `shouldUseHotkey` | `boolean?` | If `true`, the chat window will show on hotkey press (`/`) | `false` |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps rename to 'shouldUseSlashHotkey` to be more specific.

alternatively, could make sense to let the user define what the hotkey is. then user could set it to "/", "s", etc

Copy link
Collaborator

@nlarew nlarew Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for customizable hotkey. I think ideally hotkey stuff would be handled outside of our library, but we can add it as a convenience for this case.

Taking a step back, I think the most composable pattern is to present this as a new type of trigger component, a HotkeyTrigger. You can use this alongside the input bar trigger inside of the chatbot provider.

function MyApp() {
  return (
    <Chatbot>
      <HotkeyTrigger onKey="/" />
      <DevCenterInputBarTrigger />
    </Chatbot>
  )
}

Let me know if you have thoughts / questions on how to implement!

Copy link
Collaborator Author

@max-melo max-melo Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can go ahead and rename it to shouldUseSlashHotkey. I would lean towards this option because the slash in the indicator is hardcoded in leafygreen (with the justification being to keep the usage consistent across different sites' implementations)
edit: @nlarew Sorry didn't see your comment. If we went this route I think there would need to be follow up work in leafygreen, as currently the visual indicator (and focus listener) are hardcoded to /.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's quietly ship shouldUseSlashHotkey as a Chatbot prop and then remove it in favor of a more customizable trigger.

| `windowTitle` | `string?` | The text shown at the top of the chat window. | If not specified, this is the `Chatbot.name`. If that's `undefined` the window has no title. |
146 changes: 103 additions & 43 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/mongodb-chatbot-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"@lg-chat/chat-disclaimer": "^3.0.2",
"@lg-chat/chat-window": "^2.0.1",
"@lg-chat/fixed-chat-window": "^2.0.3",
"@lg-chat/input-bar": "^4.0.3",
"@lg-chat/input-bar": "^5.1.0",
"@lg-chat/leafygreen-chat-provider": "^2.0.0",
"@lg-chat/message": "^4.2.1",
"@lg-chat/message-feed": "^3.0.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/mongodb-chatbot-ui/src/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function ChatWindow(props: ChatWindowProps) {
initialMessageSuggestedPrompts,
inputBarId = "chatbot-input-bar",
inputBottomText,
shouldUseHotkey,
windowTitle,
} = props;

Expand Down Expand Up @@ -191,6 +192,7 @@ export function ChatWindow(props: ChatWindowProps) {
ref={inputBarRef}
disabled={Boolean(conversation.error?.length)}
disableSend={hasError || awaitingReply}
shouldRenderHotkeyIndicator={shouldUseHotkey}
onMessageSend={(messageContent) => {
const canSubmit =
inputTextError.length === 0 && !conversation.error;
Expand Down
1 change: 1 addition & 0 deletions packages/mongodb-chatbot-ui/src/ChatbotView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export type ChatbotViewProps = DarkModeProps & {
inputBarId?: string;
inputBarPlaceholder?: string;
inputBottomText?: string;
shouldUseHotkey?: boolean;
windowTitle?: string;
};
1 change: 1 addition & 0 deletions packages/mongodb-chatbot-ui/src/DevCenterChatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function DevCenterChatbot(props: DevCenterChatbotProps) {
/>
</>
),
shouldUseHotkey: true,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we aren't using this component, but I set this to true here so it would show up in the tester

inputBottomText: mongoDbVerifyInformationMessage,
} satisfies ModalViewProps;

Expand Down
13 changes: 12 additions & 1 deletion packages/mongodb-chatbot-ui/src/ModalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { css, cx } from "@emotion/css";
import { useDarkMode } from "@leafygreen-ui/leafygreen-provider";
import Modal, { ModalProps } from "@leafygreen-ui/modal";
import { palette } from "@leafygreen-ui/palette";
import { useEventListener } from "@leafygreen-ui/hooks";
import { Suspense, lazy } from "react";
import { type StylesProps } from "./utils";
import { type ChatbotViewProps } from "./ChatbotView";
Expand Down Expand Up @@ -52,7 +53,7 @@ export function ModalView(props: ModalViewProps) {
const { darkMode } = useDarkMode(props.darkMode);
const { className, inputBarId, ...chatWindowProps } = props;

const { closeChat, open, conversation } = useChatbotContext();
const { closeChat, open, openChat, conversation } = useChatbotContext();

const shouldClose = () => {
if (props.shouldClose?.() ?? true) {
Expand All @@ -63,6 +64,16 @@ export function ModalView(props: ModalViewProps) {
}
};

useEventListener(
"keydown",
(e: KeyboardEvent) => {
if (!e.repeat && e.key === "/") {
openChat();
}
},
{ enabled: !!props.shouldUseHotkey && !open }
);

const chatWindowInputBarId = inputBarId ?? "chatbot-modal-input-bar";

return (
Expand Down