Skip to content

Commit a3df7fb

Browse files
committed
feat(Message): Adjust components for use on white backgrounds
Added styles so components behave well on primary ChatBot background color (white). The specific components are the attachment label, table, loading indicator, and inline code block.
1 parent cda10eb commit a3df7fb

File tree

12 files changed

+107
-18
lines changed

12 files changed

+107
-18
lines changed

packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/WhiteEmbeddedChatbot.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ const MessageLoading = () => (
9797
export default MessageLoading;
9898
9999
~~~
100+
101+
Here is a table:
102+
103+
| Version | GA date | User role
104+
|-|-|-|
105+
| 2.5 | September 30, 2024 | Administrator |
106+
| 2.5 | June 27, 2023 | Editor |
107+
| 3.0 | April 1, 2025 | Administrator
100108
`;
101109

102110
// It's important to set a date and timestamp prop since the Message components re-render.
@@ -111,7 +119,8 @@ const initialMessages: MessageProps[] = [
111119
name: 'User',
112120
avatar: userAvatar,
113121
timestamp: date.toLocaleString(),
114-
avatarProps: { isBordered: true }
122+
avatarProps: { isBordered: true },
123+
isPrimary: true
115124
},
116125
{
117126
id: '2',
@@ -131,7 +140,9 @@ const initialMessages: MessageProps[] = [
131140
download: { onClick: () => console.log('Download') },
132141
// eslint-disable-next-line no-console
133142
listen: { onClick: () => console.log('Listen') }
134-
}
143+
},
144+
isPrimary: true,
145+
attachments: [{ name: 'auth-operator.yml', id: '1' }]
135146
}
136147
];
137148

@@ -220,7 +231,8 @@ export const EmbeddedChatbotDemo: FunctionComponent = () => {
220231
name: 'User',
221232
avatar: userAvatar,
222233
timestamp: date.toLocaleString(),
223-
avatarProps: { isBordered: true }
234+
avatarProps: { isBordered: true },
235+
isPrimary: true
224236
});
225237
newMessages.push({
226238
id: generateId(),
@@ -229,7 +241,8 @@ export const EmbeddedChatbotDemo: FunctionComponent = () => {
229241
name: 'Bot',
230242
avatar: patternflyAvatar,
231243
isLoading: true,
232-
timestamp: date.toLocaleString()
244+
timestamp: date.toLocaleString(),
245+
isPrimary: true
233246
});
234247
setMessages(newMessages);
235248
// make announcement to assistive devices that new messages have been added
@@ -261,7 +274,8 @@ export const EmbeddedChatbotDemo: FunctionComponent = () => {
261274
// eslint-disable-next-line no-console
262275
listen: { onClick: () => console.log('Listen') }
263276
},
264-
timestamp: date.toLocaleString()
277+
timestamp: date.toLocaleString(),
278+
isPrimary: true
265279
});
266280
setMessages(loadedMessages);
267281
// make announcement to assistive devices that new message has loaded

packages/module/src/FileDetailsLabel/FileDetailsLabel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { PropsWithChildren } from 'react';
2-
import { Button, Label } from '@patternfly/react-core';
2+
import { Button, Label, LabelProps } from '@patternfly/react-core';
33
import FileDetails from '../FileDetails';
44
import { Spinner } from '@patternfly/react-core';
55
import { TimesIcon } from '@patternfly/react-icons';
66

7-
export interface FileDetailsLabelProps {
7+
export interface FileDetailsLabelProps extends Omit<LabelProps, 'onClose' | 'onClick'> {
88
/** Name of file, including extension */
99
fileName: string;
1010
/** Unique id of file */

packages/module/src/Message/CodeBlockMessage/CodeBlockMessage.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@
8080
--pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--body--default);
8181
background-color: var(--pf-t--global--background--color--tertiary--default);
8282
font-size: var(--pf-chatbot-message-text-inline-code-font-size);
83+
84+
&.pf-m-primary {
85+
background-color: var(--pf-t--global--background--color--secondary--default);
86+
}
8387
}
8488

8589
.pf-chatbot__message-code-toggle {

packages/module/src/Message/CodeBlockMessage/CodeBlockMessage.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export interface CodeBlockMessageProps {
3737
expandedText?: string;
3838
/** Link text applied to expandable toggle when collapsed */
3939
collapsedText?: string;
40+
/** Sets background colors to be appropriate on primary chatbot background */
41+
isPrimary?: boolean;
4042
}
4143

4244
const DEFAULT_EXPANDED_TEXT = 'Show less';
@@ -51,6 +53,7 @@ const CodeBlockMessage = ({
5153
expandableSectionToggleProps,
5254
expandedText = DEFAULT_EXPANDED_TEXT,
5355
collapsedText = DEFAULT_COLLAPSED_TEXT,
56+
isPrimary,
5457
...props
5558
}: CodeBlockMessageProps) => {
5659
const [copied, setCopied] = useState(false);
@@ -105,7 +108,7 @@ const CodeBlockMessage = ({
105108

106109
if (!String(children).includes('\n')) {
107110
return (
108-
<code {...props} className="pf-chatbot__message-inline-code">
111+
<code {...props} className={`pf-chatbot__message-inline-code ${isPrimary ? 'pf-m-primary' : ''}`}>
109112
{children}
110113
</code>
111114
);

packages/module/src/Message/Message.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,4 +1075,41 @@ describe('Message', () => {
10751075
expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
10761076
expect(screen.getByText("Here's why I said this.")).toBeTruthy();
10771077
});
1078+
it('should handle isPrimary correctly for inline code when it is true', () => {
1079+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} isPrimary />);
1080+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
1081+
});
1082+
it('should handle isPrimary correctly for inline code when it is false', () => {
1083+
const { container } = render(<Message avatar="./img" role="user" name="User" content={INLINE_CODE} />);
1084+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
1085+
});
1086+
it('should handle isPrimary correctly for table when it is true', () => {
1087+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} isPrimary />);
1088+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
1089+
});
1090+
it('should handle isPrimary correctly for table when it is false', () => {
1091+
const { container } = render(<Message avatar="./img" role="user" name="User" content={TABLE} />);
1092+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
1093+
});
1094+
it('should handle isPrimary correctly for loading when it is true', () => {
1095+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isPrimary isLoading />);
1096+
expect(container.querySelector('.pf-m-primary')).toBeTruthy();
1097+
});
1098+
it('should handle isPrimary correctly for loading when it is false', () => {
1099+
const { container } = render(<Message avatar="./img" role="user" name="User" content="" isLoading />);
1100+
1101+
expect(container.querySelector('.pf-m-primary')).toBeFalsy();
1102+
});
1103+
it('should handle isPrimary correctly for attachments when it is true', () => {
1104+
const { container } = render(
1105+
<Message avatar="./img" role="user" name="User" content="" isPrimary attachments={[{ name: 'testAttachment' }]} />
1106+
);
1107+
expect(container.querySelector('.pf-m-outline')).toBeTruthy();
1108+
});
1109+
it('should handle isPrimary correctly for attachments when it is false', () => {
1110+
const { container } = render(
1111+
<Message avatar="./img" role="user" name="User" content="" attachments={[{ name: 'testAttachment' }]} />
1112+
);
1113+
expect(container.querySelector('.pf-m-outline')).toBeFalsy();
1114+
});
10781115
});

packages/module/src/Message/Message.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
205205
toolCall?: ToolCallProps;
206206
/** Whether user messages default to stripping out images in markdown */
207207
hasNoImagesInUserMessages?: boolean;
208+
/** Sets background colors to be appropriate on primary chatbot background */
209+
isPrimary?: boolean;
208210
}
209211

210212
export const MessageBase: FunctionComponent<MessageProps> = ({
@@ -252,6 +254,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
252254
remarkGfmProps,
253255
toolCall,
254256
hasNoImagesInUserMessages = true,
257+
isPrimary,
255258
...props
256259
}: MessageProps) => {
257260
const [messageText, setMessageText] = useState(content);
@@ -302,13 +305,13 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
302305
p: (props) => {
303306
// eslint-disable-next-line @typescript-eslint/no-unused-vars
304307
const { node, ...rest } = props;
305-
return <TextMessage component={ContentVariants.p} {...rest} />;
308+
return <TextMessage component={ContentVariants.p} {...rest} isPrimary={isPrimary} />;
306309
},
307310
code: ({ children, ...props }) => {
308311
// eslint-disable-next-line @typescript-eslint/no-unused-vars
309312
const { node, ...codeProps } = props;
310313
return (
311-
<CodeBlockMessage {...codeProps} {...codeBlockProps}>
314+
<CodeBlockMessage {...codeProps} {...codeBlockProps} isPrimary={isPrimary}>
312315
{children}
313316
</CodeBlockMessage>
314317
);
@@ -364,7 +367,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
364367
return <ListItemMessage {...rest} />;
365368
},
366369
// table requires node attribute for calculating headers for mobile breakpoint
367-
table: (props) => <TableMessage {...props} {...tableProps} />,
370+
table: (props) => <TableMessage {...props} {...tableProps} isPrimary={isPrimary} />,
368371
tbody: (props) => {
369372
// eslint-disable-next-line @typescript-eslint/no-unused-vars
370373
const { node, ...rest } = props;
@@ -432,7 +435,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
432435

433436
const renderMessage = () => {
434437
if (isLoading) {
435-
return <MessageLoading loadingWord={loadingWord} />;
438+
return <MessageLoading loadingWord={loadingWord} isPrimary={isPrimary} />;
436439
}
437440
if (isEditable) {
438441
return (
@@ -538,6 +541,7 @@ export const MessageBase: FunctionComponent<MessageProps> = ({
538541
closeButtonAriaLabel={attachment.closeButtonAriaLabel}
539542
languageTestId={attachment.languageTestId}
540543
spinnerTestId={attachment.spinnerTestId}
544+
variant={isPrimary ? 'outline' : undefined}
541545
/>
542546
</div>
543547
))}

packages/module/src/Message/MessageLoading.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@
5050
background-color: rgba(41, 41, 41, 0.25);
5151
}
5252
}
53+
54+
&.pf-m-primary {
55+
background-color: var(--pf-t--global--background--color--secondary--default);
56+
}
5357
}

packages/module/src/Message/MessageLoading.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// Chatbot Main - Message - Processing
33
// ============================================================================
44

5-
const MessageLoading = ({ loadingWord }) => (
6-
<div className="pf-chatbot__message-loading">
5+
const MessageLoading = ({ loadingWord, isPrimary }) => (
6+
<div className={`pf-chatbot__message-loading ${isPrimary ? 'pf-m-primary' : ''}`}>
77
<span className="pf-chatbot__message-loading-dots">
88
<span className="pf-v6-screen-reader">{loadingWord}</span>
99
</span>

packages/module/src/Message/TableMessage/TableMessage.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
border-block-start: 0;
99
}
1010

11+
&.pf-m-primary {
12+
--pf-v6-c-table--BackgroundColor: var(--pf-t--global--background--color--secondary--default) !important;
13+
}
14+
1115
tbody {
1216
border-radius: var(--pf-t--global--border--radius--small);
1317
}

packages/module/src/Message/TableMessage/TableMessage.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ export interface TableNode {
1919
type: string;
2020
}
2121

22-
const TableMessage = ({ children, ...props }: Omit<TableProps, 'ref'> & ExtraProps) => {
22+
export interface TableMessageProps {
23+
isPrimary?: boolean;
24+
}
25+
26+
const TableMessage = ({ children, isPrimary, ...props }: Omit<TableProps, 'ref'> & ExtraProps & TableMessageProps) => {
2327
const { className, ...rest } = props;
2428

2529
// This allows us to parse the nested data we get back from the 3rd party Markdown parser
@@ -72,7 +76,7 @@ const TableMessage = ({ children, ...props }: Omit<TableProps, 'ref'> & ExtraPro
7276
<Table
7377
aria-label={props['aria-label']}
7478
gridBreakPoint="grid"
75-
className={`pf-chatbot__message-table ${className ? className : ''}`}
79+
className={`pf-chatbot__message-table ${isPrimary ? 'pf-m-primary' : ''} ${className ? className : ''}`}
7680
{...rest}
7781
>
7882
{modifyChildren(children)}

0 commit comments

Comments
 (0)