@patternfly/chatbot 6.3.0-prerelease.16 → 6.3.0-prerelease.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +14 -3
- package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
- package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +136 -0
- package/dist/cjs/Message/Message.d.ts +16 -1
- package/dist/cjs/Message/Message.js +2 -1
- package/dist/cjs/Message/Message.test.js +30 -0
- package/dist/cjs/Message/Plugins/rehypeMoveImagesOutOfParagraphs.d.ts +2 -0
- package/dist/cjs/Message/Plugins/rehypeMoveImagesOutOfParagraphs.js +47 -0
- package/dist/css/main.css +10 -0
- package/dist/css/main.css.map +1 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +15 -4
- package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
- package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +130 -0
- package/dist/esm/Message/Message.d.ts +16 -1
- package/dist/esm/Message/Message.js +2 -1
- package/dist/esm/Message/Message.test.js +30 -0
- package/dist/esm/Message/Plugins/rehypeMoveImagesOutOfParagraphs.d.ts +2 -0
- package/dist/esm/Message/Plugins/rehypeMoveImagesOutOfParagraphs.js +43 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +10 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +51 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +9 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +9 -0
- package/patternfly-docs/content/extensions/chatbot/img/quick-response-confirmation.svg +67 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +7 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +99 -12
- package/src/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.tsx +220 -0
- package/src/Message/Message.test.tsx +39 -0
- package/src/Message/Message.tsx +19 -1
- package/src/Message/Plugins/rehypeMoveImagesOutOfParagraphs.ts +53 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
import { Component } from 'react';
|
2
|
+
import { PickOptional } from '@patternfly/react-core';
|
3
|
+
export declare enum ExpandableSectionVariant {
|
4
|
+
default = "default",
|
5
|
+
truncate = "truncate"
|
6
|
+
}
|
7
|
+
/** The main expandable section component. */
|
8
|
+
export interface ExpandableSectionProps extends Omit<React.HTMLProps<HTMLDivElement>, 'onToggle'> {
|
9
|
+
/** Content rendered inside the expandable section. */
|
10
|
+
children?: React.ReactNode;
|
11
|
+
/** Additional classes added to the expandable section. */
|
12
|
+
className?: string;
|
13
|
+
/** Id of the content of the expandable section. When passing in the isDetached property, this
|
14
|
+
* property's value should match the contentId property of the expandable section toggle sub-component.
|
15
|
+
*/
|
16
|
+
contentId?: string;
|
17
|
+
/** Id of the toggle of the expandable section, which provides an accessible name to the
|
18
|
+
* expandable section content via the aria-labelledby attribute. When the isDetached property
|
19
|
+
* is also passed in, the value of this property must match the toggleId property of the
|
20
|
+
* expandable section toggle sub-component.
|
21
|
+
*/
|
22
|
+
toggleId?: string;
|
23
|
+
/** Display size variant. Set to "lg" for disclosure styling. */
|
24
|
+
displaySize?: 'default' | 'lg';
|
25
|
+
/** Indicates the expandable section has a detached toggle. */
|
26
|
+
isDetached?: boolean;
|
27
|
+
/** Flag to indicate if the content is expanded. */
|
28
|
+
isExpanded?: boolean;
|
29
|
+
/** Flag to indicate if the content is indented. */
|
30
|
+
isIndented?: boolean;
|
31
|
+
/** Flag to indicate the width of the component is limited. Set to "true" for disclosure styling. */
|
32
|
+
isWidthLimited?: boolean;
|
33
|
+
/** Truncates the expandable content to the specified number of lines when using the
|
34
|
+
* "truncate" variant.
|
35
|
+
*/
|
36
|
+
truncateMaxLines?: number;
|
37
|
+
/** Determines the variant of the expandable section. When passing in "truncate" as the
|
38
|
+
* variant, the expandable content will be truncated after 3 lines by default.
|
39
|
+
*/
|
40
|
+
variant?: 'default' | 'truncate';
|
41
|
+
language?: string;
|
42
|
+
}
|
43
|
+
interface ExpandableSectionState {
|
44
|
+
isExpanded: boolean;
|
45
|
+
hasToggle: boolean;
|
46
|
+
previousWidth: number | undefined;
|
47
|
+
}
|
48
|
+
declare class ExpandableSectionForSyntaxHighlighter extends Component<ExpandableSectionProps, ExpandableSectionState> {
|
49
|
+
static displayName: string;
|
50
|
+
constructor(props: ExpandableSectionProps);
|
51
|
+
expandableContentRef: import("react").RefObject<HTMLDivElement>;
|
52
|
+
observer: any;
|
53
|
+
static defaultProps: PickOptional<ExpandableSectionProps>;
|
54
|
+
componentDidMount(): void;
|
55
|
+
componentDidUpdate(prevProps: ExpandableSectionProps): void;
|
56
|
+
componentWillUnmount(): void;
|
57
|
+
checkToggleVisibility: () => void;
|
58
|
+
resize: () => void;
|
59
|
+
handleResize: (...args: any[]) => void;
|
60
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
61
|
+
}
|
62
|
+
export { ExpandableSectionForSyntaxHighlighter };
|
@@ -0,0 +1,130 @@
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
2
|
+
var t = {};
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
4
|
+
t[p] = s[p];
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
8
|
+
t[p[i]] = s[p[i]];
|
9
|
+
}
|
10
|
+
return t;
|
11
|
+
};
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
13
|
+
import { Component, createRef } from 'react';
|
14
|
+
import styles from '@patternfly/react-styles/css/components/ExpandableSection/expandable-section';
|
15
|
+
import { css } from '@patternfly/react-styles';
|
16
|
+
import lineClamp from '@patternfly/react-tokens/dist/esm/c_expandable_section_m_truncate__content_LineClamp';
|
17
|
+
import { debounce, getResizeObserver, getUniqueId } from '@patternfly/react-core';
|
18
|
+
export var ExpandableSectionVariant;
|
19
|
+
(function (ExpandableSectionVariant) {
|
20
|
+
ExpandableSectionVariant["default"] = "default";
|
21
|
+
ExpandableSectionVariant["truncate"] = "truncate";
|
22
|
+
})(ExpandableSectionVariant || (ExpandableSectionVariant = {}));
|
23
|
+
const setLineClamp = (element, lines, language, isExpanded) => {
|
24
|
+
if (!element || !lines || lines < 1 || typeof isExpanded === 'undefined') {
|
25
|
+
return;
|
26
|
+
}
|
27
|
+
if (language) {
|
28
|
+
const selector = `.language-${language.toLowerCase()}`;
|
29
|
+
const childElement = element.querySelector(selector);
|
30
|
+
if (!childElement) {
|
31
|
+
return;
|
32
|
+
}
|
33
|
+
if (isExpanded) {
|
34
|
+
// Reset all truncation-related styles to their default values
|
35
|
+
childElement.style.removeProperty('-webkit-line-clamp');
|
36
|
+
childElement.style.removeProperty('display');
|
37
|
+
childElement.style.removeProperty('-webkit-box-orient');
|
38
|
+
childElement.style.removeProperty('overflow');
|
39
|
+
}
|
40
|
+
else {
|
41
|
+
childElement.style.setProperty('-webkit-line-clamp', lines.toString());
|
42
|
+
childElement.style.setProperty('display', '-webkit-box');
|
43
|
+
childElement.style.setProperty('-webkit-box-orient', 'vertical');
|
44
|
+
childElement.style.setProperty('overflow', 'hidden');
|
45
|
+
}
|
46
|
+
}
|
47
|
+
};
|
48
|
+
class ExpandableSectionForSyntaxHighlighter extends Component {
|
49
|
+
constructor(props) {
|
50
|
+
var _a;
|
51
|
+
super(props);
|
52
|
+
this.expandableContentRef = createRef();
|
53
|
+
/* eslint-disable-next-line */
|
54
|
+
this.observer = () => { };
|
55
|
+
this.checkToggleVisibility = () => {
|
56
|
+
var _a;
|
57
|
+
if ((_a = this.expandableContentRef) === null || _a === void 0 ? void 0 : _a.current) {
|
58
|
+
const maxLines = this.props.truncateMaxLines || parseInt(lineClamp.value);
|
59
|
+
const totalLines = this.expandableContentRef.current.scrollHeight /
|
60
|
+
parseInt(getComputedStyle(this.expandableContentRef.current).lineHeight);
|
61
|
+
this.setState({
|
62
|
+
hasToggle: totalLines > maxLines
|
63
|
+
});
|
64
|
+
}
|
65
|
+
};
|
66
|
+
this.resize = () => {
|
67
|
+
if (this.expandableContentRef.current) {
|
68
|
+
const { offsetWidth } = this.expandableContentRef.current;
|
69
|
+
if (this.state.previousWidth !== offsetWidth) {
|
70
|
+
this.setState({ previousWidth: offsetWidth });
|
71
|
+
this.checkToggleVisibility();
|
72
|
+
}
|
73
|
+
}
|
74
|
+
};
|
75
|
+
this.handleResize = debounce(this.resize, 250);
|
76
|
+
this.state = {
|
77
|
+
isExpanded: (_a = props.isExpanded) !== null && _a !== void 0 ? _a : false,
|
78
|
+
hasToggle: true,
|
79
|
+
previousWidth: undefined
|
80
|
+
};
|
81
|
+
}
|
82
|
+
componentDidMount() {
|
83
|
+
if (this.props.variant === ExpandableSectionVariant.truncate) {
|
84
|
+
const expandableContent = this.expandableContentRef.current;
|
85
|
+
if (expandableContent) {
|
86
|
+
this.setState({ previousWidth: expandableContent.offsetWidth });
|
87
|
+
this.observer = getResizeObserver(expandableContent, this.handleResize, false);
|
88
|
+
if (this.props.truncateMaxLines) {
|
89
|
+
setLineClamp(expandableContent, this.props.truncateMaxLines, this.props.language, this.state.isExpanded);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
this.checkToggleVisibility();
|
93
|
+
}
|
94
|
+
}
|
95
|
+
componentDidUpdate(prevProps) {
|
96
|
+
if (this.props.variant === ExpandableSectionVariant.truncate &&
|
97
|
+
(prevProps.truncateMaxLines !== this.props.truncateMaxLines ||
|
98
|
+
prevProps.children !== this.props.children ||
|
99
|
+
prevProps.isExpanded !== this.props.isExpanded)) {
|
100
|
+
const expandableContent = this.expandableContentRef.current;
|
101
|
+
setLineClamp(expandableContent, this.props.truncateMaxLines, this.props.language, this.props.isExpanded);
|
102
|
+
this.checkToggleVisibility();
|
103
|
+
}
|
104
|
+
}
|
105
|
+
componentWillUnmount() {
|
106
|
+
if (this.props.variant === ExpandableSectionVariant.truncate) {
|
107
|
+
this.observer();
|
108
|
+
}
|
109
|
+
}
|
110
|
+
render() {
|
111
|
+
const _a = this.props, { className, children, isExpanded, isDetached, displaySize, isWidthLimited, isIndented, contentId, toggleId, variant } = _a, props = __rest(_a, ["className", "children", "isExpanded", "isDetached", "displaySize", "isWidthLimited", "isIndented", "contentId", "toggleId", "variant"]);
|
112
|
+
if (isDetached && !toggleId) {
|
113
|
+
/* eslint-disable no-console */
|
114
|
+
console.warn('ExpandableSection: The toggleId value must be passed in and must match the toggleId of the ExpandableSectionToggle.');
|
115
|
+
}
|
116
|
+
const uniqueContentId = contentId || getUniqueId('expandable-section-content');
|
117
|
+
const uniqueToggleId = toggleId || getUniqueId('expandable-section-toggle');
|
118
|
+
return (_jsx("div", Object.assign({ className: css(styles.expandableSection, isExpanded && styles.modifiers.expanded, displaySize === 'lg' && styles.modifiers.displayLg, isWidthLimited && styles.modifiers.limitWidth, isIndented && styles.modifiers.indented, variant === ExpandableSectionVariant.truncate && styles.modifiers.truncate, className) }, props, { children: _jsx("div", { ref: this.expandableContentRef, className: css(styles.expandableSectionContent), hidden: variant !== ExpandableSectionVariant.truncate && !isExpanded, id: uniqueContentId, "aria-labelledby": uniqueToggleId, role: "region", children: children }) })));
|
119
|
+
}
|
120
|
+
}
|
121
|
+
ExpandableSectionForSyntaxHighlighter.displayName = 'ExpandableSection';
|
122
|
+
ExpandableSectionForSyntaxHighlighter.defaultProps = {
|
123
|
+
className: '',
|
124
|
+
isDetached: false,
|
125
|
+
displaySize: 'default',
|
126
|
+
isWidthLimited: false,
|
127
|
+
isIndented: false,
|
128
|
+
variant: 'default'
|
129
|
+
};
|
130
|
+
export { ExpandableSectionForSyntaxHighlighter };
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { ReactNode } from 'react';
|
2
2
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
3
|
-
import { AlertProps, AvatarProps, ButtonProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
|
3
|
+
import { AlertProps, AvatarProps, ButtonProps, ExpandableSectionProps, ExpandableSectionToggleProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
|
4
4
|
import { ActionProps } from '../ResponseActions/ResponseActions';
|
5
5
|
import { SourcesCardProps } from '../SourcesCard';
|
6
6
|
import { QuickStart, QuickstartAction } from './QuickStarts/types';
|
@@ -64,9 +64,24 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
64
64
|
botWord?: string;
|
65
65
|
/** Label for the English "Loading message," displayed to screenreaders when loading a message */
|
66
66
|
loadingWord?: string;
|
67
|
+
/** Props for code blocks */
|
67
68
|
codeBlockProps?: {
|
69
|
+
/** Aria label applied to code blocks */
|
68
70
|
'aria-label'?: string;
|
71
|
+
/** Class name applied to code blocks */
|
69
72
|
className?: string;
|
73
|
+
/** Whether code blocks are expandable */
|
74
|
+
isExpandable?: boolean;
|
75
|
+
/** Length of text initially shown in expandable code blocks; defaults to 10 characters */
|
76
|
+
maxLength?: number;
|
77
|
+
/** Additional props passed to expandable section if isExpandable is applied */
|
78
|
+
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
79
|
+
/** Additional props passed to expandable toggle if isExpandable is applied */
|
80
|
+
expandableSectionToggleProps?: ExpandableSectionToggleProps;
|
81
|
+
/** Link text applied to expandable toggle when expanded */
|
82
|
+
expandedText?: string;
|
83
|
+
/** Link text applied to expandable toggle when collapsed */
|
84
|
+
collapsedText?: string;
|
70
85
|
};
|
71
86
|
/** Props for quick responses */
|
72
87
|
quickResponses?: QuickResponse[];
|
@@ -43,6 +43,7 @@ import rehypeSanitize from 'rehype-sanitize';
|
|
43
43
|
import LinkMessage from './LinkMessage/LinkMessage';
|
44
44
|
import ErrorMessage from './ErrorMessage/ErrorMessage';
|
45
45
|
import MessageInput from './MessageInput';
|
46
|
+
import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
|
46
47
|
export const MessageBase = (_a) => {
|
47
48
|
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, editFormProps, isCompact } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "editFormProps", "isCompact"]);
|
48
49
|
const [messageText, setMessageText] = useState(content);
|
@@ -50,7 +51,7 @@ export const MessageBase = (_a) => {
|
|
50
51
|
setMessageText(content);
|
51
52
|
}, [content]);
|
52
53
|
const { beforeMainContent, afterMainContent, endContent } = extraContent || {};
|
53
|
-
let rehypePlugins = [rehypeUnwrapImages];
|
54
|
+
let rehypePlugins = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs];
|
54
55
|
if (openLinkInNewTab) {
|
55
56
|
rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
|
56
57
|
}
|
@@ -136,6 +136,7 @@ const EMPTY_TABLE = `
|
|
136
136
|
|
137
137
|
`;
|
138
138
|
const IMAGE = ``;
|
139
|
+
const INLINE_IMAGE = `inline text `;
|
139
140
|
const ERROR = {
|
140
141
|
title: 'Could not load chat',
|
141
142
|
children: 'Wait a few minutes and check your network settings. If the issue persists: ',
|
@@ -402,6 +403,30 @@ describe('Message', () => {
|
|
402
403
|
expect(screen.getByText(/url:/i)).toBeTruthy();
|
403
404
|
expect(screen.getByText(/https:\/\/raw.githubusercontent.com\/Azure-Samples\/helm-charts\/master\/docs/i)).toBeTruthy();
|
404
405
|
});
|
406
|
+
it('should render expandable code correctly', () => {
|
407
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, codeBlockProps: { isExpandable: true } }));
|
408
|
+
expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
|
409
|
+
expect(screen.getByRole('button', { name: 'Copy code' })).toBeTruthy();
|
410
|
+
expect(screen.getByText(/yaml/)).toBeTruthy();
|
411
|
+
expect(screen.getByText(/apiVersion/i)).toBeTruthy();
|
412
|
+
expect(screen.getByRole('button', { name: /Show more/i })).toBeTruthy();
|
413
|
+
});
|
414
|
+
it('should handle click on expandable code correctly', () => __awaiter(void 0, void 0, void 0, function* () {
|
415
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, codeBlockProps: { isExpandable: true } }));
|
416
|
+
const button = screen.getByRole('button', { name: /Show more/i });
|
417
|
+
yield userEvent.click(button);
|
418
|
+
expect(screen.getByRole('button', { name: /Show less/i })).toBeTruthy();
|
419
|
+
expect(screen.getByText(/yaml/)).toBeTruthy();
|
420
|
+
expect(screen.getByText(/apiVersion:/i)).toBeTruthy();
|
421
|
+
expect(screen.getByText(/helm.openshift.io\/v1beta1/i)).toBeTruthy();
|
422
|
+
expect(screen.getByText(/metadata:/i)).toBeTruthy();
|
423
|
+
expect(screen.getByText(/name:/i)).toBeTruthy();
|
424
|
+
expect(screen.getByText(/azure-sample-repo0oooo00ooo/i)).toBeTruthy();
|
425
|
+
expect(screen.getByText(/spec/i)).toBeTruthy();
|
426
|
+
expect(screen.getByText(/connectionConfig:/i)).toBeTruthy();
|
427
|
+
expect(screen.getByText(/url:/i)).toBeTruthy();
|
428
|
+
expect(screen.getByText(/https:\/\/raw.githubusercontent.com\/Azure-Samples\/helm-charts\/master\/docs/i)).toBeTruthy();
|
429
|
+
}));
|
405
430
|
it('can click copy code button', () => __awaiter(void 0, void 0, void 0, function* () {
|
406
431
|
// need explicit setup since RTL stubs clipboard if you do this
|
407
432
|
const user = userEvent.setup();
|
@@ -595,6 +620,11 @@ describe('Message', () => {
|
|
595
620
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: IMAGE }));
|
596
621
|
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
597
622
|
});
|
623
|
+
it('inline image parent should have class pf-chatbot__message-and-actions', () => {
|
624
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: INLINE_IMAGE }));
|
625
|
+
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i })).toBeTruthy();
|
626
|
+
expect(screen.getByRole('img', { name: /Multi-colored wavy lines on a black background/i }).parentElement).toHaveClass('pf-chatbot__message-and-actions');
|
627
|
+
});
|
598
628
|
it('should handle external links correctly', () => {
|
599
629
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: `[PatternFly](https://www.patternfly.org/)` }));
|
600
630
|
// we are mocking rehype libraries, so we can't test target _blank addition on links directly with RTL
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
2
|
+
// Rehype plugin to remove images from within p tags and put them as separate block-level elements.
|
3
|
+
// This allows us to avoid having a blue background on images - this is something Kayla requested.
|
4
|
+
export const rehypeMoveImagesOutOfParagraphs = () => (tree) => {
|
5
|
+
const nodesToRemove = [];
|
6
|
+
visit(tree, 'element', (node, index, parent) => {
|
7
|
+
if (node.tagName === 'p' && node.children) {
|
8
|
+
const imagesInParagraph = [];
|
9
|
+
node.children.forEach((child, childIndex) => {
|
10
|
+
if (child.type === 'element' && child.tagName === 'img') {
|
11
|
+
imagesInParagraph.push({ node: child, index: childIndex });
|
12
|
+
}
|
13
|
+
});
|
14
|
+
if (imagesInParagraph.length > 0 && parent && index !== null) {
|
15
|
+
imagesInParagraph.forEach(({ node: imgNode, index: imgIndex }) => {
|
16
|
+
nodesToRemove.push({ parent: node, index: imgIndex, node: imgNode });
|
17
|
+
});
|
18
|
+
// To avoid issues with index shifting during removal, we process in reverse
|
19
|
+
for (let i = nodesToRemove.length - 1; i >= 0; i--) {
|
20
|
+
const { parent: pTag, index: imgIndexToRemove } = nodesToRemove[i];
|
21
|
+
if (pTag.children) {
|
22
|
+
pTag.children.splice(imgIndexToRemove, 1);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
// Insert the removed images after the paragraph
|
26
|
+
const paragraphIndexInParent = parent.children.indexOf(node);
|
27
|
+
if (paragraphIndexInParent !== -1) {
|
28
|
+
imagesInParagraph.forEach(({ node: imgNode }) => {
|
29
|
+
parent.children.splice(paragraphIndexInParent + 1, 0, imgNode);
|
30
|
+
});
|
31
|
+
}
|
32
|
+
// Remove paragraph if it's now empty after image removal
|
33
|
+
if (node.children.length === 0) {
|
34
|
+
const paragraphIndexInParent = parent.children.indexOf(node);
|
35
|
+
if (paragraphIndexInParent !== -1) {
|
36
|
+
parent.children.splice(paragraphIndexInParent, 1);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
nodesToRemove.length = 0;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
});
|
43
|
+
};
|
@@ -1 +1 @@
|
|
1
|
-
{"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/EmptyState.tsx","../src/ChatbotConversationHistoryNav/LoadingState.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.test.tsx","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.test.tsx","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageInput.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ErrorMessage/ErrorMessage.tsx","../src/Message/ImageMessage/ImageMessage.tsx","../src/Message/LinkMessage/LinkMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-sanitize.ts","../src/__mocks__/rehype-unwrap-images.tsx","../src/tracking/console_tracking_provider.ts","../src/tracking/index.ts","../src/tracking/posthog_tracking_provider.ts","../src/tracking/segment_tracking_provider.ts","../src/tracking/trackingProviderProxy.ts","../src/tracking/tracking_api.ts","../src/tracking/tracking_registry.ts","../src/tracking/tracking_spi.ts","../src/tracking/umami_tracking_provider.ts"],"version":"5.6.3"}
|
1
|
+
{"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/EmptyState.tsx","../src/ChatbotConversationHistoryNav/LoadingState.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.test.tsx","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.test.tsx","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageInput.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.tsx","../src/Message/ErrorMessage/ErrorMessage.tsx","../src/Message/ImageMessage/ImageMessage.tsx","../src/Message/LinkMessage/LinkMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/Plugins/rehypeMoveImagesOutOfParagraphs.ts","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-sanitize.ts","../src/__mocks__/rehype-unwrap-images.tsx","../src/tracking/console_tracking_provider.ts","../src/tracking/index.ts","../src/tracking/posthog_tracking_provider.ts","../src/tracking/segment_tracking_provider.ts","../src/tracking/trackingProviderProxy.ts","../src/tracking/tracking_api.ts","../src/tracking/tracking_registry.ts","../src/tracking/tracking_spi.ts","../src/tracking/umami_tracking_provider.ts"],"version":"5.6.3"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@patternfly/chatbot",
|
3
|
-
"version": "6.3.0-prerelease.
|
3
|
+
"version": "6.3.0-prerelease.18",
|
4
4
|
"description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
|
5
5
|
"main": "dist/cjs/index.js",
|
6
6
|
"module": "dist/esm/index.js",
|
@@ -45,7 +45,8 @@
|
|
45
45
|
"rehype-unwrap-images": "^1.0.0",
|
46
46
|
"rehype-external-links": "^3.0.0",
|
47
47
|
"rehype-sanitize": "^6.0.0",
|
48
|
-
"path-browserify": "^1.0.1"
|
48
|
+
"path-browserify": "^1.0.1",
|
49
|
+
"unist-util-visit": "^5.0.0"
|
49
50
|
},
|
50
51
|
"peerDependencies": {
|
51
52
|
"react": "^17 || ^18 || ^19",
|
@@ -261,6 +261,16 @@ Each time a user begins a new conversation, display a [welcome message, with pro
|
|
261
261
|
|
262
262
|
As much as possible, the suggested prompts should consider the user’s location in the service or application, or the situation their project is undergoing.
|
263
263
|
|
264
|
+
### Executing user requests
|
265
|
+
|
266
|
+
Instead of automatically initiating what sounds like a request from a user, the ChatBot should ask users to confirm or deny their intent. This differentiates moments where users are simply asking about feasibility, rather than actually making a request.
|
267
|
+
|
268
|
+
This can be done using the [quick response](/patternfly-ai/chatbot/messages#messages-with-quick-responses) buttons:
|
269
|
+
|
270
|
+
<div class="ws-docs-content-img">
|
271
|
+

|
272
|
+
</div>
|
273
|
+
|
264
274
|
### Using the conversation history menu
|
265
275
|
|
266
276
|
The ChatBot history menu contains a log of a users' previous chats. Clicking the menu icon opens a side drawer in the ChatBot window.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
---
|
2
|
+
# Sidenav top-level section
|
3
|
+
# should be the same for all markdown files
|
4
|
+
section: PatternFly-AI
|
5
|
+
subsection: ChatBot
|
6
|
+
# Sidenav secondary level section
|
7
|
+
# should be the same for all markdown files
|
8
|
+
id: Customizing messages
|
9
|
+
source: Customizing messages
|
10
|
+
# Tab (react | react-demos | html | html-demos | design-guidelines | accessibility)
|
11
|
+
# If you use typescript, the name of the interface to display props for
|
12
|
+
# These are found through the sourceProps function provided in patternfly-docs.source.js
|
13
|
+
sortValue: 60
|
14
|
+
---
|
15
|
+
|
16
|
+
## Modifying static content
|
17
|
+
|
18
|
+
The ChatBot extension `<Message>` component transforms Markdown to PatternFly React components via [react-markdown](https://github.com/remarkjs/react-markdown), which supports both [rehype](https://unifiedjs.com/explore/package/rehype/) and [remark](https://unifiedjs.com/explore/package/remark/) plugins for further output customization.
|
19
|
+
|
20
|
+
remark parses Markdown as input and serializes Markdown as output, while rehype does the same for HTML. This allows you to target and make transformations at specific checkpoints in the transformation process: either while the text tree is still in Markdown, or while the text tree is in HTML format. For more detailed information on the architecture of this library, see the [react-markdown documentation](https://github.com/remarkjs/react-markdown?tab=readme-ov-file#architecture).
|
21
|
+
|
22
|
+
You may need these plugins if, for example, you wanted to change the `href` on a link based on the link text. The `<Message>` prop `rehypePlugins` will accept any custom rehype plugins. We use already use these plugins in ChatBot to [render images a certain way](https://www.npmjs.com/package/rehype-unwrap-images) and [handle external links](https://www.npmjs.com/package/rehype-external-links).
|
23
|
+
|
24
|
+
There are many open source libraries available for other common customization needs. If you can't find one for your use case, [Unified](https://unifiedjs.com/) has a helpful [guide on creating custom plugins](https://unifiedjs.com/learn/guide/create-a-rehype-plugin/).
|
25
|
+
For a more direct example, if we wanted to change the `href` on links that included the text string `react`, we could write a very basic plugin like this:
|
26
|
+
|
27
|
+
```
|
28
|
+
import { visit } from 'unist-util-visit';
|
29
|
+
|
30
|
+
export const rehypeLinkHrefUpdater = (options) => (tree) => {
|
31
|
+
visit(tree, 'element', (node) => {
|
32
|
+
if (node.tagName === 'a' && node.properties) {
|
33
|
+
if (node.properties.href.includes('react')) {
|
34
|
+
node.properties.href = '#';
|
35
|
+
}
|
36
|
+
}
|
37
|
+
});
|
38
|
+
};
|
39
|
+
|
40
|
+
```
|
41
|
+
|
42
|
+
You could then pass `[rehypeLinkHrefUpdater]` to the `additionalRehypePlugins` prop on `<Message>` to have this applied to all relevant links read by that `<Message>` component.
|
43
|
+
|
44
|
+
## Modifying dynamic content
|
45
|
+
|
46
|
+
Props can also be used to modify dynamic content in messages.
|
47
|
+
|
48
|
+
Continuing with our link modification example, let's say you also wanted to add custom `onClick` events. In this case, you would not use `remark` or `rehype`, since they're for static tree manipulation.
|
49
|
+
|
50
|
+
Instead, you can utilize ChatBot features to customize dynamic content. We offer many `<Message>` props that allow you to implement custom behavior.
|
51
|
+
For example, you can use `linkProps` to pass `onClick` events and other PatternFly `<Button>` props down to links. A very basic example would be `linkProps={{onClick: (event) => console.log(event}}`, which would apply to every link in the `<Message>`. Or, you could use the `event.target`, adding your own custom `onClick` logic based on the `innerText`, `innerHTML`, or other attributes of the `event.target`.
|
@@ -15,11 +15,13 @@ export const BotMessageExample: FunctionComponent = () => {
|
|
15
15
|
const [variant, setVariant] = useState<string>('Code');
|
16
16
|
const [isOpen, setIsOpen] = useState(false);
|
17
17
|
const [selected, setSelected] = useState<string>('Message content type');
|
18
|
+
const [isExpandable, setIsExpanded] = useState(false);
|
18
19
|
|
19
20
|
/* eslint-disable indent */
|
20
21
|
const renderContent = () => {
|
21
22
|
switch (variant) {
|
22
23
|
case 'Code':
|
24
|
+
case 'Expandable code':
|
23
25
|
return code;
|
24
26
|
case 'Heading':
|
25
27
|
return heading;
|
@@ -166,6 +168,11 @@ _Italic text, formatted with single underscores_
|
|
166
168
|
setVariant(value);
|
167
169
|
setSelected(value as string);
|
168
170
|
setIsOpen(false);
|
171
|
+
if (value === 'Expandable code') {
|
172
|
+
setIsExpanded(true);
|
173
|
+
} else {
|
174
|
+
setIsExpanded(false);
|
175
|
+
}
|
169
176
|
};
|
170
177
|
|
171
178
|
const onToggleClick = () => {
|
@@ -237,6 +244,7 @@ _Italic text, formatted with single underscores_
|
|
237
244
|
>
|
238
245
|
<SelectList>
|
239
246
|
<SelectOption value="Code">Code</SelectOption>
|
247
|
+
<SelectOption value="Expandable code">Expandable code</SelectOption>
|
240
248
|
<SelectOption value="Inline code">Inline code</SelectOption>
|
241
249
|
<SelectOption value="Heading">Heading</SelectOption>
|
242
250
|
<SelectOption value="Block quotes">Block quotes</SelectOption>
|
@@ -259,6 +267,7 @@ _Italic text, formatted with single underscores_
|
|
259
267
|
variant === 'Table' ? { 'aria-label': 'App information and user roles for bot messages' } : undefined
|
260
268
|
}
|
261
269
|
error={variant === 'Error' ? error : undefined}
|
270
|
+
codeBlockProps={{ isExpandable, expandableSectionProps: { truncateMaxLines: isExpandable ? 1 : undefined } }}
|
262
271
|
/>
|
263
272
|
</>
|
264
273
|
);
|
@@ -15,11 +15,13 @@ export const UserMessageExample: FunctionComponent = () => {
|
|
15
15
|
const [isEditable, setIsEditable] = useState<boolean>(true);
|
16
16
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
17
17
|
const [selected, setSelected] = useState<string>('Message content type');
|
18
|
+
const [isExpandable, setIsExpanded] = useState(false);
|
18
19
|
|
19
20
|
/* eslint-disable indent */
|
20
21
|
const renderContent = () => {
|
21
22
|
switch (variant) {
|
22
23
|
case 'Code':
|
24
|
+
case 'Expandable code':
|
23
25
|
return code;
|
24
26
|
case 'Inline code':
|
25
27
|
return inlineCode;
|
@@ -166,6 +168,11 @@ _Italic text, formatted with single underscores_
|
|
166
168
|
setVariant(value);
|
167
169
|
setSelected(value as string);
|
168
170
|
setIsOpen(false);
|
171
|
+
if (value === 'Expandable code') {
|
172
|
+
setIsExpanded(true);
|
173
|
+
} else {
|
174
|
+
setIsExpanded(false);
|
175
|
+
}
|
169
176
|
};
|
170
177
|
|
171
178
|
const onToggleClick = () => {
|
@@ -215,6 +222,7 @@ _Italic text, formatted with single underscores_
|
|
215
222
|
>
|
216
223
|
<SelectList>
|
217
224
|
<SelectOption value="Code">Code</SelectOption>
|
225
|
+
<SelectOption value="Expandable code">Expandable code</SelectOption>
|
218
226
|
<SelectOption value="Inline code">Inline code</SelectOption>
|
219
227
|
<SelectOption value="Heading">Heading</SelectOption>
|
220
228
|
<SelectOption value="Block quotes">Block quotes</SelectOption>
|
@@ -241,6 +249,7 @@ _Italic text, formatted with single underscores_
|
|
241
249
|
error={variant === 'Error' ? error : undefined}
|
242
250
|
onEditUpdate={() => setIsEditable(false)}
|
243
251
|
onEditCancel={() => setIsEditable(false)}
|
252
|
+
codeBlockProps={{ isExpandable, expandableSectionProps: { truncateMaxLines: isExpandable ? 1 : undefined } }}
|
244
253
|
/>
|
245
254
|
</>
|
246
255
|
);
|