From ce1fdcd37c2ec087411103a0ca45ac00eb9cfb54 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 3 Apr 2025 13:41:49 -0400 Subject: [PATCH 1/3] feat(ChatBot): Add info density adjustments --- .../chatbot/examples/demos/Chatbot.md | 9 +- .../chatbot/examples/demos/ChatbotCompact.tsx | 480 ++++++++++++++++++ .../chatbot/overview/demo/compact-chatbot.png | Bin 0 -> 40088 bytes packages/module/src/Chatbot/Chatbot.test.tsx | 9 + .../src/ChatbotFooter/ChatbotFooter.scss | 5 + .../src/ChatbotFooter/ChatbotFooter.test.tsx | 10 + .../src/ChatbotFooter/ChatbotFooter.tsx | 4 +- .../src/ChatbotHeader/ChatbotHeader.scss | 17 + .../ChatbotHeader/ChatbotHeaderMenu.test.tsx | 8 + .../src/ChatbotHeader/ChatbotHeaderMenu.tsx | 15 +- .../ChatbotHeaderOptionsDropdown.test.tsx | 6 + .../ChatbotHeaderOptionsDropdown.tsx | 7 +- .../ChatbotHeaderSelectorDropdown.test.tsx | 10 + .../ChatbotHeaderSelectorDropdown.tsx | 4 + .../src/ChatbotPopover/ChatbotPopover.scss | 14 +- .../src/ChatbotPopover/ChatbotPopover.tsx | 2 +- .../ChatbotWelcomePrompt.scss | 10 +- .../ChatbotWelcomePrompt.test.tsx | 13 + .../ChatbotWelcomePrompt.tsx | 15 +- .../ChatbotWelcomePrompt.test.tsx.snap | 2 +- packages/module/src/Message/Message.scss | 27 + .../src/Message/TextMessage/TextMessage.scss | 25 + .../module/src/MessageBar/AttachButton.scss | 10 + .../src/MessageBar/AttachButton.test.tsx | 4 + .../module/src/MessageBar/AttachButton.tsx | 7 +- .../module/src/MessageBar/MessageBar.scss | 17 + packages/module/src/MessageBar/MessageBar.tsx | 21 +- .../src/MessageBar/MicrophoneButton.scss | 10 + .../src/MessageBar/MicrophoneButton.tsx | 7 +- .../module/src/MessageBar/SendButton.scss | 10 + .../module/src/MessageBar/SendButton.test.tsx | 6 +- packages/module/src/MessageBar/SendButton.tsx | 7 +- .../module/src/MessageBar/StopButton.scss | 10 + .../module/src/MessageBar/StopButton.test.tsx | 6 +- packages/module/src/MessageBar/StopButton.tsx | 7 +- .../module/src/MessageBox/MessageBox.scss | 6 + 36 files changed, 785 insertions(+), 35 deletions(-) create mode 100644 packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx create mode 100644 packages/module/patternfly-docs/generated/patternfly-ai/chatbot/overview/demo/compact-chatbot.png diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md index 21dc1625c..73a6789bf 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md @@ -59,7 +59,6 @@ import userAvatar from '../Messages/user_avatar.svg'; import patternflyAvatar from '../Messages/patternfly_avatar.jpg'; import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking"; - ### Basic ChatBot This demo displays a basic ChatBot, which includes: @@ -87,6 +86,14 @@ This demo displays a basic ChatBot, which includes: ``` +### Compact ChatBot + +This demo displays a basic compact ChatBot + +```js file="./ChatbotCompact.tsx" isFullscreen + +``` + ### Embedded ChatBot This demo displays an embedded ChatBot. Embedded ChatBots are meant to be placed within a page in your product. This demo includes: diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx new file mode 100644 index 000000000..5cc011c93 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx @@ -0,0 +1,480 @@ +import React from 'react'; + +import { Bullseye, Brand, DropdownList, DropdownItem, DropdownGroup, SkipToContent } from '@patternfly/react-core'; + +import ChatbotToggle from '@patternfly/chatbot/dist/dynamic/ChatbotToggle'; +import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot'; +import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent'; +import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt'; +import ChatbotFooter, { ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic/ChatbotFooter'; +import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar'; +import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox'; +import Message, { MessageProps } from '@patternfly/chatbot/dist/dynamic/Message'; +import ChatbotConversationHistoryNav, { + Conversation +} from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav'; +import ChatbotHeader, { + ChatbotHeaderMenu, + ChatbotHeaderMain, + ChatbotHeaderTitle, + ChatbotHeaderActions, + ChatbotHeaderSelectorDropdown, + ChatbotHeaderOptionsDropdown +} from '@patternfly/chatbot/dist/dynamic/ChatbotHeader'; + +import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon'; +import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-drawer-right-icon'; +import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon'; + +import PFHorizontalLogoColor from '../UI/PF-HorizontalLogo-Color.svg'; +import PFHorizontalLogoReverse from '../UI/PF-HorizontalLogo-Reverse.svg'; +import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg'; +import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg'; +import userAvatar from '../Messages/user_avatar.svg'; +import patternflyAvatar from '../Messages/patternfly_avatar.jpg'; + +const footnoteProps = { + label: 'ChatBot uses AI. Check for mistakes.', + popover: { + title: 'Verify information', + description: `While ChatBot strives for accuracy, AI is experimental and can make mistakes. We cannot guarantee that all information provided by ChatBot is up to date or without error. You should always verify responses using reliable sources, especially for crucial information and decision making.`, + bannerImage: { + src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif', + alt: 'Example image for footnote popover' + }, + cta: { + label: 'Dismiss', + onClick: () => { + alert('Do something!'); + } + }, + link: { + label: 'View AI policy', + url: 'https://www.redhat.com/' + } + } +}; + +const markdown = `A paragraph with *emphasis* and **strong importance**. + +> A block quote with ~strikethrough~ and a URL: https://reactjs.org. + +Here is an inline code - \`() => void\` + +Here is some YAML code: + +~~~yaml +apiVersion: helm.openshift.io/v1beta1/ +kind: HelmChartRepository +metadata: + name: azure-sample-repo0oooo00ooo +spec: + connectionConfig: + url: https://raw.githubusercontent.com/Azure-Samples/helm-charts/master/docs +~~~ + +Here is some JavaScript code: + +~~~js +import React from 'react'; + +const MessageLoading = () => ( +
+ + Loading message + +
+); + +export default MessageLoading; + +~~~ +`; + +// It's important to set a date and timestamp prop since the Message components re-render. +// The timestamps re-render with them. +const date = new Date(); + +const initialMessages: MessageProps[] = [ + { + id: '1', + role: 'user', + content: 'Hello, can you give me an example of what you can do?', + name: 'User', + avatar: userAvatar, + timestamp: date.toLocaleString(), + avatarProps: { isBordered: true } + }, + { + id: '2', + role: 'bot', + content: markdown, + name: 'Bot', + avatar: patternflyAvatar, + timestamp: date.toLocaleString(), + actions: { + // eslint-disable-next-line no-console + positive: { onClick: () => console.log('Good response') }, + // eslint-disable-next-line no-console + negative: { onClick: () => console.log('Bad response') }, + // eslint-disable-next-line no-console + copy: { onClick: () => console.log('Copy') }, + // eslint-disable-next-line no-console + share: { onClick: () => console.log('Share') }, + // eslint-disable-next-line no-console + listen: { onClick: () => console.log('Listen') } + } + } +]; + +const welcomePrompts = [ + { + title: 'Set up account', + message: 'Choose the necessary settings and preferences for your account.' + }, + { + title: 'Troubleshoot issue', + message: 'Find documentation and instructions to resolve your issue.' + } +]; + +const initialConversations = { + Today: [{ id: '1', text: 'Hello, can you give me an example of what you can do?' }], + 'This month': [ + { + id: '2', + text: 'Enterprise Linux installation and setup' + }, + { id: '3', text: 'Troubleshoot system crash' } + ], + March: [ + { id: '4', text: 'Ansible security and updates' }, + { id: '5', text: 'Red Hat certification' }, + { id: '6', text: 'Lightspeed user documentation' } + ], + February: [ + { id: '7', text: 'Crashing pod assistance' }, + { id: '8', text: 'OpenShift AI pipelines' }, + { id: '9', text: 'Updating subscription plan' }, + { id: '10', text: 'Red Hat licensing options' } + ], + January: [ + { id: '11', text: 'RHEL system performance' }, + { id: '12', text: 'Manage user accounts' } + ] +}; + +export const ChatbotDemo: React.FunctionComponent = () => { + const [chatbotVisible, setChatbotVisible] = React.useState(true); + const [displayMode, setDisplayMode] = React.useState(ChatbotDisplayMode.default); + const [messages, setMessages] = React.useState(initialMessages); + const [selectedModel, setSelectedModel] = React.useState('Granite 7B'); + const [isSendButtonDisabled, setIsSendButtonDisabled] = React.useState(false); + const [isDrawerOpen, setIsDrawerOpen] = React.useState(false); + const [conversations, setConversations] = React.useState( + initialConversations + ); + const [announcement, setAnnouncement] = React.useState(); + const scrollToBottomRef = React.useRef(null); + const toggleRef = React.useRef(null); + const chatbotRef = React.useRef(null); + const historyRef = React.useRef(null); + + // Auto-scrolls to the latest message + React.useEffect(() => { + // don't scroll the first load - in this demo, we know we start with two messages + if (messages.length > 2) { + scrollToBottomRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + }, [messages]); + + const onSelectModel = ( + _event: React.MouseEvent | undefined, + value: string | number | undefined + ) => { + setSelectedModel(value as string); + }; + + const onSelectDisplayMode = ( + _event: React.MouseEvent | undefined, + value: string | number | undefined + ) => { + setDisplayMode(value as ChatbotDisplayMode); + }; + + // you will likely want to come up with your own unique id function; this is for demo purposes only + const generateId = () => { + const id = Date.now() + Math.random(); + return id.toString(); + }; + + const handleSend = (message: string) => { + setIsSendButtonDisabled(true); + const newMessages: MessageProps[] = []; + // We can't use structuredClone since messages contains functions, but we can't mutate + // items that are going into state or the UI won't update correctly + messages.forEach((message) => newMessages.push(message)); + // It's important to set a timestamp prop since the Message components re-render. + // The timestamps re-render with them. + const date = new Date(); + newMessages.push({ + id: generateId(), + role: 'user', + content: message, + name: 'User', + avatar: userAvatar, + timestamp: date.toLocaleString(), + avatarProps: { isBordered: true } + }); + newMessages.push({ + id: generateId(), + role: 'bot', + content: 'API response goes here', + name: 'Bot', + isLoading: true, + avatar: patternflyAvatar, + timestamp: date.toLocaleString() + }); + setMessages(newMessages); + // make announcement to assistive devices that new messages have been added + setAnnouncement(`Message from User: ${message}. Message from Bot is loading.`); + + // this is for demo purposes only; in a real situation, there would be an API response we would wait for + setTimeout(() => { + const loadedMessages: MessageProps[] = []; + // We can't use structuredClone since messages contains functions, but we can't mutate + // items that are going into state or the UI won't update correctly + newMessages.forEach((message) => loadedMessages.push(message)); + loadedMessages.pop(); + loadedMessages.push({ + id: generateId(), + role: 'bot', + content: 'API response goes here', + name: 'Bot', + isLoading: false, + avatar: patternflyAvatar, + timestamp: date.toLocaleString(), + actions: { + // eslint-disable-next-line no-console + positive: { onClick: () => console.log('Good response') }, + // eslint-disable-next-line no-console + negative: { onClick: () => console.log('Bad response') }, + // eslint-disable-next-line no-console + copy: { onClick: () => console.log('Copy') }, + // eslint-disable-next-line no-console + share: { onClick: () => console.log('Share') }, + // eslint-disable-next-line no-console + listen: { onClick: () => console.log('Listen') } + } + }); + setMessages(loadedMessages); + // make announcement to assistive devices that new message has loaded + setAnnouncement(`Message from Bot: API response goes here`); + setIsSendButtonDisabled(false); + }, 5000); + }; + + const findMatchingItems = (targetValue: string) => { + let filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => { + const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase())); + if (filteredItems.length > 0) { + acc[key] = filteredItems; + } + return acc; + }, {}); + + // append message if no items are found + if (Object.keys(filteredConversations).length === 0) { + filteredConversations = [{ id: '13', noIcon: true, text: 'No results found' }]; + } + return filteredConversations; + }; + + const horizontalLogo = ( + + + + + ); + + const iconLogo = ( + <> + + + + ); + + const handleSkipToContent = (e) => { + e.preventDefault(); + /* eslint-disable indent */ + switch (displayMode) { + case ChatbotDisplayMode.default: + if (!chatbotVisible && toggleRef.current) { + toggleRef.current.focus(); + } + if (chatbotVisible && chatbotRef.current) { + chatbotRef.current.focus(); + } + break; + + case ChatbotDisplayMode.docked: + if (chatbotRef.current) { + chatbotRef.current.focus(); + } + break; + default: + if (historyRef.current) { + historyRef.current.focus(); + } + break; + } + /* eslint-enable indent */ + }; + + return ( + <> + + Skip to chatbot + + + + { + setIsDrawerOpen(!isDrawerOpen); + setConversations(initialConversations); + }} + isDrawerOpen={isDrawerOpen} + setIsDrawerOpen={setIsDrawerOpen} + activeItemId="1" + // eslint-disable-next-line no-console + onSelectActiveItem={(e, selectedItem) => console.log(`Selected history item with id ${selectedItem}`)} + conversations={conversations} + onNewChat={() => { + setIsDrawerOpen(!isDrawerOpen); + setMessages([]); + setConversations(initialConversations); + }} + handleTextInputChange={(value: string) => { + if (value === '') { + setConversations(initialConversations); + } + // this is where you would perform search on the items in the drawer + // and update the state + const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value); + setConversations(newConversations); + }} + drawerContent={ + <> + + + setIsDrawerOpen(!isDrawerOpen)} + isCompact + /> + + + + + + + Granite 7B + + + Llama 3.0 + + + Mistral 3B + + + + + + + } + isSelected={displayMode === ChatbotDisplayMode.default} + > + Overlay + + } + isSelected={displayMode === ChatbotDisplayMode.docked} + > + Dock to window + + } + isSelected={displayMode === ChatbotDisplayMode.fullscreen} + > + Fullscreen + + + + + + + + {/* Update the announcement prop on MessageBox whenever a new message is sent + so that users of assistive devices receive sufficient context */} + + + {/* This code block enables scrolling to the top of the last message. + You can instead choose to move the div with scrollToBottomRef on it below + the map of messages, so that users are forced to scroll to the bottom. + If you are using streaming, you will want to take a different approach; + see: https://github.com/patternfly/chatbot/issues/201#issuecomment-2400725173 */} + {messages.map((message, index) => { + if (index === messages.length - 1) { + return ( + <> +
+ + + ); + } + return ; + })} +
+
+ + + + + + } + >
+
+ + ); +}; diff --git a/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/overview/demo/compact-chatbot.png b/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/overview/demo/compact-chatbot.png new file mode 100644 index 0000000000000000000000000000000000000000..2a154c0b6bc93d38a0fa518584b001afde4e06d0 GIT binary patch literal 40088 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 C26_1a literal 0 HcmV?d00001 diff --git a/packages/module/src/Chatbot/Chatbot.test.tsx b/packages/module/src/Chatbot/Chatbot.test.tsx index 2efe8e2fb..26992b16b 100644 --- a/packages/module/src/Chatbot/Chatbot.test.tsx +++ b/packages/module/src/Chatbot/Chatbot.test.tsx @@ -28,4 +28,13 @@ describe('Chatbot', () => { render(Chatbot Content); expect(screen.queryByLabelText('Chatbot')).toBeFalsy(); }); + + it('should handle isCompact', () => { + render( + + Chatbot Content + + ); + expect(screen.getByTestId('chatbot')).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotFooter/ChatbotFooter.scss b/packages/module/src/ChatbotFooter/ChatbotFooter.scss index 7ef6eedea..52297f84f 100644 --- a/packages/module/src/ChatbotFooter/ChatbotFooter.scss +++ b/packages/module/src/ChatbotFooter/ChatbotFooter.scss @@ -57,3 +57,8 @@ padding: var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--lg); } } + +.pf-chatbot__footer.pf-m-compact .pf-chatbot__footer-container { + padding: 0 var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--sm); + row-gap: var(--pf-t--global--spacer--sm); +} diff --git a/packages/module/src/ChatbotFooter/ChatbotFooter.test.tsx b/packages/module/src/ChatbotFooter/ChatbotFooter.test.tsx index 46992454a..c44595a57 100644 --- a/packages/module/src/ChatbotFooter/ChatbotFooter.test.tsx +++ b/packages/module/src/ChatbotFooter/ChatbotFooter.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import ChatbotFooter from './ChatbotFooter'; +import '@testing-library/jest-dom'; describe('ChatbotFooter', () => { it('should render ChatbotFooter with children', () => { @@ -12,4 +13,13 @@ describe('ChatbotFooter', () => { const { container } = render(Chatbot Content); expect(container.querySelector('.custom-class')).toBeTruthy(); }); + + it('should handle isCompact', () => { + render( + + Chatbot Content + + ); + expect(screen.getByTestId('footer')).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotFooter/ChatbotFooter.tsx b/packages/module/src/ChatbotFooter/ChatbotFooter.tsx index f166c0109..81e4e693c 100644 --- a/packages/module/src/ChatbotFooter/ChatbotFooter.tsx +++ b/packages/module/src/ChatbotFooter/ChatbotFooter.tsx @@ -17,14 +17,16 @@ export interface ChatbotFooterProps extends React.HTMLProps { children?: React.ReactNode; /** Custom classname for the Footer component */ className?: string; + isCompact?: boolean; } export const ChatbotFooter: React.FunctionComponent = ({ children, className, + isCompact, ...props }: ChatbotFooterProps) => ( -
+
{children}
diff --git a/packages/module/src/ChatbotHeader/ChatbotHeader.scss b/packages/module/src/ChatbotHeader/ChatbotHeader.scss index e259c1311..baeddc1fb 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeader.scss +++ b/packages/module/src/ChatbotHeader/ChatbotHeader.scss @@ -141,7 +141,24 @@ // ============================================================================ // Information density styles // ============================================================================ +.pf-chatbot.pf-m-compact { + .pf-chatbot__header { + gap: var(--pf-t--global--spacer--sm); + padding: var(--pf-t--global--spacer--sm); + } + + .pf-chatbot__header .pf-chatbot__title img { + max-height: 24px; + vertical-align: middle; + } +} + +.pf-v6-c-menu-toggle.pf-chatbot__button--toggle-options.pf-m-compact, .pf-chatbot__button--toggle-menu.pf-m-compact { width: 2rem; height: 2rem; } + +.pf-chatbot__header .pf-chatbot__actions .pf-v6-c-menu-toggle.pf-m-secondary.pf-m-compact { + width: initial; +} diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx index dc935f815..1526e4b2d 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { ChatbotHeaderMenu } from './ChatbotHeaderMenu'; +import '@testing-library/jest-dom'; describe('ChatbotHeaderMenu', () => { it('should render ChatbotHeaderMenu with custom class', () => { @@ -16,4 +17,11 @@ describe('ChatbotHeaderMenu', () => { expect(onMenuToggle).toHaveBeenCalled(); }); + + it('should handle isCompact', () => { + render( + + ); + expect(screen.getByTestId('button')).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.tsx index a590af834..7630b9708 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderMenu.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { Button, Icon, Tooltip, TooltipProps } from '@patternfly/react-core'; +import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core'; import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon'; -export interface ChatbotHeaderMenuProps { +export interface ChatbotHeaderMenuProps extends ButtonProps { /** Callback function to attach to menu toggle on top right of chatbot header. */ onMenuToggle: () => void; /** Custom classname for the header component */ @@ -16,6 +16,7 @@ export interface ChatbotHeaderMenuProps { innerRef?: React.Ref; /** Content used in tooltip */ tooltipContent?: string; + isCompact?: boolean; } const ChatbotHeaderMenuBase: React.FunctionComponent = ({ @@ -24,7 +25,9 @@ const ChatbotHeaderMenuBase: React.FunctionComponent = ( tooltipProps, menuAriaLabel = 'Toggle menu', innerRef, - tooltipContent = 'Menu' + tooltipContent = 'Menu', + isCompact, + ...props }: ChatbotHeaderMenuProps) => (
= ( {...tooltipProps} >
diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx index 484bef704..408ea134b 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { DropdownItem } from '@patternfly/react-core'; import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { ChatbotHeaderOptionsDropdown } from './ChatbotHeaderOptionsDropdown'; +import '@testing-library/jest-dom'; describe('ChatbotHeaderOptionsDropdown', () => { const dropdownItems = ( @@ -42,4 +43,9 @@ describe('ChatbotHeaderOptionsDropdown', () => { expect(onSelect).toHaveBeenCalled(); }); }); + + it('should handle isCompact', () => { + render({dropdownItems}); + expect(screen.getByRole('button', { name: 'Chatbot options' })).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx index e8e1fe9c8..a4ca4771e 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx @@ -20,6 +20,7 @@ export interface ChatbotHeaderOptionsDropdownProps extends Omit = ({ @@ -28,6 +29,7 @@ export const ChatbotHeaderOptionsDropdown: React.FunctionComponent { const [isOptionsMenuOpen, setIsOptionsMenuOpen] = React.useState(false); @@ -42,17 +44,18 @@ export const ChatbotHeaderOptionsDropdown: React.FunctionComponent + } isExpanded={isOptionsMenuOpen} onClick={() => setIsOptionsMenuOpen(!isOptionsMenuOpen)} + size={isCompact ? 'sm' : undefined} /> ); diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx index 7d18bf3ff..778703435 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { DropdownItem } from '@patternfly/react-core'; import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { ChatbotHeaderSelectorDropdown } from './ChatbotHeaderSelectorDropdown'; +import '@testing-library/jest-dom'; describe('ChatbotHeaderSelectorDropdown', () => { const dropdownItems = ( @@ -40,4 +41,13 @@ describe('ChatbotHeaderSelectorDropdown', () => { expect(onSelect).toHaveBeenCalled(); }); }); + + it('should handle isCompact', () => { + render( + + {dropdownItems} + + ); + expect(screen.getByRole('button', { name: /Select model/i })).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx b/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx index a5884c279..3eafb70b4 100644 --- a/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx +++ b/packages/module/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx @@ -15,6 +15,7 @@ export interface ChatbotHeaderSelectorDropdownProps extends Omit = ({ @@ -25,6 +26,7 @@ export const ChatbotHeaderSelectorDropdown: React.FunctionComponent { const [isOptionsMenuOpen, setIsOptionsMenuOpen] = React.useState(false); @@ -45,6 +47,8 @@ export const ChatbotHeaderSelectorDropdown: React.FunctionComponent setIsOptionsMenuOpen(!isOptionsMenuOpen)} + size={isCompact ? 'sm' : undefined} + className={`${isCompact ? 'pf-m-compact' : ''}`} > {value} diff --git a/packages/module/src/ChatbotPopover/ChatbotPopover.scss b/packages/module/src/ChatbotPopover/ChatbotPopover.scss index 83476ecb9..19d64bbec 100644 --- a/packages/module/src/ChatbotPopover/ChatbotPopover.scss +++ b/packages/module/src/ChatbotPopover/ChatbotPopover.scss @@ -2,14 +2,19 @@ // Chatbot Popover // ============================================================================ .pf-chatbot__popover { - .pf-v6-c-popover__arrow { display: none; } + .pf-v6-c-popover__arrow { + display: none; + } // Footnote popover &--footnote.pf-chatbot__popover { - // Contents - img { border-radius: var(--pf-t--global--border--radius--small); } - .pf-v6-c-content--h3 { font-weight: var(--pf-t--global--font--weight--body--bold); } + img { + border-radius: var(--pf-t--global--border--radius--small); + } + .pf-v6-c-content--h3 { + font-weight: var(--pf-t--global--font--weight--body--bold); + } .pf-v6-c-content--p { font-size: var(--pf-t--global--font--size--body--lg); } @@ -23,5 +28,4 @@ font-size: var(--pf-t--global--font--size--body--lg); } } - } diff --git a/packages/module/src/ChatbotPopover/ChatbotPopover.tsx b/packages/module/src/ChatbotPopover/ChatbotPopover.tsx index 6627a3e81..ee92461b9 100644 --- a/packages/module/src/ChatbotPopover/ChatbotPopover.tsx +++ b/packages/module/src/ChatbotPopover/ChatbotPopover.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { Popover, PopoverProps } from '@patternfly/react-core'; export const ChatbotPopover: React.FunctionComponent = ({ children, className, ...props }) => ( - + {children} ); diff --git a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss index 0a37e02d0..8aa3e6138 100644 --- a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss +++ b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss @@ -8,7 +8,6 @@ gap: var(--pf-t--global--spacer--lg); .pf-v6-c-content--h1 { - --pf-v6-c-content--h1--FontSize: var(--pf-t--global--font--size--3xl); // larger than any of our semantic tokens --pf-v6-c-content--h1--FontWeight: var(--pf-t--global--font--weight--400); --pf-v6-c-content--h1--MarginBlockEnd: 0; } @@ -34,6 +33,15 @@ } } +.pf-chatbot--layout--welcome.pf-m-compact { + gap: var(--pf-t--global--spacer--md); + padding-block-end: var(--pf-t--global--spacer--md); + + .pf-chatbot__prompt-suggestions { + gap: var(--pf-t--global--spacer--md); + } +} + // ============================================================================ // Chatbot Display Mode - Fullscreen and Embedded // ============================================================================ diff --git a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx index ea67fede6..0dc5ecc02 100644 --- a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx +++ b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx @@ -69,4 +69,17 @@ describe('ChatbotWelcomePrompt', () => { const element = screen.getByTestId('welcome-prompt'); expect(element).toHaveClass('test'); }); + + it('should handle isCompact', () => { + render( + + ); + expect(screen.getByTestId('welcome-prompt')).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx index afe7a6e2a..e9808cb63 100644 --- a/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx +++ b/packages/module/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx @@ -16,6 +16,7 @@ export interface ChatbotWelcomePromptProps extends React.HTMLProps ( -
+
{title}
@@ -45,7 +51,12 @@ export const ChatbotWelcomePrompt: React.FunctionComponent {prompts?.map((prompt, index) => ( - +

{ expect(input.files).toHaveLength(1); expect(spy).toHaveBeenCalledTimes(1); }); + it('should handle isCompact', () => { + render(); + expect(screen.getByTestId('button')).toHaveClass('pf-m-compact'); + }); }); diff --git a/packages/module/src/MessageBar/AttachButton.tsx b/packages/module/src/MessageBar/AttachButton.tsx index f5275b7cd..15d4c6e47 100644 --- a/packages/module/src/MessageBar/AttachButton.tsx +++ b/packages/module/src/MessageBar/AttachButton.tsx @@ -25,6 +25,7 @@ export interface AttachButtonProps extends ButtonProps { tooltipContent?: string; /** Test id applied to input */ inputTestId?: string; + isCompact?: boolean; } const AttachButtonBase: React.FunctionComponent = ({ @@ -36,6 +37,7 @@ const AttachButtonBase: React.FunctionComponent = ({ innerRef, tooltipContent = 'Attach', inputTestId, + isCompact, ...props }: AttachButtonProps) => { const { open, getInputProps } = useDropzone({ @@ -62,15 +64,16 @@ const AttachButtonBase: React.FunctionComponent = ({