-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9653fd2
commit 5d87961
Showing
4 changed files
with
339 additions
and
0 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
255 changes: 255 additions & 0 deletions
255
...e/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx
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,255 @@ | ||
import React from 'react'; | ||
|
||
import { | ||
Page, | ||
Masthead, | ||
MastheadMain, | ||
MastheadBrand, | ||
MastheadLogo, | ||
PageSidebarBody, | ||
PageSidebar, | ||
MastheadToggle, | ||
PageToggleButton, | ||
SkipToContent, | ||
ToggleGroup, | ||
ToggleGroupItem | ||
} from '@patternfly/react-core'; | ||
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 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 ChatbotHeader, { ChatbotHeaderMain } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader'; | ||
import { BarsIcon } from '@patternfly/react-icons'; | ||
import userAvatar from '../Messages/user_avatar.svg'; | ||
import patternflyAvatar from '../Messages/patternfly_avatar.jpg'; | ||
|
||
export const CompareChild = ({ name, input, hasNewInput }) => { | ||
const [messages, setMessages] = React.useState<MessageProps[]>([]); | ||
const [announcement, setAnnouncement] = React.useState<string>(); | ||
const scrollToBottomRef = React.useRef<HTMLDivElement>(null); | ||
const displayMode = ChatbotDisplayMode.embedded; | ||
|
||
// 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 = (input: string) => { | ||
const date = new Date(); | ||
const newMessages: MessageProps[] = []; | ||
messages.forEach((message) => newMessages.push(message)); | ||
newMessages.push({ | ||
avatar: userAvatar, | ||
id: generateId(), | ||
name: 'You', | ||
role: 'user', | ||
content: input, | ||
timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}` | ||
}); | ||
newMessages.push({ | ||
avatar: patternflyAvatar, | ||
id: generateId(), | ||
name, | ||
role: 'bot', | ||
timestamp: `${date?.toLocaleDateString()} ${date?.toLocaleTimeString()}`, | ||
isLoading: true | ||
}); | ||
setMessages(newMessages); | ||
// make announcement to assistive devices that new messages have been added | ||
setAnnouncement(`Message from You: ${input}. Message from ${name} 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 from ${name} goes here`, | ||
name, | ||
avatar: patternflyAvatar, | ||
isLoading: false, | ||
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') } | ||
}, | ||
timestamp: date.toLocaleString() | ||
}); | ||
setMessages(loadedMessages); | ||
// make announcement to assistive devices that new message has loaded | ||
setAnnouncement(`Message from ${name}: API response goes here`); | ||
}, 5000); | ||
}; | ||
|
||
React.useEffect(() => { | ||
if (input) { | ||
handleSend(input); | ||
} | ||
}, [hasNewInput]); | ||
Check warning on line 103 in packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx GitHub Actions / call-build-lint-test-workflow / lint
|
||
|
||
// Auto-scrolls to the latest message | ||
React.useEffect(() => { | ||
// don't scroll the first load, but scroll if there's a current stream or a new source has popped up | ||
if (messages.length > 0) { | ||
scrollToBottomRef.current?.scrollIntoView(); | ||
} | ||
}, [messages]); | ||
|
||
return ( | ||
<Chatbot displayMode={displayMode}> | ||
<ChatbotHeader className="compare-header"> | ||
<ChatbotHeaderMain>{name}</ChatbotHeaderMain> | ||
</ChatbotHeader> | ||
<ChatbotContent> | ||
<MessageBox ariaLabel={`Scrollable message log for ${name}`} announcement={announcement}> | ||
<ChatbotWelcomePrompt title="Hello, Chatbot User" description="How may I help you today?" /> | ||
{messages.map((message) => ( | ||
<Message key={message.id} {...message} /> | ||
))} | ||
<div ref={scrollToBottomRef}></div> | ||
</MessageBox> | ||
</ChatbotContent> | ||
</Chatbot> | ||
); | ||
}; | ||
|
||
export const EmbeddedComparisonChatbotDemo: React.FunctionComponent = () => { | ||
const [input, setInput] = React.useState<string>(); | ||
const [hasNewInput, setHasNewInput] = React.useState(false); | ||
const [isSelected, setIsSelected] = React.useState('toggle-group-assistant-1'); | ||
const [showFirstChatbot, setShowFirstChatbot] = React.useState(true); | ||
const [showSecondChatbot, setShowSecondChatbot] = React.useState(false); | ||
const [isSidebarOpen, setIsSidebarOpen] = React.useState(false); | ||
const historyRef = React.useRef<HTMLButtonElement>(null); | ||
|
||
React.useEffect(() => { | ||
// we want to show the first if we switch to the mobile toggle view | ||
// and reset/switch back to normal otherwise | ||
const updateChatbotVisibility = () => { | ||
if (window.innerWidth >= 901) { | ||
setShowFirstChatbot(true); | ||
setShowSecondChatbot(true); | ||
} else { | ||
setShowFirstChatbot(true); | ||
setShowSecondChatbot(false); | ||
setIsSelected('toggle-group-assistant-1'); | ||
} | ||
}; | ||
window.addEventListener('resize', updateChatbotVisibility); | ||
|
||
return () => { | ||
window.removeEventListener('resize', updateChatbotVisibility); | ||
}; | ||
}, []); | ||
|
||
// this only happens on mobile | ||
const handleChildToggleClick = (event) => { | ||
const id = event.currentTarget.id; | ||
setIsSelected(id); | ||
setShowSecondChatbot(!showSecondChatbot); | ||
setShowFirstChatbot(!showFirstChatbot); | ||
}; | ||
|
||
const handleSend = (value: string) => { | ||
setInput(value); | ||
setHasNewInput(!hasNewInput); | ||
}; | ||
|
||
const masthead = ( | ||
<Masthead> | ||
<MastheadMain> | ||
<MastheadToggle> | ||
<PageToggleButton | ||
variant="plain" | ||
aria-label="Global navigation" | ||
isSidebarOpen={isSidebarOpen} | ||
onSidebarToggle={() => setIsSidebarOpen(!isSidebarOpen)} | ||
id="fill-nav-toggle" | ||
> | ||
<BarsIcon /> | ||
</PageToggleButton> | ||
</MastheadToggle> | ||
<MastheadBrand> | ||
<MastheadLogo href="https://patternfly.org" target="_blank"> | ||
Logo | ||
</MastheadLogo> | ||
</MastheadBrand> | ||
</MastheadMain> | ||
</Masthead> | ||
); | ||
|
||
const sidebar = ( | ||
<PageSidebar isSidebarOpen={isSidebarOpen} id="fill-sidebar"> | ||
<PageSidebarBody>Navigation</PageSidebarBody> | ||
</PageSidebar> | ||
); | ||
|
||
const skipToChatbot = (event: React.MouseEvent) => { | ||
event.preventDefault(); | ||
if (historyRef.current) { | ||
historyRef.current.focus(); | ||
} | ||
}; | ||
|
||
const skipToContent = ( | ||
/* You can also add a SkipToContent for your main content here */ | ||
<SkipToContent href="#" onClick={skipToChatbot}> | ||
Skip to chatbot | ||
</SkipToContent> | ||
); | ||
|
||
return ( | ||
<Page skipToContent={skipToContent} masthead={masthead} sidebar={sidebar} isContentFilled> | ||
<div className="pf-chatbot__compare-container"> | ||
<div className="pf-chatbot__compare-mobile-controls"> | ||
<ToggleGroup aria-label="Select which assistant to display"> | ||
<ToggleGroupItem | ||
className="pf-chatbot__compare-toggle" | ||
text="Assistant 1" | ||
buttonId="toggle-group-assistant-1" | ||
isSelected={isSelected === 'toggle-group-assistant-1'} | ||
onChange={handleChildToggleClick} | ||
/> | ||
<ToggleGroupItem | ||
className="pf-chatbot__compare-toggle" | ||
text="Assistant 2" | ||
buttonId="toggle-group-assistant-2" | ||
isSelected={isSelected === 'toggle-group-assistant-2'} | ||
onChange={handleChildToggleClick} | ||
/> | ||
</ToggleGroup> | ||
</div> | ||
<div className="pf-chatbot__compare"> | ||
<div | ||
className={`pf-chatbot__compare-item ${!showFirstChatbot ? 'pf-chatbot__compare-item-hidden' : undefined}`} | ||
> | ||
<CompareChild input={input} hasNewInput={hasNewInput} name="ChatBot One" /> | ||
</div> | ||
<div | ||
className={`pf-chatbot__compare-item ${!showSecondChatbot ? 'pf-chatbot__compare-item-hidden' : undefined}`} | ||
> | ||
<CompareChild input={input} hasNewInput={hasNewInput} name="ChatBot Two" /> | ||
</div> | ||
</div> | ||
<ChatbotFooter> | ||
<MessageBar onSendMessage={handleSend} hasAttachButton={false} /> | ||
</ChatbotFooter> | ||
</div> | ||
</Page> | ||
); | ||
}; |
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,77 @@ | ||
.pf-chatbot__compare-container { | ||
display: flex; | ||
flex-direction: column; | ||
position: relative; | ||
height: 100%; | ||
|
||
.pf-chatbot__footer { | ||
position: sticky; | ||
bottom: 0; | ||
} | ||
} | ||
.pf-chatbot__compare-toggle { | ||
width: 100%; | ||
|
||
.pf-v6-c-toggle-group__button { | ||
width: 100%; | ||
display: flex; | ||
justify-content: center; | ||
} | ||
} | ||
.pf-chatbot__compare { | ||
display: flex; | ||
height: 100%; | ||
width: 100%; | ||
|
||
@media screen and (max-width: 900px) { | ||
overflow-y: auto; | ||
} | ||
|
||
.pf-chatbot__compare-item:first-of-type { | ||
border-right: 1px solid var(--pf-t--global--border--color--default); | ||
|
||
@media screen and (max-width: 900px) { | ||
border-right: 0px; | ||
} | ||
} | ||
} | ||
|
||
.pf-chatbot__compare-item { | ||
flex: 1; | ||
|
||
.pf-chatbot--embedded .pf-chatbot__messagebox { | ||
width: 100%; | ||
} | ||
|
||
.pf-chatbot__content { | ||
padding: 0; | ||
} | ||
|
||
.pf-chatbot.pf-chatbot--embedded { | ||
@media screen and (max-width: 900px) { | ||
height: 100%; | ||
} | ||
} | ||
} | ||
.pf-chatbot__compare-item-hidden { | ||
display: block; | ||
|
||
@media screen and (max-width: 900px) { | ||
display: none; | ||
} | ||
} | ||
|
||
.pf-chatbot__compare-mobile-controls { | ||
padding: var(--pf-t--global--spacer--md) var(--pf-t--global--spacer--lg) 0 var(--pf-t--global--spacer--lg); | ||
display: none; | ||
background-color: var(--pf-t--global--background--color--secondary--default); | ||
position: sticky; | ||
top: 0; | ||
z-index: 9999; | ||
|
||
@media screen and (max-width: 900px) { | ||
display: flex; | ||
flex-direction: column; | ||
gap: var(--pf-t--global--spacer--md); | ||
} | ||
} |
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