@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.
Files changed (33) hide show
  1. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
  2. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +14 -3
  3. package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
  4. package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +136 -0
  5. package/dist/cjs/Message/Message.d.ts +16 -1
  6. package/dist/cjs/Message/Message.js +2 -1
  7. package/dist/cjs/Message/Message.test.js +30 -0
  8. package/dist/cjs/Message/Plugins/rehypeMoveImagesOutOfParagraphs.d.ts +2 -0
  9. package/dist/cjs/Message/Plugins/rehypeMoveImagesOutOfParagraphs.js +47 -0
  10. package/dist/css/main.css +10 -0
  11. package/dist/css/main.css.map +1 -1
  12. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
  13. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +15 -4
  14. package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
  15. package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +130 -0
  16. package/dist/esm/Message/Message.d.ts +16 -1
  17. package/dist/esm/Message/Message.js +2 -1
  18. package/dist/esm/Message/Message.test.js +30 -0
  19. package/dist/esm/Message/Plugins/rehypeMoveImagesOutOfParagraphs.d.ts +2 -0
  20. package/dist/esm/Message/Plugins/rehypeMoveImagesOutOfParagraphs.js +43 -0
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/package.json +3 -2
  23. package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +10 -0
  24. package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +51 -0
  25. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +9 -0
  26. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +9 -0
  27. package/patternfly-docs/content/extensions/chatbot/img/quick-response-confirmation.svg +67 -0
  28. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +7 -0
  29. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +99 -12
  30. package/src/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.tsx +220 -0
  31. package/src/Message/Message.test.tsx +39 -0
  32. package/src/Message/Message.tsx +19 -1
  33. 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 = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
139
+ const INLINE_IMAGE = `inline text ![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`;
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,2 @@
1
+ import { Node } from 'unist';
2
+ export declare const rehypeMoveImagesOutOfParagraphs: () => (tree: Node) => void;
@@ -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.16",
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
+ ![Confirmation options from a bot in response to a user's request.](./img/quick-response-confirmation.svg)
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
  );