@patternfly/chatbot 6.3.0-prerelease.17 → 6.3.0-prerelease.19

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 (48) hide show
  1. package/dist/cjs/FileDropZone/FileDropZone.d.ts +7 -0
  2. package/dist/cjs/FileDropZone/FileDropZone.js +2 -2
  3. package/dist/cjs/FileDropZone/FileDropZone.test.js +26 -0
  4. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
  5. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +14 -3
  6. package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
  7. package/dist/cjs/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +136 -0
  8. package/dist/cjs/Message/Message.d.ts +16 -1
  9. package/dist/cjs/Message/Message.test.js +24 -0
  10. package/dist/cjs/MessageBar/AttachButton.d.ts +7 -0
  11. package/dist/cjs/MessageBar/AttachButton.js +3 -2
  12. package/dist/cjs/MessageBar/AttachButton.test.js +24 -0
  13. package/dist/cjs/MessageBar/MessageBar.d.ts +7 -0
  14. package/dist/cjs/MessageBar/MessageBar.js +2 -2
  15. package/dist/css/main.css +10 -0
  16. package/dist/css/main.css.map +1 -1
  17. package/dist/esm/FileDropZone/FileDropZone.d.ts +7 -0
  18. package/dist/esm/FileDropZone/FileDropZone.js +2 -2
  19. package/dist/esm/FileDropZone/FileDropZone.test.js +26 -0
  20. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +20 -2
  21. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +15 -4
  22. package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.d.ts +62 -0
  23. package/dist/esm/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.js +130 -0
  24. package/dist/esm/Message/Message.d.ts +16 -1
  25. package/dist/esm/Message/Message.test.js +24 -0
  26. package/dist/esm/MessageBar/AttachButton.d.ts +7 -0
  27. package/dist/esm/MessageBar/AttachButton.js +3 -2
  28. package/dist/esm/MessageBar/AttachButton.test.js +24 -0
  29. package/dist/esm/MessageBar/MessageBar.d.ts +7 -0
  30. package/dist/esm/MessageBar/MessageBar.js +2 -2
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +1 -1
  33. package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +10 -0
  34. package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +51 -0
  35. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +9 -0
  36. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +9 -0
  37. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachment.tsx +10 -1
  38. package/patternfly-docs/content/extensions/chatbot/img/quick-response-confirmation.svg +67 -0
  39. package/src/FileDropZone/FileDropZone.test.tsx +29 -0
  40. package/src/FileDropZone/FileDropZone.tsx +9 -0
  41. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +7 -0
  42. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +99 -12
  43. package/src/Message/CodeBlockMessage/ExpandableSectionForSyntaxHighlighter.tsx +220 -0
  44. package/src/Message/Message.test.tsx +30 -0
  45. package/src/Message/Message.tsx +17 -0
  46. package/src/MessageBar/AttachButton.test.tsx +45 -0
  47. package/src/MessageBar/AttachButton.tsx +10 -2
  48. package/src/MessageBar/MessageBar.tsx +10 -0
@@ -1,6 +1,7 @@
1
1
  import { DropEvent } from '@patternfly/react-core';
2
2
  import type { FunctionComponent } from 'react';
3
3
  import { ChatbotDisplayMode } from '../Chatbot';
4
+ import { Accept } from 'react-dropzone/.';
4
5
  export interface FileDropZoneProps {
5
6
  /** Content displayed when the drop zone is not currently in use */
6
7
  children?: React.ReactNode;
@@ -10,6 +11,12 @@ export interface FileDropZoneProps {
10
11
  infoText?: string;
11
12
  /** When files are dropped or uploaded this callback will be called with all accepted files */
12
13
  onFileDrop: (event: DropEvent, data: File[]) => void;
14
+ /** Specifies the file types accepted by the attachment upload component.
15
+ * Files that don't match the accepted types will be disabled in the file picker.
16
+ * For example,
17
+ * allowedFileTypes: { 'application/json': ['.json'], 'text/plain': ['.txt'] }
18
+ **/
19
+ allowedFileTypes?: Accept;
13
20
  /** Display mode for the Chatbot parent; this influences the styles applied */
14
21
  displayMode?: ChatbotDisplayMode;
15
22
  }
@@ -15,9 +15,9 @@ import { useState } from 'react';
15
15
  import { ChatbotDisplayMode } from '../Chatbot';
16
16
  import { UploadIcon } from '@patternfly/react-icons';
17
17
  const FileDropZone = (_a) => {
18
- var { children, className, infoText = 'Maximum file size is 25 MB', onFileDrop, displayMode = ChatbotDisplayMode.default } = _a, props = __rest(_a, ["children", "className", "infoText", "onFileDrop", "displayMode"]);
18
+ var { children, className, infoText = 'Maximum file size is 25 MB', onFileDrop, allowedFileTypes, displayMode = ChatbotDisplayMode.default } = _a, props = __rest(_a, ["children", "className", "infoText", "onFileDrop", "allowedFileTypes", "displayMode"]);
19
19
  const [showDropZone, setShowDropZone] = useState(false);
20
20
  const renderDropZone = () => (_jsx(_Fragment, { children: _jsx(MultipleFileUploadMain, { titleIcon: _jsx(UploadIcon, {}), titleText: "Drag and drop your file here", infoText: infoText, isUploadButtonHidden: true }) }));
21
- return (_jsx(MultipleFileUpload, { dropzoneProps: Object.assign({ onDrop: () => setShowDropZone(false) }, props), onDragEnter: () => setShowDropZone(true), onDragLeave: () => setShowDropZone(false), onFileDrop: onFileDrop, className: `pf-chatbot__dropzone pf-chatbot__dropzone--${displayMode} pf-chatbot__dropzone--${showDropZone ? 'visible' : 'invisible'} ${className ? className : ''}`, children: showDropZone ? renderDropZone() : children }));
21
+ return (_jsx(MultipleFileUpload, { dropzoneProps: Object.assign({ accept: allowedFileTypes, onDrop: () => setShowDropZone(false) }, props), onDragEnter: () => setShowDropZone(true), onDragLeave: () => setShowDropZone(false), onFileDrop: onFileDrop, className: `pf-chatbot__dropzone pf-chatbot__dropzone--${displayMode} pf-chatbot__dropzone--${showDropZone ? 'visible' : 'invisible'} ${className ? className : ''}`, children: showDropZone ? renderDropZone() : children }));
22
22
  };
23
23
  export default FileDropZone;
@@ -1,7 +1,17 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { jsx as _jsx } from "react/jsx-runtime";
2
11
  import { render, screen } from '@testing-library/react';
3
12
  import '@testing-library/jest-dom';
4
13
  import FileDropZone from './FileDropZone';
14
+ import userEvent from '@testing-library/user-event';
5
15
  describe('FileDropZone', () => {
6
16
  it('should render file drop zone', () => {
7
17
  const { container } = render(_jsx(FileDropZone, { onFileDrop: jest.fn() }));
@@ -11,4 +21,20 @@ describe('FileDropZone', () => {
11
21
  render(_jsx(FileDropZone, { onFileDrop: jest.fn(), children: "Hi" }));
12
22
  expect(screen.getByText('Hi')).toBeTruthy();
13
23
  });
24
+ it('should call onFileDrop when file type is accepted', () => __awaiter(void 0, void 0, void 0, function* () {
25
+ const onFileDrop = jest.fn();
26
+ const { container } = render(_jsx(FileDropZone, { "data-testid": "input", allowedFileTypes: { 'text/plain': ['.txt'] }, onFileDrop: onFileDrop }));
27
+ const file = new File(['Test'], 'example.text', { type: 'text/plain' });
28
+ const fileInput = container.querySelector('input[type="file"]');
29
+ yield userEvent.upload(fileInput, file);
30
+ expect(onFileDrop).toHaveBeenCalled();
31
+ }));
32
+ it('should not call onFileDrop when file type is not accepted', () => __awaiter(void 0, void 0, void 0, function* () {
33
+ const onFileDrop = jest.fn();
34
+ const { container } = render(_jsx(FileDropZone, { "data-testid": "input", allowedFileTypes: { 'text/plain': ['.txt'] }, onFileDrop: onFileDrop }));
35
+ const file = new File(['[]'], 'example.json', { type: 'application/json' });
36
+ const fileInput = container.querySelector('input[type="file"]');
37
+ yield userEvent.upload(fileInput, file);
38
+ expect(onFileDrop).not.toHaveBeenCalled();
39
+ }));
14
40
  });
@@ -1,3 +1,21 @@
1
- import { ExtraProps } from 'react-markdown';
2
- declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, ...props }: Omit<JSX.IntrinsicElements["code"], "ref"> & ExtraProps) => import("react/jsx-runtime").JSX.Element;
1
+ import { ExpandableSectionProps, ExpandableSectionToggleProps } from '@patternfly/react-core';
2
+ export interface CodeBlockMessageProps {
3
+ /** Content rendered in code block */
4
+ children?: React.ReactNode;
5
+ /** Aria label applied to code block */
6
+ 'aria-label'?: string;
7
+ /** Class name applied to code block */
8
+ className?: string;
9
+ /** Whether code block is expandable */
10
+ isExpandable?: boolean;
11
+ /** Additional props passed to expandable section if isExpandable is applied */
12
+ expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
13
+ /** Additional props passed to expandable toggle if isExpandable is applied */
14
+ expandableSectionToggleProps?: ExpandableSectionToggleProps;
15
+ /** Link text applied to expandable toggle when expanded */
16
+ expandedText?: string;
17
+ /** Link text applied to expandable toggle when collapsed */
18
+ collapsedText?: string;
19
+ }
20
+ declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, ...props }: CodeBlockMessageProps) => import("react/jsx-runtime").JSX.Element;
3
21
  export default CodeBlockMessage;
@@ -17,16 +17,24 @@ import { useState, useRef, useId, useCallback, useEffect } from 'react';
17
17
  import SyntaxHighlighter from 'react-syntax-highlighter';
18
18
  import { obsidian } from 'react-syntax-highlighter/dist/esm/styles/hljs';
19
19
  // Import PatternFly components
20
- import { CodeBlock, CodeBlockAction, CodeBlockCode, Button, Tooltip } from '@patternfly/react-core';
20
+ import { CodeBlock, CodeBlockAction, CodeBlockCode, Button, Tooltip, ExpandableSection, ExpandableSectionToggle, ExpandableSectionVariant } from '@patternfly/react-core';
21
21
  import { CheckIcon } from '@patternfly/react-icons/dist/esm/icons/check-icon';
22
22
  import { CopyIcon } from '@patternfly/react-icons/dist/esm/icons/copy-icon';
23
+ import { ExpandableSectionForSyntaxHighlighter } from './ExpandableSectionForSyntaxHighlighter';
23
24
  const CodeBlockMessage = (_a) => {
24
25
  var _b;
25
- var { children, className, 'aria-label': ariaLabel } = _a, props = __rest(_a, ["children", "className", 'aria-label']);
26
+ var { children, className, 'aria-label': ariaLabel, isExpandable = false, expandableSectionProps, expandableSectionToggleProps, expandedText = 'Show less', collapsedText = 'Show more' } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText"]);
26
27
  const [copied, setCopied] = useState(false);
27
- const buttonRef = useRef(undefined);
28
+ const [isExpanded, setIsExpanded] = useState(false);
29
+ const buttonRef = useRef();
28
30
  const tooltipID = useId();
31
+ const toggleId = useId();
32
+ const contentId = useId();
33
+ const codeBlockRef = useRef(null);
29
34
  const language = (_b = /language-(\w+)/.exec(className || '')) === null || _b === void 0 ? void 0 : _b[1];
35
+ const onToggle = (isExpanded) => {
36
+ setIsExpanded(isExpanded);
37
+ };
30
38
  // Handle clicking copy button
31
39
  const handleCopy = useCallback((event, text) => {
32
40
  navigator.clipboard.writeText(text.toString());
@@ -46,6 +54,9 @@ const CodeBlockMessage = (_a) => {
46
54
  }
47
55
  // Setup code block header
48
56
  const actions = (_jsx(_Fragment, { children: _jsxs(CodeBlockAction, { children: [language && _jsx("div", { className: "pf-chatbot__message-code-block-language", children: language }), _jsx(Button, { ref: buttonRef, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : 'Copy code', variant: "plain", className: "pf-chatbot__button--copy", onClick: (event) => handleCopy(event, children), children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx(Tooltip, { id: tooltipID, content: "Copy", position: "top", triggerRef: buttonRef })] }) }));
49
- return (_jsx("div", { className: "pf-chatbot__message-code-block", children: _jsx(CodeBlock, { actions: actions, children: _jsx(CodeBlockCode, { children: language ? (_jsx(SyntaxHighlighter, Object.assign({}, props, { language: language, style: obsidian, PreTag: "div", CodeTag: "div", wrapLongLines: true, children: String(children).replace(/\n$/, '') }))) : (_jsx(_Fragment, { children: children })) }) }) }));
57
+ return (_jsx("div", { className: "pf-chatbot__message-code-block", ref: codeBlockRef, children: _jsxs(CodeBlock, { actions: actions, children: [_jsx(CodeBlockCode, { children: _jsx(_Fragment, { children: language ? (
58
+ // SyntaxHighlighter doesn't work with ExpandableSection because it targets the direct child
59
+ // Forked for now and adjusted to match what we need
60
+ _jsx(ExpandableSectionForSyntaxHighlighter, Object.assign({ variant: ExpandableSectionVariant.truncate, isExpanded: isExpanded, isDetached: true, toggleId: toggleId, contentId: contentId, language: language }, expandableSectionProps, { children: _jsx(SyntaxHighlighter, Object.assign({}, props, { language: language, style: obsidian, PreTag: "div", CodeTag: "div", wrapLongLines: true, children: String(children).replace(/\n$/, '') })) }))) : (_jsx(ExpandableSection, Object.assign({ variant: ExpandableSectionVariant.truncate, isExpanded: isExpanded, isDetached: true, toggleId: toggleId, contentId: contentId }, expandableSectionProps, { children: children }))) }) }), isExpandable && (_jsx(ExpandableSectionToggle, Object.assign({ isExpanded: isExpanded, onToggle: onToggle, direction: "up", toggleId: toggleId, contentId: contentId, hasTruncatedContent: true, className: "pf-chatbot__message-code-toggle" }, expandableSectionToggleProps, { children: isExpanded ? expandedText : collapsedText })))] }) }));
50
61
  };
51
62
  export default CodeBlockMessage;
@@ -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[];
@@ -403,6 +403,30 @@ describe('Message', () => {
403
403
  expect(screen.getByText(/url:/i)).toBeTruthy();
404
404
  expect(screen.getByText(/https:\/\/raw.githubusercontent.com\/Azure-Samples\/helm-charts\/master\/docs/i)).toBeTruthy();
405
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
+ }));
406
430
  it('can click copy code button', () => __awaiter(void 0, void 0, void 0, function* () {
407
431
  // need explicit setup since RTL stubs clipboard if you do this
408
432
  const user = userEvent.setup();
@@ -1,9 +1,16 @@
1
1
  import { ButtonProps, DropEvent, TooltipProps } from '@patternfly/react-core';
2
+ import { Accept } from 'react-dropzone';
2
3
  export interface AttachButtonProps extends ButtonProps {
3
4
  /** Callback for when button is clicked */
4
5
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
5
6
  /** Callback function for AttachButton when an attachment is made */
6
7
  onAttachAccepted?: (data: File[], event: DropEvent) => void;
8
+ /** Specifies the file types accepted by the attachment upload component.
9
+ * Files that don't match the accepted types will be disabled in the file picker.
10
+ * For example,
11
+ * allowedFileTypes: { 'application/json': ['.json'], 'text/plain': ['.txt'] }
12
+ **/
13
+ allowedFileTypes?: Accept;
7
14
  /** Class name for AttachButton */
8
15
  className?: string;
9
16
  /** Props to control if the AttachButton should be disabled */
@@ -16,10 +16,11 @@ import { Button, Icon, Tooltip } from '@patternfly/react-core';
16
16
  import { useDropzone } from 'react-dropzone';
17
17
  import { PaperclipIcon } from '@patternfly/react-icons/dist/esm/icons/paperclip-icon';
18
18
  const AttachButtonBase = (_a) => {
19
- var { onAttachAccepted, onClick, isDisabled, className, tooltipProps, innerRef, tooltipContent = 'Attach', inputTestId, isCompact } = _a, props = __rest(_a, ["onAttachAccepted", "onClick", "isDisabled", "className", "tooltipProps", "innerRef", "tooltipContent", "inputTestId", "isCompact"]);
19
+ var { onAttachAccepted, onClick, isDisabled, className, tooltipProps, innerRef, tooltipContent = 'Attach', inputTestId, isCompact, allowedFileTypes } = _a, props = __rest(_a, ["onAttachAccepted", "onClick", "isDisabled", "className", "tooltipProps", "innerRef", "tooltipContent", "inputTestId", "isCompact", "allowedFileTypes"]);
20
20
  const { open, getInputProps } = useDropzone({
21
21
  multiple: true,
22
- onDropAccepted: onAttachAccepted
22
+ onDropAccepted: onAttachAccepted,
23
+ accept: allowedFileTypes
23
24
  });
24
25
  return (_jsxs(_Fragment, { children: [_jsx("input", Object.assign({ "data-testid": inputTestId }, getInputProps(), { hidden: true })), _jsx(Tooltip, Object.assign({ id: "pf-chatbot__tooltip--attach", content: tooltipContent, position: "top", entryDelay: (tooltipProps === null || tooltipProps === void 0 ? void 0 : tooltipProps.entryDelay) || 0, exitDelay: (tooltipProps === null || tooltipProps === void 0 ? void 0 : tooltipProps.exitDelay) || 0, distance: (tooltipProps === null || tooltipProps === void 0 ? void 0 : tooltipProps.distance) || 8, animationDuration: (tooltipProps === null || tooltipProps === void 0 ? void 0 : tooltipProps.animationDuration) || 0,
25
26
  // prevents VO announcements of both aria label and tooltip
@@ -62,4 +62,28 @@ describe('Attach button', () => {
62
62
  render(_jsx(AttachButton, { isCompact: true, "data-testid": "button" }));
63
63
  expect(screen.getByTestId('button')).toHaveClass('pf-m-compact');
64
64
  });
65
+ it('should set correct accept attribute on file input', () => __awaiter(void 0, void 0, void 0, function* () {
66
+ render(_jsx(AttachButton, { inputTestId: "input", allowedFileTypes: { 'text/plain': ['.txt'] } }));
67
+ yield userEvent.click(screen.getByRole('button', { name: 'Attach' }));
68
+ const input = screen.getByTestId('input');
69
+ expect(input).toHaveAttribute('accept', 'text/plain,.txt');
70
+ }));
71
+ it('should call onAttachAccepted when file type is accepted', () => __awaiter(void 0, void 0, void 0, function* () {
72
+ const onAttachAccepted = jest.fn();
73
+ render(_jsx(AttachButton, { inputTestId: "input", allowedFileTypes: { 'text/plain': ['.txt'] }, onAttachAccepted: onAttachAccepted }));
74
+ const file = new File(['hello'], 'example.txt', { type: 'text/plain' });
75
+ const input = screen.getByTestId('input');
76
+ yield userEvent.upload(input, file);
77
+ expect(onAttachAccepted).toHaveBeenCalled();
78
+ const [attachedFile] = onAttachAccepted.mock.calls[0][0];
79
+ expect(attachedFile).toEqual(file);
80
+ }));
81
+ it('should not call onAttachAccepted when file type is not accepted', () => __awaiter(void 0, void 0, void 0, function* () {
82
+ const onAttachAccepted = jest.fn();
83
+ render(_jsx(AttachButton, { inputTestId: "input", allowedFileTypes: { 'text/plain': ['.txt'] }, onAttachAccepted: onAttachAccepted }));
84
+ const file = new File(['[]'], 'example.json', { type: 'application/json' });
85
+ const input = screen.getByTestId('input');
86
+ yield userEvent.upload(input, file);
87
+ expect(onAttachAccepted).not.toHaveBeenCalled();
88
+ }));
65
89
  });
@@ -1,4 +1,5 @@
1
1
  import type { FunctionComponent } from 'react';
2
+ import { Accept } from 'react-dropzone/.';
2
3
  import { ButtonProps, DropEvent, TextAreaProps, TooltipProps } from '@patternfly/react-core';
3
4
  import { ChatbotDisplayMode } from '../Chatbot';
4
5
  export interface MessageBarWithAttachMenuProps {
@@ -79,6 +80,12 @@ export interface MessageBarProps extends TextAreaProps {
79
80
  /** Display mode of chatbot, if you want to message bar to resize when the display mode changes */
80
81
  displayMode?: ChatbotDisplayMode;
81
82
  isCompact?: boolean;
83
+ /** Specifies the file types accepted by the attachment upload component.
84
+ * Files that don't match the accepted types will be disabled in the file picker.
85
+ * For example,
86
+ * allowedFileTypes: { 'application/json': ['.json'], 'text/plain': ['.txt'] }
87
+ **/
88
+ allowedFileTypes?: Accept;
82
89
  }
83
90
  export declare const MessageBar: FunctionComponent<MessageBarProps>;
84
91
  export default MessageBar;
@@ -19,7 +19,7 @@ import { AttachButton } from './AttachButton';
19
19
  import AttachMenu from '../AttachMenu';
20
20
  import StopButton from './StopButton';
21
21
  export const MessageBar = (_a) => {
22
- var { onSendMessage, className, alwayShowSendButton, placeholder = 'Send a message...', hasAttachButton = true, hasMicrophoneButton, listeningText = 'Listening', handleAttach, attachMenuProps, isSendButtonDisabled, handleStopButton, hasStopButton, buttonProps, onChange, displayMode, value, isCompact = false } = _a, props = __rest(_a, ["onSendMessage", "className", "alwayShowSendButton", "placeholder", "hasAttachButton", "hasMicrophoneButton", "listeningText", "handleAttach", "attachMenuProps", "isSendButtonDisabled", "handleStopButton", "hasStopButton", "buttonProps", "onChange", "displayMode", "value", "isCompact"]);
22
+ var { onSendMessage, className, alwayShowSendButton, placeholder = 'Send a message...', hasAttachButton = true, hasMicrophoneButton, listeningText = 'Listening', handleAttach, attachMenuProps, isSendButtonDisabled, handleStopButton, hasStopButton, buttonProps, onChange, displayMode, value, isCompact = false, allowedFileTypes } = _a, props = __rest(_a, ["onSendMessage", "className", "alwayShowSendButton", "placeholder", "hasAttachButton", "hasMicrophoneButton", "listeningText", "handleAttach", "attachMenuProps", "isSendButtonDisabled", "handleStopButton", "hasStopButton", "buttonProps", "onChange", "displayMode", "value", "isCompact", "allowedFileTypes"]);
23
23
  // Text Input
24
24
  // --------------------------------------------------------------------------
25
25
  const [message, setMessage] = useState(value !== null && value !== void 0 ? value : '');
@@ -174,7 +174,7 @@ export const MessageBar = (_a) => {
174
174
  if (hasStopButton && handleStopButton) {
175
175
  return (_jsx(StopButton, Object.assign({ onClick: handleStopButton, tooltipContent: (_a = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.stop) === null || _a === void 0 ? void 0 : _a.tooltipContent, isCompact: isCompact, tooltipProps: (_b = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.stop) === null || _b === void 0 ? void 0 : _b.tooltipProps }, (_c = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.stop) === null || _c === void 0 ? void 0 : _c.props)));
176
176
  }
177
- return (_jsxs(_Fragment, { children: [attachMenuProps && (_jsx(AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.tooltipContent, isCompact: isCompact, tooltipProps: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipProps }, (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.props))), !attachMenuProps && hasAttachButton && (_jsx(AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.tooltipContent, inputTestId: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _h === void 0 ? void 0 : _h.inputTestId, isCompact: isCompact, tooltipProps: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _j === void 0 ? void 0 : _j.tooltipProps }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _k === void 0 ? void 0 : _k.props))), hasMicrophoneButton && (_jsx(MicrophoneButton, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _l === void 0 ? void 0 : _l.tooltipContent, language: (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _m === void 0 ? void 0 : _m.language, isCompact: isCompact, tooltipProps: (_o = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _o === void 0 ? void 0 : _o.tooltipProps }, (_p = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _p === void 0 ? void 0 : _p.props))), (alwayShowSendButton || message) && (_jsx(SendButton, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_q = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _q === void 0 ? void 0 : _q.tooltipContent, isCompact: isCompact, tooltipProps: (_r = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _r === void 0 ? void 0 : _r.tooltipProps }, (_s = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _s === void 0 ? void 0 : _s.props)))] }));
177
+ return (_jsxs(_Fragment, { children: [attachMenuProps && (_jsx(AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.tooltipContent, isCompact: isCompact, tooltipProps: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipProps, allowedFileTypes: allowedFileTypes }, (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.props))), !attachMenuProps && hasAttachButton && (_jsx(AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.tooltipContent, inputTestId: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _h === void 0 ? void 0 : _h.inputTestId, isCompact: isCompact, tooltipProps: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _j === void 0 ? void 0 : _j.tooltipProps, allowedFileTypes: allowedFileTypes }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _k === void 0 ? void 0 : _k.props))), hasMicrophoneButton && (_jsx(MicrophoneButton, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _l === void 0 ? void 0 : _l.tooltipContent, language: (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _m === void 0 ? void 0 : _m.language, isCompact: isCompact, tooltipProps: (_o = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _o === void 0 ? void 0 : _o.tooltipProps }, (_p = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _p === void 0 ? void 0 : _p.props))), (alwayShowSendButton || message) && (_jsx(SendButton, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_q = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _q === void 0 ? void 0 : _q.tooltipContent, isCompact: isCompact, tooltipProps: (_r = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _r === void 0 ? void 0 : _r.tooltipProps }, (_s = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _s === void 0 ? void 0 : _s.props)))] }));
178
178
  };
179
179
  const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
180
180
  if (attachMenuProps) {
@@ -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/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"}
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.17",
3
+ "version": "6.3.0-prerelease.19",
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",
@@ -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.