@patternfly/chatbot 6.3.0-prerelease.20 → 6.3.0-prerelease.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Message/Message.test.js +4 -0
- package/dist/cjs/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/cjs/ResponseActions/ResponseActions.js +3 -3
- package/dist/cjs/ResponseActions/ResponseActions.test.js +5 -1
- package/dist/cjs/SourcesCard/SourcesCard.d.ts +10 -1
- package/dist/cjs/SourcesCard/SourcesCard.js +2 -1
- package/dist/cjs/SourcesCard/SourcesCard.test.js +10 -0
- package/dist/esm/Message/Message.test.js +4 -0
- package/dist/esm/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/esm/ResponseActions/ResponseActions.js +4 -4
- package/dist/esm/ResponseActions/ResponseActions.test.js +5 -1
- package/dist/esm/SourcesCard/SourcesCard.d.ts +10 -1
- package/dist/esm/SourcesCard/SourcesCard.js +2 -1
- package/dist/esm/SourcesCard/SourcesCard.test.js +10 -0
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +0 -8
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomResponseActions.tsx +0 -10
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithResponseActions.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithActions.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSelection.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotWelcomeInteraction.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotInDrawer.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotScrolling.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedChatbot.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx +1 -1
- package/src/Message/Message.test.tsx +4 -0
- package/src/ResponseActions/ResponseActions.test.tsx +5 -1
- package/src/ResponseActions/ResponseActions.tsx +22 -2
- package/src/SourcesCard/SourcesCard.test.tsx +14 -0
- package/src/SourcesCard/SourcesCard.tsx +12 -0
@@ -348,6 +348,8 @@ describe('Message', () => {
|
|
348
348
|
// eslint-disable-next-line no-console
|
349
349
|
share: { onClick: () => console.log('Share') },
|
350
350
|
// eslint-disable-next-line no-console
|
351
|
+
download: { onClick: () => console.log('Download') },
|
352
|
+
// eslint-disable-next-line no-console
|
351
353
|
listen: { onClick: () => console.log('Listen') }
|
352
354
|
} }));
|
353
355
|
ALL_ACTIONS.forEach(({ label }) => {
|
@@ -365,6 +367,8 @@ describe('Message', () => {
|
|
365
367
|
// eslint-disable-next-line no-console
|
366
368
|
share: { onClick: () => console.log('Share') },
|
367
369
|
// eslint-disable-next-line no-console
|
370
|
+
download: { onClick: () => console.log('Download') },
|
371
|
+
// eslint-disable-next-line no-console
|
368
372
|
listen: { onClick: () => console.log('Listen') }
|
369
373
|
} }));
|
370
374
|
expect(react_2.screen.getByText('Loading message')).toBeTruthy();
|
@@ -21,9 +21,9 @@ const react_2 = require("react");
|
|
21
21
|
const react_icons_1 = require("@patternfly/react-icons");
|
22
22
|
const ResponseActionButton_1 = __importDefault(require("./ResponseActionButton"));
|
23
23
|
const ResponseActions = ({ actions }) => {
|
24
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
24
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
25
25
|
const [activeButton, setActiveButton] = (0, react_2.useState)();
|
26
|
-
const { positive, negative, copy, share, listen } = actions, additionalActions = __rest(actions, ["positive", "negative", "copy", "share", "listen"]);
|
26
|
+
const { positive, negative, copy, share, download, listen } = actions, additionalActions = __rest(actions, ["positive", "negative", "copy", "share", "download", "listen"]);
|
27
27
|
const responseActions = (0, react_2.useRef)(null);
|
28
28
|
(0, react_2.useEffect)(() => {
|
29
29
|
const handleClickOutside = (e) => {
|
@@ -40,7 +40,7 @@ const ResponseActions = ({ actions }) => {
|
|
40
40
|
setActiveButton(id);
|
41
41
|
onClick && onClick(e);
|
42
42
|
};
|
43
|
-
return ((0, jsx_runtime_1.jsxs)("div", { ref: responseActions, className: "pf-chatbot__response-actions", children: [positive && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, positive, { ariaLabel: (_a = positive.ariaLabel) !== null && _a !== void 0 ? _a : 'Good response', clickedAriaLabel: (_b = positive.ariaLabel) !== null && _b !== void 0 ? _b : 'Response recorded', onClick: (e) => handleClick(e, 'positive', positive.onClick), className: positive.className, isDisabled: positive.isDisabled, tooltipContent: (_c = positive.tooltipContent) !== null && _c !== void 0 ? _c : 'Good response', clickedTooltipContent: (_d = positive.clickedTooltipContent) !== null && _d !== void 0 ? _d : 'Response recorded', tooltipProps: positive.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedThumbsUpIcon, {}), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))), negative && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, negative, { ariaLabel: (_e = negative.ariaLabel) !== null && _e !== void 0 ? _e : 'Bad response', clickedAriaLabel: (_f = negative.ariaLabel) !== null && _f !== void 0 ? _f : 'Response recorded', onClick: (e) => handleClick(e, 'negative', negative.onClick), className: negative.className, isDisabled: negative.isDisabled, tooltipContent: (_g = negative.tooltipContent) !== null && _g !== void 0 ? _g : 'Bad response', clickedTooltipContent: (_h = negative.clickedTooltipContent) !== null && _h !== void 0 ? _h : 'Response recorded', tooltipProps: negative.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedThumbsDownIcon, {}), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))), copy && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, copy, { ariaLabel: (_j = copy.ariaLabel) !== null && _j !== void 0 ? _j : 'Copy', clickedAriaLabel: (_k = copy.ariaLabel) !== null && _k !== void 0 ? _k : 'Copied', onClick: (e) => handleClick(e, 'copy', copy.onClick), className: copy.className, isDisabled: copy.isDisabled, tooltipContent: (_l = copy.tooltipContent) !== null && _l !== void 0 ? _l : 'Copy', clickedTooltipContent: (_m = copy.clickedTooltipContent) !== null && _m !== void 0 ? _m : 'Copied', tooltipProps: copy.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedCopyIcon, {}), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))), share && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, share, { ariaLabel: (_o = share.ariaLabel) !== null && _o !== void 0 ? _o : 'Share', clickedAriaLabel: (_p = share.ariaLabel) !== null && _p !== void 0 ? _p : 'Shared', onClick: (e) => handleClick(e, 'share', share.onClick), className: share.className, isDisabled: share.isDisabled, tooltipContent: (_q = share.tooltipContent) !== null && _q !== void 0 ? _q : 'Share', clickedTooltipContent: (_r = share.clickedTooltipContent) !== null && _r !== void 0 ? _r : 'Shared', tooltipProps: share.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkAltIcon, {}), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))),
|
43
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: responseActions, className: "pf-chatbot__response-actions", children: [positive && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, positive, { ariaLabel: (_a = positive.ariaLabel) !== null && _a !== void 0 ? _a : 'Good response', clickedAriaLabel: (_b = positive.ariaLabel) !== null && _b !== void 0 ? _b : 'Response recorded', onClick: (e) => handleClick(e, 'positive', positive.onClick), className: positive.className, isDisabled: positive.isDisabled, tooltipContent: (_c = positive.tooltipContent) !== null && _c !== void 0 ? _c : 'Good response', clickedTooltipContent: (_d = positive.clickedTooltipContent) !== null && _d !== void 0 ? _d : 'Response recorded', tooltipProps: positive.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedThumbsUpIcon, {}), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))), negative && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, negative, { ariaLabel: (_e = negative.ariaLabel) !== null && _e !== void 0 ? _e : 'Bad response', clickedAriaLabel: (_f = negative.ariaLabel) !== null && _f !== void 0 ? _f : 'Response recorded', onClick: (e) => handleClick(e, 'negative', negative.onClick), className: negative.className, isDisabled: negative.isDisabled, tooltipContent: (_g = negative.tooltipContent) !== null && _g !== void 0 ? _g : 'Bad response', clickedTooltipContent: (_h = negative.clickedTooltipContent) !== null && _h !== void 0 ? _h : 'Response recorded', tooltipProps: negative.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedThumbsDownIcon, {}), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))), copy && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, copy, { ariaLabel: (_j = copy.ariaLabel) !== null && _j !== void 0 ? _j : 'Copy', clickedAriaLabel: (_k = copy.ariaLabel) !== null && _k !== void 0 ? _k : 'Copied', onClick: (e) => handleClick(e, 'copy', copy.onClick), className: copy.className, isDisabled: copy.isDisabled, tooltipContent: (_l = copy.tooltipContent) !== null && _l !== void 0 ? _l : 'Copy', clickedTooltipContent: (_m = copy.clickedTooltipContent) !== null && _m !== void 0 ? _m : 'Copied', tooltipProps: copy.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.OutlinedCopyIcon, {}), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))), share && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, share, { ariaLabel: (_o = share.ariaLabel) !== null && _o !== void 0 ? _o : 'Share', clickedAriaLabel: (_p = share.ariaLabel) !== null && _p !== void 0 ? _p : 'Shared', onClick: (e) => handleClick(e, 'share', share.onClick), className: share.className, isDisabled: share.isDisabled, tooltipContent: (_q = share.tooltipContent) !== null && _q !== void 0 ? _q : 'Share', clickedTooltipContent: (_r = share.clickedTooltipContent) !== null && _r !== void 0 ? _r : 'Shared', tooltipProps: share.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkAltIcon, {}), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))), download && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, download, { ariaLabel: (_s = download.ariaLabel) !== null && _s !== void 0 ? _s : 'Download', clickedAriaLabel: (_t = download.ariaLabel) !== null && _t !== void 0 ? _t : 'Downloaded', onClick: (e) => handleClick(e, 'download', download.onClick), className: download.className, isDisabled: download.isDisabled, tooltipContent: (_u = download.tooltipContent) !== null && _u !== void 0 ? _u : 'Download', clickedTooltipContent: (_v = download.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Downloaded', tooltipProps: download.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.DownloadIcon, {}), isClicked: activeButton === 'download', ref: download.ref, "aria-expanded": download['aria-expanded'], "aria-controls": download['aria-controls'] }))), listen && ((0, jsx_runtime_1.jsx)(ResponseActionButton_1.default, Object.assign({}, listen, { ariaLabel: (_w = listen.ariaLabel) !== null && _w !== void 0 ? _w : 'Listen', clickedAriaLabel: (_x = listen.ariaLabel) !== null && _x !== void 0 ? _x : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_y = listen.tooltipContent) !== null && _y !== void 0 ? _y : 'Listen', clickedTooltipContent: (_z = listen.clickedTooltipContent) !== null && _z !== void 0 ? _z : 'Listening', tooltipProps: listen.tooltipProps, icon: (0, jsx_runtime_1.jsx)(react_icons_1.VolumeUpIcon, {}), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] }))), Object.keys(additionalActions).map((action) => {
|
44
44
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
45
45
|
return ((0, react_1.createElement)(ResponseActionButton_1.default, Object.assign({}, additionalActions[action], { key: action, ariaLabel: (_a = additionalActions[action]) === null || _a === void 0 ? void 0 : _a.ariaLabel, clickedAriaLabel: (_b = additionalActions[action]) === null || _b === void 0 ? void 0 : _b.clickedAriaLabel, onClick: (e) => { var _a; return handleClick(e, action, (_a = additionalActions[action]) === null || _a === void 0 ? void 0 : _a.onClick); }, className: (_c = additionalActions[action]) === null || _c === void 0 ? void 0 : _c.className, isDisabled: (_d = additionalActions[action]) === null || _d === void 0 ? void 0 : _d.isDisabled, tooltipContent: (_e = additionalActions[action]) === null || _e === void 0 ? void 0 : _e.tooltipContent, tooltipProps: (_f = additionalActions[action]) === null || _f === void 0 ? void 0 : _f.tooltipProps, clickedTooltipContent: (_g = additionalActions[action]) === null || _g === void 0 ? void 0 : _g.clickedTooltipContent, icon: (_h = additionalActions[action]) === null || _h === void 0 ? void 0 : _h.icon, isClicked: activeButton === action, ref: (_j = additionalActions[action]) === null || _j === void 0 ? void 0 : _j.ref, "aria-expanded": (_k = additionalActions[action]) === null || _k === void 0 ? void 0 : _k['aria-expanded'], "aria-controls": (_l = additionalActions[action]) === null || _l === void 0 ? void 0 : _l['aria-controls'] })));
|
46
46
|
})] }));
|
@@ -57,6 +57,7 @@ const ALL_ACTIONS_DATA_TEST = [
|
|
57
57
|
{ type: 'negative', label: 'Bad response', dataTestId: 'negative' },
|
58
58
|
{ type: 'copy', label: 'Copy', dataTestId: 'copy' },
|
59
59
|
{ type: 'share', label: 'Share', dataTestId: 'share' },
|
60
|
+
{ type: 'download', label: 'Download', dataTestId: 'download' },
|
60
61
|
{ type: 'listen', label: 'Listen', dataTestId: 'listen' }
|
61
62
|
];
|
62
63
|
describe('ResponseActions', () => {
|
@@ -69,14 +70,16 @@ describe('ResponseActions', () => {
|
|
69
70
|
negative: { onClick: jest.fn() },
|
70
71
|
copy: { onClick: jest.fn() },
|
71
72
|
share: { onClick: jest.fn() },
|
73
|
+
download: { onClick: jest.fn() },
|
72
74
|
listen: { onClick: jest.fn() }
|
73
75
|
} }));
|
74
76
|
const goodBtn = react_1.screen.getByRole('button', { name: 'Good response' });
|
75
77
|
const badBtn = react_1.screen.getByRole('button', { name: 'Bad response' });
|
76
78
|
const copyBtn = react_1.screen.getByRole('button', { name: 'Copy' });
|
77
79
|
const shareBtn = react_1.screen.getByRole('button', { name: 'Share' });
|
80
|
+
const downloadBtn = react_1.screen.getByRole('button', { name: 'Download' });
|
78
81
|
const listenBtn = react_1.screen.getByRole('button', { name: 'Listen' });
|
79
|
-
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, listenBtn];
|
82
|
+
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, downloadBtn, listenBtn];
|
80
83
|
buttons.forEach((button) => {
|
81
84
|
expect(button).toBeTruthy();
|
82
85
|
});
|
@@ -149,6 +152,7 @@ describe('ResponseActions', () => {
|
|
149
152
|
{ type: 'negative', ariaLabel: 'Thumbs down' },
|
150
153
|
{ type: 'copy', ariaLabel: 'Copy the message' },
|
151
154
|
{ type: 'share', ariaLabel: 'Share it with friends' },
|
155
|
+
{ type: 'download', ariaLabel: 'Download your cool message' },
|
152
156
|
{ type: 'listen', ariaLabel: 'Listen up' }
|
153
157
|
];
|
154
158
|
actions.forEach(({ type, ariaLabel }) => {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
2
|
-
import { CardProps } from '@patternfly/react-core';
|
2
|
+
import { ButtonProps, CardProps } from '@patternfly/react-core';
|
3
3
|
export interface SourcesCardProps extends CardProps {
|
4
4
|
/** Additional classes for the pagination navigation container. */
|
5
5
|
className?: string;
|
@@ -11,11 +11,20 @@ export interface SourcesCardProps extends CardProps {
|
|
11
11
|
paginationAriaLabel?: string;
|
12
12
|
/** Content rendered inside the paginated card */
|
13
13
|
sources: {
|
14
|
+
/** Title of sources card */
|
14
15
|
title?: string;
|
16
|
+
/** Link to source */
|
15
17
|
link: string;
|
18
|
+
/** Body of sources card */
|
16
19
|
body?: React.ReactNode | string;
|
20
|
+
/** Whether link is external */
|
17
21
|
isExternal?: boolean;
|
22
|
+
/** Whether sources card is expandable */
|
18
23
|
hasShowMore?: boolean;
|
24
|
+
/** onClick event applied to the title of the Sources card */
|
25
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
26
|
+
/** Any additional props applied to the title of the Sources card */
|
27
|
+
titleProps?: ButtonProps;
|
19
28
|
}[];
|
20
29
|
/** Label for the English word "source" */
|
21
30
|
sourceWord?: string;
|
@@ -17,6 +17,7 @@ const react_1 = require("react");
|
|
17
17
|
const react_core_1 = require("@patternfly/react-core");
|
18
18
|
const react_icons_1 = require("@patternfly/react-icons");
|
19
19
|
const SourcesCard = (_a) => {
|
20
|
+
var _b;
|
20
21
|
var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact"]);
|
21
22
|
const [page, setPage] = (0, react_1.useState)(1);
|
22
23
|
const [isExpanded, setIsExpanded] = (0, react_1.useState)(false);
|
@@ -33,7 +34,7 @@ const SourcesCard = (_a) => {
|
|
33
34
|
}
|
34
35
|
return `Source ${page}`;
|
35
36
|
};
|
36
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__source", children: [(0, jsx_runtime_1.jsx)("span", { children: (0, react_core_1.pluralize)(sources.length, sourceWord, sourceWordPlural) }), (0, jsx_runtime_1.jsxs)(react_core_1.Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [(0, jsx_runtime_1.jsx)(react_core_1.CardTitle, { className: "pf-chatbot__sources-card-title", children: (0, jsx_runtime_1.jsx)(react_core_1.Button, { component: "a", variant: react_core_1.ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, children: renderTitle(sources[page - 1].title) }) }), sources[page - 1].body && ((0, jsx_runtime_1.jsx)(react_core_1.CardBody, { className: `pf-chatbot__sources-card-body`, children: sources[page - 1].hasShowMore ? (
|
37
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__source", children: [(0, jsx_runtime_1.jsx)("span", { children: (0, react_core_1.pluralize)(sources.length, sourceWord, sourceWordPlural) }), (0, jsx_runtime_1.jsxs)(react_core_1.Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [(0, jsx_runtime_1.jsx)(react_core_1.CardTitle, { className: "pf-chatbot__sources-card-title", children: (0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ component: "a", variant: react_core_1.ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title) })) }), sources[page - 1].body && ((0, jsx_runtime_1.jsx)(react_core_1.CardBody, { className: `pf-chatbot__sources-card-body`, children: sources[page - 1].hasShowMore ? (
|
37
38
|
// prevents extra VO announcements of button text - parent Message has aria-live
|
38
39
|
(0, jsx_runtime_1.jsx)("div", { "aria-live": "off", children: (0, jsx_runtime_1.jsx)(react_core_1.ExpandableSection, { variant: react_core_1.ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) })), sources.length > 1 && ((0, jsx_runtime_1.jsx)(react_core_1.CardFooter, { className: "pf-chatbot__sources-card-footer-container", children: (0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__sources-card-footer", children: (0, jsx_runtime_1.jsxs)("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [(0, jsx_runtime_1.jsx)(react_core_1.Button, { variant: react_core_1.ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
|
39
40
|
const newPage = page >= 1 ? page - 1 : 1;
|
@@ -174,4 +174,14 @@ describe('SourcesCard', () => {
|
|
174
174
|
] }));
|
175
175
|
expect(react_1.screen.getByRole('region')).toHaveAttribute('class', 'pf-v6-c-expandable-section__content');
|
176
176
|
}));
|
177
|
+
it('should call onClick appropriately', () => __awaiter(void 0, void 0, void 0, function* () {
|
178
|
+
const spy = jest.fn();
|
179
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(SourcesCard_1.default, { sources: [{ title: 'How to make an apple pie', link: '', onClick: spy }] }));
|
180
|
+
yield user_event_1.default.click(react_1.screen.getByRole('link', { name: /How to make an apple pie/i }));
|
181
|
+
expect(spy).toHaveBeenCalled();
|
182
|
+
}));
|
183
|
+
it('should apply titleProps appropriately', () => {
|
184
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(SourcesCard_1.default, { sources: [{ title: 'How to make an apple pie', link: '', titleProps: { className: 'test' } }] }));
|
185
|
+
expect(react_1.screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
|
186
|
+
});
|
177
187
|
});
|
@@ -343,6 +343,8 @@ describe('Message', () => {
|
|
343
343
|
// eslint-disable-next-line no-console
|
344
344
|
share: { onClick: () => console.log('Share') },
|
345
345
|
// eslint-disable-next-line no-console
|
346
|
+
download: { onClick: () => console.log('Download') },
|
347
|
+
// eslint-disable-next-line no-console
|
346
348
|
listen: { onClick: () => console.log('Listen') }
|
347
349
|
} }));
|
348
350
|
ALL_ACTIONS.forEach(({ label }) => {
|
@@ -360,6 +362,8 @@ describe('Message', () => {
|
|
360
362
|
// eslint-disable-next-line no-console
|
361
363
|
share: { onClick: () => console.log('Share') },
|
362
364
|
// eslint-disable-next-line no-console
|
365
|
+
download: { onClick: () => console.log('Download') },
|
366
|
+
// eslint-disable-next-line no-console
|
363
367
|
listen: { onClick: () => console.log('Listen') }
|
364
368
|
} }));
|
365
369
|
expect(screen.getByText('Loading message')).toBeTruthy();
|
@@ -12,12 +12,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
12
12
|
import { createElement as _createElement } from "react";
|
13
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
14
14
|
import { useEffect, useRef, useState } from 'react';
|
15
|
-
import { ExternalLinkAltIcon, VolumeUpIcon, OutlinedThumbsUpIcon, OutlinedThumbsDownIcon, OutlinedCopyIcon } from '@patternfly/react-icons';
|
15
|
+
import { ExternalLinkAltIcon, VolumeUpIcon, OutlinedThumbsUpIcon, OutlinedThumbsDownIcon, OutlinedCopyIcon, DownloadIcon } from '@patternfly/react-icons';
|
16
16
|
import ResponseActionButton from './ResponseActionButton';
|
17
17
|
export const ResponseActions = ({ actions }) => {
|
18
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
18
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
19
19
|
const [activeButton, setActiveButton] = useState();
|
20
|
-
const { positive, negative, copy, share, listen } = actions, additionalActions = __rest(actions, ["positive", "negative", "copy", "share", "listen"]);
|
20
|
+
const { positive, negative, copy, share, download, listen } = actions, additionalActions = __rest(actions, ["positive", "negative", "copy", "share", "download", "listen"]);
|
21
21
|
const responseActions = useRef(null);
|
22
22
|
useEffect(() => {
|
23
23
|
const handleClickOutside = (e) => {
|
@@ -34,7 +34,7 @@ export const ResponseActions = ({ actions }) => {
|
|
34
34
|
setActiveButton(id);
|
35
35
|
onClick && onClick(e);
|
36
36
|
};
|
37
|
-
return (_jsxs("div", { ref: responseActions, className: "pf-chatbot__response-actions", children: [positive && (_jsx(ResponseActionButton, Object.assign({}, positive, { ariaLabel: (_a = positive.ariaLabel) !== null && _a !== void 0 ? _a : 'Good response', clickedAriaLabel: (_b = positive.ariaLabel) !== null && _b !== void 0 ? _b : 'Response recorded', onClick: (e) => handleClick(e, 'positive', positive.onClick), className: positive.className, isDisabled: positive.isDisabled, tooltipContent: (_c = positive.tooltipContent) !== null && _c !== void 0 ? _c : 'Good response', clickedTooltipContent: (_d = positive.clickedTooltipContent) !== null && _d !== void 0 ? _d : 'Response recorded', tooltipProps: positive.tooltipProps, icon: _jsx(OutlinedThumbsUpIcon, {}), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))), negative && (_jsx(ResponseActionButton, Object.assign({}, negative, { ariaLabel: (_e = negative.ariaLabel) !== null && _e !== void 0 ? _e : 'Bad response', clickedAriaLabel: (_f = negative.ariaLabel) !== null && _f !== void 0 ? _f : 'Response recorded', onClick: (e) => handleClick(e, 'negative', negative.onClick), className: negative.className, isDisabled: negative.isDisabled, tooltipContent: (_g = negative.tooltipContent) !== null && _g !== void 0 ? _g : 'Bad response', clickedTooltipContent: (_h = negative.clickedTooltipContent) !== null && _h !== void 0 ? _h : 'Response recorded', tooltipProps: negative.tooltipProps, icon: _jsx(OutlinedThumbsDownIcon, {}), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))), copy && (_jsx(ResponseActionButton, Object.assign({}, copy, { ariaLabel: (_j = copy.ariaLabel) !== null && _j !== void 0 ? _j : 'Copy', clickedAriaLabel: (_k = copy.ariaLabel) !== null && _k !== void 0 ? _k : 'Copied', onClick: (e) => handleClick(e, 'copy', copy.onClick), className: copy.className, isDisabled: copy.isDisabled, tooltipContent: (_l = copy.tooltipContent) !== null && _l !== void 0 ? _l : 'Copy', clickedTooltipContent: (_m = copy.clickedTooltipContent) !== null && _m !== void 0 ? _m : 'Copied', tooltipProps: copy.tooltipProps, icon: _jsx(OutlinedCopyIcon, {}), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))), share && (_jsx(ResponseActionButton, Object.assign({}, share, { ariaLabel: (_o = share.ariaLabel) !== null && _o !== void 0 ? _o : 'Share', clickedAriaLabel: (_p = share.ariaLabel) !== null && _p !== void 0 ? _p : 'Shared', onClick: (e) => handleClick(e, 'share', share.onClick), className: share.className, isDisabled: share.isDisabled, tooltipContent: (_q = share.tooltipContent) !== null && _q !== void 0 ? _q : 'Share', clickedTooltipContent: (_r = share.clickedTooltipContent) !== null && _r !== void 0 ? _r : 'Shared', tooltipProps: share.tooltipProps, icon: _jsx(ExternalLinkAltIcon, {}), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))),
|
37
|
+
return (_jsxs("div", { ref: responseActions, className: "pf-chatbot__response-actions", children: [positive && (_jsx(ResponseActionButton, Object.assign({}, positive, { ariaLabel: (_a = positive.ariaLabel) !== null && _a !== void 0 ? _a : 'Good response', clickedAriaLabel: (_b = positive.ariaLabel) !== null && _b !== void 0 ? _b : 'Response recorded', onClick: (e) => handleClick(e, 'positive', positive.onClick), className: positive.className, isDisabled: positive.isDisabled, tooltipContent: (_c = positive.tooltipContent) !== null && _c !== void 0 ? _c : 'Good response', clickedTooltipContent: (_d = positive.clickedTooltipContent) !== null && _d !== void 0 ? _d : 'Response recorded', tooltipProps: positive.tooltipProps, icon: _jsx(OutlinedThumbsUpIcon, {}), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))), negative && (_jsx(ResponseActionButton, Object.assign({}, negative, { ariaLabel: (_e = negative.ariaLabel) !== null && _e !== void 0 ? _e : 'Bad response', clickedAriaLabel: (_f = negative.ariaLabel) !== null && _f !== void 0 ? _f : 'Response recorded', onClick: (e) => handleClick(e, 'negative', negative.onClick), className: negative.className, isDisabled: negative.isDisabled, tooltipContent: (_g = negative.tooltipContent) !== null && _g !== void 0 ? _g : 'Bad response', clickedTooltipContent: (_h = negative.clickedTooltipContent) !== null && _h !== void 0 ? _h : 'Response recorded', tooltipProps: negative.tooltipProps, icon: _jsx(OutlinedThumbsDownIcon, {}), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))), copy && (_jsx(ResponseActionButton, Object.assign({}, copy, { ariaLabel: (_j = copy.ariaLabel) !== null && _j !== void 0 ? _j : 'Copy', clickedAriaLabel: (_k = copy.ariaLabel) !== null && _k !== void 0 ? _k : 'Copied', onClick: (e) => handleClick(e, 'copy', copy.onClick), className: copy.className, isDisabled: copy.isDisabled, tooltipContent: (_l = copy.tooltipContent) !== null && _l !== void 0 ? _l : 'Copy', clickedTooltipContent: (_m = copy.clickedTooltipContent) !== null && _m !== void 0 ? _m : 'Copied', tooltipProps: copy.tooltipProps, icon: _jsx(OutlinedCopyIcon, {}), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))), share && (_jsx(ResponseActionButton, Object.assign({}, share, { ariaLabel: (_o = share.ariaLabel) !== null && _o !== void 0 ? _o : 'Share', clickedAriaLabel: (_p = share.ariaLabel) !== null && _p !== void 0 ? _p : 'Shared', onClick: (e) => handleClick(e, 'share', share.onClick), className: share.className, isDisabled: share.isDisabled, tooltipContent: (_q = share.tooltipContent) !== null && _q !== void 0 ? _q : 'Share', clickedTooltipContent: (_r = share.clickedTooltipContent) !== null && _r !== void 0 ? _r : 'Shared', tooltipProps: share.tooltipProps, icon: _jsx(ExternalLinkAltIcon, {}), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))), download && (_jsx(ResponseActionButton, Object.assign({}, download, { ariaLabel: (_s = download.ariaLabel) !== null && _s !== void 0 ? _s : 'Download', clickedAriaLabel: (_t = download.ariaLabel) !== null && _t !== void 0 ? _t : 'Downloaded', onClick: (e) => handleClick(e, 'download', download.onClick), className: download.className, isDisabled: download.isDisabled, tooltipContent: (_u = download.tooltipContent) !== null && _u !== void 0 ? _u : 'Download', clickedTooltipContent: (_v = download.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Downloaded', tooltipProps: download.tooltipProps, icon: _jsx(DownloadIcon, {}), isClicked: activeButton === 'download', ref: download.ref, "aria-expanded": download['aria-expanded'], "aria-controls": download['aria-controls'] }))), listen && (_jsx(ResponseActionButton, Object.assign({}, listen, { ariaLabel: (_w = listen.ariaLabel) !== null && _w !== void 0 ? _w : 'Listen', clickedAriaLabel: (_x = listen.ariaLabel) !== null && _x !== void 0 ? _x : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_y = listen.tooltipContent) !== null && _y !== void 0 ? _y : 'Listen', clickedTooltipContent: (_z = listen.clickedTooltipContent) !== null && _z !== void 0 ? _z : 'Listening', tooltipProps: listen.tooltipProps, icon: _jsx(VolumeUpIcon, {}), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] }))), Object.keys(additionalActions).map((action) => {
|
38
38
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
39
39
|
return (_createElement(ResponseActionButton, Object.assign({}, additionalActions[action], { key: action, ariaLabel: (_a = additionalActions[action]) === null || _a === void 0 ? void 0 : _a.ariaLabel, clickedAriaLabel: (_b = additionalActions[action]) === null || _b === void 0 ? void 0 : _b.clickedAriaLabel, onClick: (e) => { var _a; return handleClick(e, action, (_a = additionalActions[action]) === null || _a === void 0 ? void 0 : _a.onClick); }, className: (_c = additionalActions[action]) === null || _c === void 0 ? void 0 : _c.className, isDisabled: (_d = additionalActions[action]) === null || _d === void 0 ? void 0 : _d.isDisabled, tooltipContent: (_e = additionalActions[action]) === null || _e === void 0 ? void 0 : _e.tooltipContent, tooltipProps: (_f = additionalActions[action]) === null || _f === void 0 ? void 0 : _f.tooltipProps, clickedTooltipContent: (_g = additionalActions[action]) === null || _g === void 0 ? void 0 : _g.clickedTooltipContent, icon: (_h = additionalActions[action]) === null || _h === void 0 ? void 0 : _h.icon, isClicked: activeButton === action, ref: (_j = additionalActions[action]) === null || _j === void 0 ? void 0 : _j.ref, "aria-expanded": (_k = additionalActions[action]) === null || _k === void 0 ? void 0 : _k['aria-expanded'], "aria-controls": (_l = additionalActions[action]) === null || _l === void 0 ? void 0 : _l['aria-controls'] })));
|
40
40
|
})] }));
|
@@ -52,6 +52,7 @@ const ALL_ACTIONS_DATA_TEST = [
|
|
52
52
|
{ type: 'negative', label: 'Bad response', dataTestId: 'negative' },
|
53
53
|
{ type: 'copy', label: 'Copy', dataTestId: 'copy' },
|
54
54
|
{ type: 'share', label: 'Share', dataTestId: 'share' },
|
55
|
+
{ type: 'download', label: 'Download', dataTestId: 'download' },
|
55
56
|
{ type: 'listen', label: 'Listen', dataTestId: 'listen' }
|
56
57
|
];
|
57
58
|
describe('ResponseActions', () => {
|
@@ -64,14 +65,16 @@ describe('ResponseActions', () => {
|
|
64
65
|
negative: { onClick: jest.fn() },
|
65
66
|
copy: { onClick: jest.fn() },
|
66
67
|
share: { onClick: jest.fn() },
|
68
|
+
download: { onClick: jest.fn() },
|
67
69
|
listen: { onClick: jest.fn() }
|
68
70
|
} }));
|
69
71
|
const goodBtn = screen.getByRole('button', { name: 'Good response' });
|
70
72
|
const badBtn = screen.getByRole('button', { name: 'Bad response' });
|
71
73
|
const copyBtn = screen.getByRole('button', { name: 'Copy' });
|
72
74
|
const shareBtn = screen.getByRole('button', { name: 'Share' });
|
75
|
+
const downloadBtn = screen.getByRole('button', { name: 'Download' });
|
73
76
|
const listenBtn = screen.getByRole('button', { name: 'Listen' });
|
74
|
-
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, listenBtn];
|
77
|
+
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, downloadBtn, listenBtn];
|
75
78
|
buttons.forEach((button) => {
|
76
79
|
expect(button).toBeTruthy();
|
77
80
|
});
|
@@ -144,6 +147,7 @@ describe('ResponseActions', () => {
|
|
144
147
|
{ type: 'negative', ariaLabel: 'Thumbs down' },
|
145
148
|
{ type: 'copy', ariaLabel: 'Copy the message' },
|
146
149
|
{ type: 'share', ariaLabel: 'Share it with friends' },
|
150
|
+
{ type: 'download', ariaLabel: 'Download your cool message' },
|
147
151
|
{ type: 'listen', ariaLabel: 'Listen up' }
|
148
152
|
];
|
149
153
|
actions.forEach(({ type, ariaLabel }) => {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
2
|
-
import { CardProps } from '@patternfly/react-core';
|
2
|
+
import { ButtonProps, CardProps } from '@patternfly/react-core';
|
3
3
|
export interface SourcesCardProps extends CardProps {
|
4
4
|
/** Additional classes for the pagination navigation container. */
|
5
5
|
className?: string;
|
@@ -11,11 +11,20 @@ export interface SourcesCardProps extends CardProps {
|
|
11
11
|
paginationAriaLabel?: string;
|
12
12
|
/** Content rendered inside the paginated card */
|
13
13
|
sources: {
|
14
|
+
/** Title of sources card */
|
14
15
|
title?: string;
|
16
|
+
/** Link to source */
|
15
17
|
link: string;
|
18
|
+
/** Body of sources card */
|
16
19
|
body?: React.ReactNode | string;
|
20
|
+
/** Whether link is external */
|
17
21
|
isExternal?: boolean;
|
22
|
+
/** Whether sources card is expandable */
|
18
23
|
hasShowMore?: boolean;
|
24
|
+
/** onClick event applied to the title of the Sources card */
|
25
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
26
|
+
/** Any additional props applied to the title of the Sources card */
|
27
|
+
titleProps?: ButtonProps;
|
19
28
|
}[];
|
20
29
|
/** Label for the English word "source" */
|
21
30
|
sourceWord?: string;
|
@@ -15,6 +15,7 @@ import { useState } from 'react';
|
|
15
15
|
import { Button, ButtonVariant, Card, CardBody, CardFooter, CardTitle, ExpandableSection, ExpandableSectionVariant, Icon, pluralize, Truncate } from '@patternfly/react-core';
|
16
16
|
import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
|
17
17
|
const SourcesCard = (_a) => {
|
18
|
+
var _b;
|
18
19
|
var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact"]);
|
19
20
|
const [page, setPage] = useState(1);
|
20
21
|
const [isExpanded, setIsExpanded] = useState(false);
|
@@ -31,7 +32,7 @@ const SourcesCard = (_a) => {
|
|
31
32
|
}
|
32
33
|
return `Source ${page}`;
|
33
34
|
};
|
34
|
-
return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, { className: "pf-chatbot__sources-card-title", children: _jsx(Button, { component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, children: renderTitle(sources[page - 1].title) }) }), sources[page - 1].body && (_jsx(CardBody, { className: `pf-chatbot__sources-card-body`, children: sources[page - 1].hasShowMore ? (
|
35
|
+
return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, { className: "pf-chatbot__sources-card-title", children: _jsx(Button, Object.assign({ component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title) })) }), sources[page - 1].body && (_jsx(CardBody, { className: `pf-chatbot__sources-card-body`, children: sources[page - 1].hasShowMore ? (
|
35
36
|
// prevents extra VO announcements of button text - parent Message has aria-live
|
36
37
|
_jsx("div", { "aria-live": "off", children: _jsx(ExpandableSection, { variant: ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : (_jsx("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) })), sources.length > 1 && (_jsx(CardFooter, { className: "pf-chatbot__sources-card-footer-container", children: _jsx("div", { className: "pf-chatbot__sources-card-footer", children: _jsxs("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
|
37
38
|
const newPage = page >= 1 ? page - 1 : 1;
|
@@ -169,4 +169,14 @@ describe('SourcesCard', () => {
|
|
169
169
|
] }));
|
170
170
|
expect(screen.getByRole('region')).toHaveAttribute('class', 'pf-v6-c-expandable-section__content');
|
171
171
|
}));
|
172
|
+
it('should call onClick appropriately', () => __awaiter(void 0, void 0, void 0, function* () {
|
173
|
+
const spy = jest.fn();
|
174
|
+
render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', onClick: spy }] }));
|
175
|
+
yield userEvent.click(screen.getByRole('link', { name: /How to make an apple pie/i }));
|
176
|
+
expect(spy).toHaveBeenCalled();
|
177
|
+
}));
|
178
|
+
it('should apply titleProps appropriately', () => {
|
179
|
+
render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', titleProps: { className: 'test' } }] }));
|
180
|
+
expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
|
181
|
+
});
|
172
182
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@patternfly/chatbot",
|
3
|
-
"version": "6.3.0-prerelease.
|
3
|
+
"version": "6.3.0-prerelease.22",
|
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",
|
@@ -225,14 +225,6 @@ _Italic text, formatted with single underscores_
|
|
225
225
|
content="This bot has a square avatar. You can further customize the avatar by applying an additional class or passing [PatternFly avatar props](/components/avatar) to the `<Message>` component via `avatarProps`."
|
226
226
|
hasRoundAvatar={false}
|
227
227
|
/>
|
228
|
-
<Message
|
229
|
-
name="Bot"
|
230
|
-
role="bot"
|
231
|
-
avatar={patternflyAvatar}
|
232
|
-
content={`Text-based message from a bot named "Bot," with updated timestamp`}
|
233
|
-
timestamp="1 hour ago"
|
234
|
-
/>
|
235
|
-
<Message name="Bot" role="bot" avatar={patternflyAvatar} content="Example content" isLoading />
|
236
228
|
<Select
|
237
229
|
id="single-select"
|
238
230
|
isOpen={isOpen}
|
@@ -3,7 +3,6 @@ import { FunctionComponent } from 'react';
|
|
3
3
|
import Message from '@patternfly/chatbot/dist/dynamic/Message';
|
4
4
|
import patternflyAvatar from './patternfly_avatar.jpg';
|
5
5
|
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';
|
6
|
-
import DownloadIcon from '@patternfly/react-icons/dist/esm/icons/download-icon';
|
7
6
|
import RedoIcon from '@patternfly/react-icons/dist/esm/icons/redo-icon';
|
8
7
|
|
9
8
|
export const CustomActionExample: FunctionComponent = () => (
|
@@ -22,15 +21,6 @@ export const CustomActionExample: FunctionComponent = () => (
|
|
22
21
|
clickedTooltipContent: 'Regenerated',
|
23
22
|
icon: <RedoIcon />
|
24
23
|
},
|
25
|
-
download: {
|
26
|
-
ariaLabel: 'Download',
|
27
|
-
clickedAriaLabel: 'Downloaded',
|
28
|
-
// eslint-disable-next-line no-console
|
29
|
-
onClick: () => console.log('Clicked download'),
|
30
|
-
tooltipContent: 'Download',
|
31
|
-
clickedTooltipContent: 'Downloaded',
|
32
|
-
icon: <DownloadIcon />
|
33
|
-
},
|
34
24
|
info: {
|
35
25
|
ariaLabel: 'Info',
|
36
26
|
// eslint-disable-next-line no-console
|
package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithResponseActions.tsx
CHANGED
@@ -17,7 +17,7 @@ export const ResponseActionExample: FunctionComponent = () => (
|
|
17
17
|
// eslint-disable-next-line no-console
|
18
18
|
copy: { onClick: () => console.log('Copy') },
|
19
19
|
// eslint-disable-next-line no-console
|
20
|
-
|
20
|
+
download: { onClick: () => console.log('Download') },
|
21
21
|
// eslint-disable-next-line no-console
|
22
22
|
listen: { onClick: () => console.log('Listen') }
|
23
23
|
}}
|
package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithActions.tsx
CHANGED
@@ -7,8 +7,8 @@ import { Checkbox, DropdownItem, DropdownList } from '@patternfly/react-core';
|
|
7
7
|
|
8
8
|
const menuItems = [
|
9
9
|
<DropdownList key="list-1">
|
10
|
-
<DropdownItem value="
|
11
|
-
|
10
|
+
<DropdownItem value="Download" id="Download">
|
11
|
+
Download
|
12
12
|
</DropdownItem>
|
13
13
|
<DropdownItem value="Rename" id="Rename">
|
14
14
|
Rename
|
package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSelection.tsx
CHANGED
@@ -7,8 +7,8 @@ import { Checkbox, DropdownItem, DropdownList } from '@patternfly/react-core';
|
|
7
7
|
|
8
8
|
const menuItems = [
|
9
9
|
<DropdownList key="list-1">
|
10
|
-
<DropdownItem value="
|
11
|
-
|
10
|
+
<DropdownItem value="Download" id="Download">
|
11
|
+
Download
|
12
12
|
</DropdownItem>
|
13
13
|
<DropdownItem value="Rename" id="Rename">
|
14
14
|
Rename
|
package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotWelcomeInteraction.tsx
CHANGED
@@ -80,7 +80,7 @@ export const ChatbotWelcomeInteractionDemo: FunctionComponent = () => {
|
|
80
80
|
// eslint-disable-next-line no-console
|
81
81
|
copy: { onClick: () => console.log('Copy') },
|
82
82
|
// eslint-disable-next-line no-console
|
83
|
-
|
83
|
+
download: { onClick: () => console.log('Download') },
|
84
84
|
// eslint-disable-next-line no-console
|
85
85
|
listen: { onClick: () => console.log('Listen') }
|
86
86
|
}
|
@@ -132,7 +132,7 @@ const initialMessages: MessageProps[] = [
|
|
132
132
|
positive: { onClick: () => tracking.trackSingleItem(actionEventName, { response: 'Good response' }) },
|
133
133
|
negative: { onClick: () => tracking.trackSingleItem(actionEventName, { response: 'Bad response' }) },
|
134
134
|
copy: { onClick: () => tracking.trackSingleItem(actionEventName, { response: 'Copy' }) },
|
135
|
-
|
135
|
+
download: { onClick: () => tracking.trackSingleItem(actionEventName, { response: 'Download' }) },
|
136
136
|
listen: { onClick: () => tracking.trackSingleItem(actionEventName, { response: 'Listen' }) }
|
137
137
|
}
|
138
138
|
}
|
@@ -269,7 +269,7 @@ export const ChatbotDemo: FunctionComponent = () => {
|
|
269
269
|
positive: { onClick: () => tracking.trackSingleItem(actionEvent2, { response: 'Good response' }) },
|
270
270
|
negative: { onClick: () => tracking.trackSingleItem(actionEvent2, { response: 'Bad response' }) },
|
271
271
|
copy: { onClick: () => tracking.trackSingleItem(actionEvent2, { response: 'Copy' }) },
|
272
|
-
|
272
|
+
download: { onClick: () => tracking.trackSingleItem(actionEvent2, { response: 'Download' }) },
|
273
273
|
listen: { onClick: () => tracking.trackSingleItem(actionEvent2, { response: 'Listen' }) }
|
274
274
|
}
|
275
275
|
});
|
@@ -117,7 +117,7 @@ const initialMessages: MessageProps[] = [
|
|
117
117
|
// eslint-disable-next-line no-console
|
118
118
|
copy: { onClick: () => console.log('Copy') },
|
119
119
|
// eslint-disable-next-line no-console
|
120
|
-
|
120
|
+
download: { onClick: () => console.log('Download') },
|
121
121
|
// eslint-disable-next-line no-console
|
122
122
|
listen: { onClick: () => console.log('Listen') }
|
123
123
|
}
|
@@ -256,7 +256,7 @@ export const ChatbotDemo: FunctionComponent = () => {
|
|
256
256
|
// eslint-disable-next-line no-console
|
257
257
|
copy: { onClick: () => console.log('Copy') },
|
258
258
|
// eslint-disable-next-line no-console
|
259
|
-
|
259
|
+
download: { onClick: () => console.log('Download') },
|
260
260
|
// eslint-disable-next-line no-console
|
261
261
|
listen: { onClick: () => console.log('Listen') }
|
262
262
|
}
|
@@ -130,7 +130,7 @@ const initialMessages: MessageProps[] = [
|
|
130
130
|
// eslint-disable-next-line no-console
|
131
131
|
copy: { onClick: () => console.log('Copy') },
|
132
132
|
// eslint-disable-next-line no-console
|
133
|
-
|
133
|
+
download: { onClick: () => console.log('Download') },
|
134
134
|
// eslint-disable-next-line no-console
|
135
135
|
listen: { onClick: () => console.log('Listen') }
|
136
136
|
}
|
@@ -266,7 +266,7 @@ export const EmbeddedChatbotDemo: FunctionComponent = () => {
|
|
266
266
|
// eslint-disable-next-line no-console
|
267
267
|
copy: { onClick: () => console.log('Copy') },
|
268
268
|
// eslint-disable-next-line no-console
|
269
|
-
|
269
|
+
download: { onClick: () => console.log('Download') },
|
270
270
|
// eslint-disable-next-line no-console
|
271
271
|
listen: { onClick: () => console.log('Listen') }
|
272
272
|
},
|
@@ -122,7 +122,7 @@ const initialMessages: MessageProps[] = [
|
|
122
122
|
// eslint-disable-next-line no-console
|
123
123
|
copy: { onClick: () => console.log('Copy') },
|
124
124
|
// eslint-disable-next-line no-console
|
125
|
-
|
125
|
+
download: { onClick: () => console.log('Download') },
|
126
126
|
// eslint-disable-next-line no-console
|
127
127
|
listen: { onClick: () => console.log('Listen') }
|
128
128
|
}
|
@@ -283,7 +283,7 @@ export const ChatbotScrollingDemo: React.FunctionComponent = () => {
|
|
283
283
|
// eslint-disable-next-line no-console
|
284
284
|
copy: { onClick: () => console.log('Copy') },
|
285
285
|
// eslint-disable-next-line no-console
|
286
|
-
|
286
|
+
download: { onClick: () => console.log('Download') },
|
287
287
|
// eslint-disable-next-line no-console
|
288
288
|
listen: { onClick: () => console.log('Listen') }
|
289
289
|
}
|
@@ -128,7 +128,7 @@ const initialMessages: MessageProps[] = [
|
|
128
128
|
// eslint-disable-next-line no-console
|
129
129
|
copy: { onClick: () => console.log('Copy') },
|
130
130
|
// eslint-disable-next-line no-console
|
131
|
-
|
131
|
+
download: { onClick: () => console.log('Download') },
|
132
132
|
// eslint-disable-next-line no-console
|
133
133
|
listen: { onClick: () => console.log('Listen') }
|
134
134
|
}
|
@@ -257,7 +257,7 @@ export const EmbeddedChatbotDemo: FunctionComponent = () => {
|
|
257
257
|
// eslint-disable-next-line no-console
|
258
258
|
copy: { onClick: () => console.log('Copy') },
|
259
259
|
// eslint-disable-next-line no-console
|
260
|
-
|
260
|
+
download: { onClick: () => console.log('Download') },
|
261
261
|
// eslint-disable-next-line no-console
|
262
262
|
listen: { onClick: () => console.log('Listen') }
|
263
263
|
},
|
package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx
CHANGED
@@ -86,7 +86,7 @@ export const CompareChild = ({ name, input, hasNewInput, setIsSendButtonDisabled
|
|
86
86
|
// eslint-disable-next-line no-console
|
87
87
|
copy: { onClick: () => console.log('Copy') },
|
88
88
|
// eslint-disable-next-line no-console
|
89
|
-
|
89
|
+
download: { onClick: () => console.log('Download') },
|
90
90
|
// eslint-disable-next-line no-console
|
91
91
|
listen: { onClick: () => console.log('Listen') }
|
92
92
|
},
|
@@ -428,6 +428,8 @@ describe('Message', () => {
|
|
428
428
|
// eslint-disable-next-line no-console
|
429
429
|
share: { onClick: () => console.log('Share') },
|
430
430
|
// eslint-disable-next-line no-console
|
431
|
+
download: { onClick: () => console.log('Download') },
|
432
|
+
// eslint-disable-next-line no-console
|
431
433
|
listen: { onClick: () => console.log('Listen') }
|
432
434
|
}}
|
433
435
|
/>
|
@@ -454,6 +456,8 @@ describe('Message', () => {
|
|
454
456
|
// eslint-disable-next-line no-console
|
455
457
|
share: { onClick: () => console.log('Share') },
|
456
458
|
// eslint-disable-next-line no-console
|
459
|
+
download: { onClick: () => console.log('Download') },
|
460
|
+
// eslint-disable-next-line no-console
|
457
461
|
listen: { onClick: () => console.log('Listen') }
|
458
462
|
}}
|
459
463
|
/>
|
@@ -45,6 +45,7 @@ const ALL_ACTIONS_DATA_TEST = [
|
|
45
45
|
{ type: 'negative', label: 'Bad response', dataTestId: 'negative' },
|
46
46
|
{ type: 'copy', label: 'Copy', dataTestId: 'copy' },
|
47
47
|
{ type: 'share', label: 'Share', dataTestId: 'share' },
|
48
|
+
{ type: 'download', label: 'Download', dataTestId: 'download' },
|
48
49
|
{ type: 'listen', label: 'Listen', dataTestId: 'listen' }
|
49
50
|
];
|
50
51
|
|
@@ -60,6 +61,7 @@ describe('ResponseActions', () => {
|
|
60
61
|
negative: { onClick: jest.fn() },
|
61
62
|
copy: { onClick: jest.fn() },
|
62
63
|
share: { onClick: jest.fn() },
|
64
|
+
download: { onClick: jest.fn() },
|
63
65
|
listen: { onClick: jest.fn() }
|
64
66
|
}}
|
65
67
|
/>
|
@@ -68,8 +70,9 @@ describe('ResponseActions', () => {
|
|
68
70
|
const badBtn = screen.getByRole('button', { name: 'Bad response' });
|
69
71
|
const copyBtn = screen.getByRole('button', { name: 'Copy' });
|
70
72
|
const shareBtn = screen.getByRole('button', { name: 'Share' });
|
73
|
+
const downloadBtn = screen.getByRole('button', { name: 'Download' });
|
71
74
|
const listenBtn = screen.getByRole('button', { name: 'Listen' });
|
72
|
-
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, listenBtn];
|
75
|
+
const buttons = [goodBtn, badBtn, copyBtn, shareBtn, downloadBtn, listenBtn];
|
73
76
|
buttons.forEach((button) => {
|
74
77
|
expect(button).toBeTruthy();
|
75
78
|
});
|
@@ -166,6 +169,7 @@ describe('ResponseActions', () => {
|
|
166
169
|
{ type: 'negative', ariaLabel: 'Thumbs down' },
|
167
170
|
{ type: 'copy', ariaLabel: 'Copy the message' },
|
168
171
|
{ type: 'share', ariaLabel: 'Share it with friends' },
|
172
|
+
{ type: 'download', ariaLabel: 'Download your cool message' },
|
169
173
|
{ type: 'listen', ariaLabel: 'Listen up' }
|
170
174
|
];
|
171
175
|
actions.forEach(({ type, ariaLabel }) => {
|
@@ -5,7 +5,8 @@ import {
|
|
5
5
|
VolumeUpIcon,
|
6
6
|
OutlinedThumbsUpIcon,
|
7
7
|
OutlinedThumbsDownIcon,
|
8
|
-
OutlinedCopyIcon
|
8
|
+
OutlinedCopyIcon,
|
9
|
+
DownloadIcon
|
9
10
|
} from '@patternfly/react-icons';
|
10
11
|
import ResponseActionButton from './ResponseActionButton';
|
11
12
|
import { ButtonProps, TooltipProps } from '@patternfly/react-core';
|
@@ -47,13 +48,14 @@ export interface ResponseActionProps {
|
|
47
48
|
negative?: ActionProps;
|
48
49
|
copy?: ActionProps;
|
49
50
|
share?: ActionProps;
|
51
|
+
download?: ActionProps;
|
50
52
|
listen?: ActionProps;
|
51
53
|
};
|
52
54
|
}
|
53
55
|
|
54
56
|
export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ actions }) => {
|
55
57
|
const [activeButton, setActiveButton] = useState<string>();
|
56
|
-
const { positive, negative, copy, share, listen, ...additionalActions } = actions;
|
58
|
+
const { positive, negative, copy, share, download, listen, ...additionalActions } = actions;
|
57
59
|
const responseActions = useRef<HTMLDivElement>(null);
|
58
60
|
|
59
61
|
useEffect(() => {
|
@@ -152,6 +154,24 @@ export const ResponseActions: FunctionComponent<ResponseActionProps> = ({ action
|
|
152
154
|
aria-controls={share['aria-controls']}
|
153
155
|
></ResponseActionButton>
|
154
156
|
)}
|
157
|
+
{download && (
|
158
|
+
<ResponseActionButton
|
159
|
+
{...download}
|
160
|
+
ariaLabel={download.ariaLabel ?? 'Download'}
|
161
|
+
clickedAriaLabel={download.ariaLabel ?? 'Downloaded'}
|
162
|
+
onClick={(e) => handleClick(e, 'download', download.onClick)}
|
163
|
+
className={download.className}
|
164
|
+
isDisabled={download.isDisabled}
|
165
|
+
tooltipContent={download.tooltipContent ?? 'Download'}
|
166
|
+
clickedTooltipContent={download.clickedTooltipContent ?? 'Downloaded'}
|
167
|
+
tooltipProps={download.tooltipProps}
|
168
|
+
icon={<DownloadIcon />}
|
169
|
+
isClicked={activeButton === 'download'}
|
170
|
+
ref={download.ref}
|
171
|
+
aria-expanded={download['aria-expanded']}
|
172
|
+
aria-controls={download['aria-controls']}
|
173
|
+
></ResponseActionButton>
|
174
|
+
)}
|
155
175
|
{listen && (
|
156
176
|
<ResponseActionButton
|
157
177
|
{...listen}
|
@@ -242,4 +242,18 @@ describe('SourcesCard', () => {
|
|
242
242
|
);
|
243
243
|
expect(screen.getByRole('region')).toHaveAttribute('class', 'pf-v6-c-expandable-section__content');
|
244
244
|
});
|
245
|
+
|
246
|
+
it('should call onClick appropriately', async () => {
|
247
|
+
const spy = jest.fn();
|
248
|
+
render(<SourcesCard sources={[{ title: 'How to make an apple pie', link: '', onClick: spy }]} />);
|
249
|
+
await userEvent.click(screen.getByRole('link', { name: /How to make an apple pie/i }));
|
250
|
+
expect(spy).toHaveBeenCalled();
|
251
|
+
});
|
252
|
+
|
253
|
+
it('should apply titleProps appropriately', () => {
|
254
|
+
render(
|
255
|
+
<SourcesCard sources={[{ title: 'How to make an apple pie', link: '', titleProps: { className: 'test' } }]} />
|
256
|
+
);
|
257
|
+
expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
|
258
|
+
});
|
245
259
|
});
|
@@ -6,6 +6,7 @@ import { useState } from 'react';
|
|
6
6
|
// Import PatternFly components
|
7
7
|
import {
|
8
8
|
Button,
|
9
|
+
ButtonProps,
|
9
10
|
ButtonVariant,
|
10
11
|
Card,
|
11
12
|
CardBody,
|
@@ -31,11 +32,20 @@ export interface SourcesCardProps extends CardProps {
|
|
31
32
|
paginationAriaLabel?: string;
|
32
33
|
/** Content rendered inside the paginated card */
|
33
34
|
sources: {
|
35
|
+
/** Title of sources card */
|
34
36
|
title?: string;
|
37
|
+
/** Link to source */
|
35
38
|
link: string;
|
39
|
+
/** Body of sources card */
|
36
40
|
body?: React.ReactNode | string;
|
41
|
+
/** Whether link is external */
|
37
42
|
isExternal?: boolean;
|
43
|
+
/** Whether sources card is expandable */
|
38
44
|
hasShowMore?: boolean;
|
45
|
+
/** onClick event applied to the title of the Sources card */
|
46
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
47
|
+
/** Any additional props applied to the title of the Sources card */
|
48
|
+
titleProps?: ButtonProps;
|
39
49
|
}[];
|
40
50
|
/** Label for the English word "source" */
|
41
51
|
sourceWord?: string;
|
@@ -107,6 +117,8 @@ const SourcesCard: FunctionComponent<SourcesCardProps> = ({
|
|
107
117
|
isInline
|
108
118
|
rel={sources[page - 1].isExternal ? 'noreferrer' : undefined}
|
109
119
|
target={sources[page - 1].isExternal ? '_blank' : undefined}
|
120
|
+
onClick={sources[page - 1].onClick ?? undefined}
|
121
|
+
{...sources[page - 1].titleProps}
|
110
122
|
>
|
111
123
|
{renderTitle(sources[page - 1].title)}
|
112
124
|
</Button>
|