Skip to content

Commit

Permalink
docs: add footer examples
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolethoen committed Sep 19, 2024
1 parent 8198a92 commit becb40b
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ id: Chatbot attachment
source: react
# If you use typescript, the name of the interface to display props for
# These are found through the sourceProps function provided in patternfly-docs.source.js
propComponents: ['AttachMenu', 'AttachmentEdit', 'FileDetails', 'FileDetailsLabel', 'FileDropZone', 'PreviewAttachment']
propComponents: ['AttachMenu', 'AttachmentEdit', 'FileDetails', 'FileDetailsLabel', 'FileDropZone', 'PreviewAttachment', 'SourceDetailsMenuItem']
---

import AttachmentEdit from '@patternfly/virtual-assistant/dist/dynamic/AttachmentEdit';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ section: extensions
subsection: Chat bots / AI
# Sidenav secondary level section
# should be the same for all markdown files
id: Chatbot Footer
id: Chatbot footer
# Tab (react | react-demos | html | html-demos | design-guidelines | accessibility)
source: react
# If you use typescript, the name of the interface to display props for
Expand All @@ -19,14 +19,46 @@ propComponents:
'ChatbotFootnotePopoverCTA',
'ChatbotFootnotePopoverBannerImage',
'ChatbotFootnotePopoverLink',
'MessageBarWithAttachMenuProps'
'MessageBarWithAttachMenuProps',
'SourceDetailsMenuItem'
]
---

import { ChatbotFooter, ChatbotFootnote } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotFooter';
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';
import SourceDetailsMenuItem from '@patternfly/virtual-assistant/dist/dynamic/SourceDetailsMenuItem';
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, UploadIcon } from '@patternfly/react-icons';

### Basic example

### Footnote with popover
A footnote can be placed in a chatbot footer to display any legal disclaimers or information about the chatbot.
Footnotes can be static text or a button which triggers a popover.

```js file="./ChatbotFootnote.tsx"

```

### Message bar with speech to text
By default the message bar enables uploading files. Setting the `hasAttachButton` to `false` will disable that feature.

```js file="./ChatbotMessageBar.tsx"

```

### Message bar with attach menu appended to attach button
```js file="./ChatbotMessageBarAttach.tsx"

```

### Simple footer with Message bar and footnote

Footers contain the message bar and optional interactive footnote
```noLive
<ChatbotFooter>
<MessageBar ... />
<ChatbotFootnote .../>
</ChatbotFooter>
```

```js file="./ChatbotFooter.tsx"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,13 @@ import React from 'react';
import { ChatbotFooter, ChatbotFootnote } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotFooter';
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';

const footnoteProps = {
label: 'Lightspeed uses AI. Check for mistakes.',
popover: {
title: 'Verify accuracy',
description: `While Lightspeed strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,
bannerImage: {
src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
alt: 'Example image for footnote popover'
},
cta: {
label: 'Got it',
onClick: () => {
alert('Do something!');
}
},
link: {
label: 'Learn more',
url: 'https://www.redhat.com/'
}
}
};

export const BasicDemo: React.FunctionComponent = () => {
export const ChatbotFooterExample: React.FunctionComponent = () => {
const handleSend = (message) => alert(message);

return (
<ChatbotFooter>
<MessageBar onSendMessage={handleSend} hasMicrophoneButton hasAttachButton />
<ChatbotFootnote {...footnoteProps} />
<ChatbotFootnote label="Lightspeed uses AI. Check for mistakes." />
</ChatbotFooter>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { ChatbotFootnote } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotFooter';

export const FootnoteDemo: React.FunctionComponent = () => (
<ChatbotFootnote
label="Lightspeed uses AI. Check for mistakes."
popover={{
title: 'Verify accuracy',
description: `While Lightspeed strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`,
bannerImage: {
src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
alt: 'Example image for footnote popover'
},
cta: {
label: 'Got it',
onClick: () => {
alert('Do something!');
}
},
link: {
label: 'Learn more',
url: 'https://www.redhat.com/'
}
}}
/>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';

export const ChatbotMessageBarExample: React.FunctionComponent = () => {
const handleSend = (message) => alert(message);

return <MessageBar onSendMessage={handleSend} hasMicrophoneButton />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React from 'react';
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';
import SourceDetailsMenuItem from '@patternfly/virtual-assistant/dist/dynamic/SourceDetailsMenuItem';
import { Divider, DropdownGroup, DropdownItem, DropdownList } from '@patternfly/react-core';
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, UploadIcon } from '@patternfly/react-icons';

export const ChatbotMessageBarDefaultAttachExample: React.FunctionComponent = () => {
const [isOpen, setIsOpen] = React.useState<boolean>(false);
const [userFacingMenuItems, setUserFacingMenuItems] = React.useState<React.ReactNode>([]);

const handleSend = (message) => alert(message);

const onTextChange = (textValue: string) => {
if (textValue === '') {
setUserFacingMenuItems(initialMenuItems.concat(uploadMenuItems));
return;
}

const newMenuItems = findMatchingElements(initialMenuItems, textValue);
// this is necessary because the React nodes we find traversing the recursive search
// aren't correctly wrapped in a DropdownList. This leads to problems with the
// auth-operator item where it winds up floating in a bad place in the DOM and never
// gets removed
setUserFacingMenuItems(
<>
<DropdownList>
{newMenuItems.length === 0 ? (
<DropdownItem key="no-items">No results found</DropdownItem>
) : (
newMenuItems.map((item) => item)
)}
</DropdownList>
{uploadMenuItems.map((item) => item)}
</>
);
};

const onToggleClick = () => {
setUserFacingMenuItems(initialMenuItems.concat(uploadMenuItems));
};

const initialMenuItems = [
<DropdownList key="list-1">
<DropdownItem value="auth-operator Pod" id="0" className="pf-chatbot-source-details-dropdown-item">
<SourceDetailsMenuItem
icon={
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M0 12.5C0 5.87258 5.37258 0.5 12 0.5C18.6274 0.5 24 5.87258 24 12.5C24 19.1274 18.6274 24.5 12 24.5C5.37258 24.5 0 19.1274 0 12.5Z"
fill="currentColor"
/>
<g clipPath="url(#clip0_3280_27488)">
<path
d="M8.25 8.75C8.25 7.92266 8.92266 7.25 9.75 7.25H12C14.0719 7.25 15.75 8.92812 15.75 11C15.75 13.0719 14.0719 14.75 12 14.75H9.75V17C9.75 17.4148 9.41484 17.75 9 17.75C8.58516 17.75 8.25 17.4148 8.25 17V14V8.75ZM9.75 13.25H12C13.2422 13.25 14.25 12.2422 14.25 11C14.25 9.75781 13.2422 8.75 12 8.75H9.75V13.25Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_3280_27488">
<rect width="7.5" height="12" fill="white" transform="translate(8.25 6.5)" />
</clipPath>
</defs>
</svg>
}
name="auth-operator"
type="Pod"
/>
</DropdownItem>
</DropdownList>,
<DropdownGroup key="group2">
<DropdownList>
<DropdownItem value="Alerts" id="1" icon={<BellIcon />}>
Alerts
</DropdownItem>
<DropdownItem value="Events" id="2" icon={<CalendarAltIcon />}>
Events
</DropdownItem>
<DropdownItem value="Logs" id="3" icon={<ClipboardIcon />}>
Logs
</DropdownItem>
<DropdownItem value="YAML - Status" id="4" icon={<CodeIcon />}>
YAML - Status
</DropdownItem>
<DropdownItem value="YAML - All contents" id="5" icon={<CodeIcon />}>
YAML - All contents
</DropdownItem>
</DropdownList>
</DropdownGroup>
];

const uploadMenuItems = [
<Divider key="divider" />,
<DropdownList key="list-2">
<DropdownItem
onClick={() => {
open();
setIsOpen(!isOpen);
}}
key="upload"
value="upload"
id="upload"
icon={<UploadIcon />}
>
Upload from computer
</DropdownItem>
</DropdownList>
];

return (
<MessageBar
onSendMessage={handleSend}
attachMenuProps={{
isAttachMenuOpen: isOpen,
setIsAttachMenuOpen: setIsOpen,
attachMenuItems: userFacingMenuItems,
onAttachMenuSelect: (_ev, value) => {
// eslint-disable-next-line no-console
console.log('selected', value);
},
attachMenuInputPlaceholder: 'Search cluster resources...',
onAttachMenuInputChange: onTextChange,
onAttachMenuToggleClick: onToggleClick
}}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import OpenDrawerRightIcon from '@patternfly/react-icons/dist/esm/icons/open-dra
import PFHorizontalLogoColor from './PF-HorizontalLogo-Color.svg';
import PFHorizontalLogoReverse from './PF-HorizontalLogo-Reverse.svg';

### Basic example
### Chatbot header with controls

```js file="./ChatbotHeaderBasic.tsx"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
# Sidenav top-level section
# should be the same for all markdown files
section: extensions
subsection: Chat bots / AI
# Sidenav secondary level section
# should be the same for all markdown files
id: Chatbot messages
# Tab (react | react-demos | html | html-demos | design-guidelines | accessibility)
source: react
---

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { PropsWithChildren } from 'react';
import React from 'react';
import { Icon, Flex, Stack, StackItem } from '@patternfly/react-core';

interface SourceDetailsMenuItemProps {
export interface SourceDetailsMenuItemProps extends React.HTMLProps<HTMLDivElement> {
/** Icon */
icon: React.ReactNode;
/** Name of source */
Expand All @@ -10,8 +10,13 @@ interface SourceDetailsMenuItemProps {
type?: string;
}

export const SourceDetailsMenuItem = ({ icon, name, type }: PropsWithChildren<SourceDetailsMenuItemProps>) => (
<Flex className="pf-chatbot__source-details" gap={{ default: 'gapSm' }}>
export const SourceDetailsMenuItem: React.FunctionComponent<SourceDetailsMenuItemProps> = ({
icon,
name,
type,
...props
}: SourceDetailsMenuItemProps) => (
<Flex className="pf-chatbot__source-details" gap={{ default: 'gapSm' }} {...props}>
<Flex
justifyContent={{ default: 'justifyContentCenter' }}
alignItems={{ default: 'alignItemsCenter' }}
Expand Down

0 comments on commit becb40b

Please sign in to comment.