Skip to content

Commit 72b3cfd

Browse files
committed
feat(SkipToContent): Add SkipToContent to demos
1 parent c7b51b8 commit 72b3cfd

File tree

6 files changed

+63
-15
lines changed

6 files changed

+63
-15
lines changed

packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/demos/Chatbot.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ This demo displays a basic chatbot, which includes:
7777

7878
6. A [`<ChatbotConversationHistoryNav>`](/patternfly-ai/chatbot/chatbot-conversation-history) toggled open and closed by the `<ChatbotHeaderMenu`> in the `<ChatbotHeader>`.
7979

80+
7. A skip to chatbot link, made with a [PatternFly 6 Skip to Content](/components/skip-to-content)
81+
8082
```js file="./Chatbot.tsx" isFullscreen
8183

8284
```
@@ -85,7 +87,7 @@ This demo displays a basic chatbot, which includes:
8587

8688
This demo displays an embedded chatbot. Embedded chatbots are meant to be placed within a page in your product. This demo includes:
8789

88-
1. A [PatternFly page](/components/page) with a sidebar and masthead
90+
1. A [PatternFly page](/components/page) with a sidebar, skip to chatbot link, and masthead
8991
2. A [`<Chatbot>`](/patternfly-ai/chatbot/chatbot-container) container.
9092
3. A [`<ChatbotHeader>`](/patternfly-ai/chatbot/chatbot-header) with all built sub-components laid out, including a `<ChatbotHeaderTitle>`
9193
4. [`<ChatbotContent>` and `<MessageBox>`](/patternfly-ai/chatbot/chatbot-container#content-and-message-box) with:

packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/demos/Chatbot.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import { Bullseye, Brand, DropdownList, DropdownItem, DropdownGroup } from '@patternfly/react-core';
3+
import { Bullseye, Brand, DropdownList, DropdownItem, DropdownGroup, SkipToContent } from '@patternfly/react-core';
44

55
import ChatbotToggle from '@patternfly/virtual-assistant/dist/dynamic/ChatbotToggle';
66
import Chatbot, { ChatbotDisplayMode } from '@patternfly/virtual-assistant/dist/dynamic/Chatbot';
@@ -169,6 +169,7 @@ export const ChatbotDemo: React.FunctionComponent = () => {
169169
);
170170
const [announcement, setAnnouncement] = React.useState<string>();
171171
const scrollToBottomRef = React.useRef<HTMLDivElement>(null);
172+
const toggleRef = React.useRef<HTMLButtonElement>(null);
172173

173174
// Autu-scrolls to the latest message
174175
React.useEffect(() => {
@@ -281,12 +282,24 @@ export const ChatbotDemo: React.FunctionComponent = () => {
281282
</>
282283
);
283284

285+
const handleSkipToContent = (e) => {
286+
e.preventDefault();
287+
if (toggleRef.current) {
288+
toggleRef.current.focus();
289+
}
290+
};
291+
284292
return (
285293
<>
294+
<SkipToContent onClick={handleSkipToContent} href="#">
295+
Skip to chatbot
296+
</SkipToContent>
286297
<ChatbotToggle
287298
toolTipLabel="Chatbot"
288299
isChatbotVisible={chatbotVisible}
289300
onToggleChatbot={() => setChatbotVisible(!chatbotVisible)}
301+
id="chatbot-toggle"
302+
ref={toggleRef}
290303
/>
291304
<Chatbot isVisible={chatbotVisible} displayMode={displayMode}>
292305
<ChatbotConversationHistoryNav

packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/demos/EmbeddedChatbot.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
PageSidebarBody,
1414
PageSidebar,
1515
MastheadToggle,
16-
PageToggleButton
16+
PageToggleButton,
17+
SkipToContent
1718
} from '@patternfly/react-core';
1819

1920
import Chatbot, { ChatbotDisplayMode } from '@patternfly/virtual-assistant/dist/dynamic/Chatbot';
@@ -175,6 +176,8 @@ export const EmbeddedChatbotDemo: React.FunctionComponent = () => {
175176
const [isSidebarOpen, setIsSidebarOpen] = React.useState(false);
176177
const [announcement, setAnnouncement] = React.useState<string>();
177178
const scrollToBottomRef = React.useRef<HTMLDivElement>(null);
179+
const chatbotRef = React.useRef<HTMLDivElement>(null);
180+
178181
const displayMode = ChatbotDisplayMode.embedded;
179182
// Autu-scrolls to the latest message
180183
React.useEffect(() => {
@@ -302,8 +305,20 @@ export const EmbeddedChatbotDemo: React.FunctionComponent = () => {
302305
</PageSidebar>
303306
);
304307

308+
const skipToChatbot = (event: React.MouseEvent) => {
309+
event.preventDefault();
310+
chatbotRef.current?.focus();
311+
};
312+
313+
const skipToContent = (
314+
/* You can also add a SkipToContent for your main content here */
315+
<SkipToContent href="#" onClick={skipToChatbot}>
316+
Skip to chatbot
317+
</SkipToContent>
318+
);
319+
305320
return (
306-
<Page masthead={masthead} sidebar={sidebar} isContentFilled>
321+
<Page skipToContent={skipToContent} masthead={masthead} sidebar={sidebar} isContentFilled>
307322
<Chatbot displayMode={displayMode}>
308323
<ChatbotConversationHistoryNav
309324
displayMode={displayMode}
@@ -357,7 +372,7 @@ export const EmbeddedChatbotDemo: React.FunctionComponent = () => {
357372
<ChatbotContent>
358373
{/* Update the announcement prop on MessageBox whenever a new message is sent
359374
so that users of assistive devices receive sufficient context */}
360-
<MessageBox announcement={announcement}>
375+
<MessageBox announcement={announcement} ref={chatbotRef}>
361376
<ChatbotWelcomePrompt
362377
title="Hello, Chatbot User"
363378
description="How may I help you today?"

packages/module/src/Chatbot/Chatbot.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export const Chatbot: React.FunctionComponent<ChatbotProps> = ({
2626
children,
2727
displayMode = ChatbotDisplayMode.default,
2828
isVisible = true,
29-
className
29+
className,
30+
...props
3031
}: ChatbotProps) => {
3132
// Configure docked mode
3233
React.useEffect(() => {
@@ -49,6 +50,7 @@ export const Chatbot: React.FunctionComponent<ChatbotProps> = ({
4950
variants={motionChatbot}
5051
initial="hidden"
5152
animate={isVisible ? 'visible' : 'hidden'}
53+
{...props}
5254
>
5355
{isVisible ? children : undefined}
5456
</motion.div>

packages/module/src/ChatbotToggle/ChatbotToggle.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
// ============================================================================
22
// Chatbot Toggle
33
// ============================================================================
4-
54
import React from 'react';
6-
7-
// Import PatternFly components
85
import { Button, ButtonProps, Tooltip, TooltipProps, Icon } from '@patternfly/react-core';
96
import AngleDownIcon from '@patternfly/react-icons/dist/esm/icons/angle-down-icon';
107

11-
// Import Chatbot components
12-
138
export interface ChatbotToggleProps extends ButtonProps {
149
/** Contents of the tooltip applied to the toggle button */
1510
toolTipLabel?: React.ReactNode;
@@ -23,6 +18,8 @@ export interface ChatbotToggleProps extends ButtonProps {
2318
toggleButtonLabel?: string;
2419
/** An image displayed in the chatbot toggle when it is closed */
2520
closedToggleIcon?: () => JSX.Element;
21+
/** Ref applied to toggle */
22+
innerRef?: React.Ref<any>;
2623
}
2724

2825
const ChatIcon = () => (
@@ -44,13 +41,14 @@ const ChatIcon = () => (
4441
</svg>
4542
);
4643

47-
export const ChatbotToggle: React.FunctionComponent<ChatbotToggleProps> = ({
44+
const ChatbotToggleBase: React.FunctionComponent<ChatbotToggleProps> = ({
4845
toolTipLabel,
4946
isChatbotVisible,
5047
onToggleChatbot,
5148
tooltipProps,
5249
toggleButtonLabel,
5350
closedToggleIcon: ClosedToggleIcon,
51+
innerRef,
5452
...props
5553
}: ChatbotToggleProps) => {
5654
// Configure icon
@@ -66,6 +64,7 @@ export const ChatbotToggle: React.FunctionComponent<ChatbotToggleProps> = ({
6664
onClick={onToggleChatbot}
6765
aria-expanded={isChatbotVisible}
6866
icon={<Icon isInline>{icon}</Icon>}
67+
ref={innerRef}
6968
{...props}
7069
>
7170
{/* Notification dot placeholder */}
@@ -74,4 +73,8 @@ export const ChatbotToggle: React.FunctionComponent<ChatbotToggleProps> = ({
7473
);
7574
};
7675

76+
const ChatbotToggle = React.forwardRef((props: ChatbotToggleProps, ref: React.Ref<any>) => (
77+
<ChatbotToggleBase innerRef={ref} {...props} />
78+
));
79+
7780
export default ChatbotToggle;

packages/module/src/MessageBox/MessageBox.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,27 @@ export interface MessageBoxProps extends React.HTMLProps<HTMLDivElement> {
1313
children: React.ReactNode;
1414
/** Custom classname for the MessageBox component */
1515
className?: string;
16+
/** Ref applied to message box */
17+
innerRef?: React.Ref<HTMLDivElement>;
1618
}
1719

18-
const MessageBox: React.FunctionComponent<MessageBoxProps> = ({
20+
const MessageBoxBase: React.FunctionComponent<MessageBoxProps> = ({
1921
announcement,
2022
ariaLabel = 'Scrollable message log',
2123
children,
24+
innerRef,
2225
className
2326
}: MessageBoxProps) => {
2427
const [atTop, setAtTop] = React.useState(false);
2528
const [atBottom, setAtBottom] = React.useState(true);
2629
const [isOverflowing, setIsOverflowing] = React.useState(false);
27-
const messageBoxRef = React.useRef<HTMLDivElement>(null);
30+
const defaultRef = React.useRef<HTMLDivElement>(null);
31+
let messageBoxRef;
32+
if (innerRef) {
33+
messageBoxRef = innerRef;
34+
} else {
35+
messageBoxRef = defaultRef;
36+
}
2837

2938
// Configure handlers
3039
const handleScroll = React.useCallback(() => {
@@ -83,7 +92,7 @@ const MessageBox: React.FunctionComponent<MessageBoxProps> = ({
8392
tabIndex={0}
8493
aria-label={ariaLabel}
8594
className={`pf-chatbot__messagebox ${className ?? ''}`}
86-
ref={messageBoxRef}
95+
ref={innerRef ?? messageBoxRef}
8796
>
8897
{children}
8998
<div className="pf-chatbot__messagebox-announcement" aria-live="polite">
@@ -95,4 +104,8 @@ const MessageBox: React.FunctionComponent<MessageBoxProps> = ({
95104
);
96105
};
97106

107+
export const MessageBox = React.forwardRef((props: MessageBoxProps, ref: React.Ref<any>) => (
108+
<MessageBoxBase innerRef={ref} {...props} />
109+
));
110+
98111
export default MessageBox;

0 commit comments

Comments
 (0)