@patternfly/chatbot 6.3.0-prerelease.11 → 6.3.0-prerelease.13

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.
@@ -175,15 +175,15 @@ const MessageBar = (_a) => {
175
175
  onChange && onChange({}, message);
176
176
  };
177
177
  const renderButtons = () => {
178
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
178
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
179
179
  if (hasStopButton && handleStopButton) {
180
- return (react_1.default.createElement(StopButton_1.default, 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 }, (_b = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.stop) === null || _b === void 0 ? void 0 : _b.props)));
180
+ return (react_1.default.createElement(StopButton_1.default, 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)));
181
181
  }
182
182
  return (react_1.default.createElement(react_1.default.Fragment, null,
183
- attachMenuProps && (react_1.default.createElement(AttachButton_1.AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_c = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _c === void 0 ? void 0 : _c.tooltipContent, isCompact: isCompact }, (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.props))),
184
- !attachMenuProps && hasAttachButton && (react_1.default.createElement(AttachButton_1.AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipContent, inputTestId: (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.inputTestId, isCompact: isCompact }, (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.props))),
185
- hasMicrophoneButton && (react_1.default.createElement(MicrophoneButton_1.default, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _h === void 0 ? void 0 : _h.tooltipContent, language: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _j === void 0 ? void 0 : _j.language, isCompact: isCompact }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _k === void 0 ? void 0 : _k.props))),
186
- (alwayShowSendButton || message) && (react_1.default.createElement(SendButton_1.default, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _l === void 0 ? void 0 : _l.tooltipContent, isCompact: isCompact }, (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _m === void 0 ? void 0 : _m.props)))));
183
+ attachMenuProps && (react_1.default.createElement(AttachButton_1.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))),
184
+ !attachMenuProps && hasAttachButton && (react_1.default.createElement(AttachButton_1.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))),
185
+ hasMicrophoneButton && (react_1.default.createElement(MicrophoneButton_1.default, 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))),
186
+ (alwayShowSendButton || message) && (react_1.default.createElement(SendButton_1.default, 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)))));
187
187
  };
188
188
  const messageBarContents = (react_1.default.createElement(react_1.default.Fragment, null,
189
189
  react_1.default.createElement("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}` },
@@ -125,6 +125,11 @@ describe('Message bar', () => {
125
125
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Send' }));
126
126
  expect(react_2.screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
127
127
  }));
128
+ it('can handle buttonProps tooltipProps appropriately for send', () => {
129
+ (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { send: { tooltipProps: { isVisible: true } } } }));
130
+ // isVisible, so no need for click
131
+ expect(react_2.screen.getByRole('tooltip', { name: 'Send' })).toBeTruthy();
132
+ });
128
133
  it('can handle buttonProps props appropriately for send', () => __awaiter(void 0, void 0, void 0, function* () {
129
134
  (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { send: { props: { 'aria-label': 'Test' } } } }));
130
135
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Test' }));
@@ -189,6 +194,11 @@ describe('Message bar', () => {
189
194
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Attach' }));
190
195
  expect(react_2.screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
191
196
  }));
197
+ it('can handle buttonProps tooltipProps appropriately for attach', () => {
198
+ (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { attach: { tooltipProps: { isVisible: true } } } }));
199
+ // isVisible, so no need for click
200
+ expect(react_2.screen.getByRole('tooltip', { name: 'Attach' })).toBeTruthy();
201
+ });
192
202
  it('can handle buttonProps props appropriately for attach', () => __awaiter(void 0, void 0, void 0, function* () {
193
203
  (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, hasAttachButton: true, buttonProps: { attach: { props: { 'aria-label': 'Test' } } } }));
194
204
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Test' }));
@@ -210,6 +220,11 @@ describe('Message bar', () => {
210
220
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Stop' }));
211
221
  expect(react_2.screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
212
222
  }));
223
+ it('can handle buttonProps tooltipProps appropriately for stop', () => {
224
+ (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, hasStopButton: true, handleStopButton: jest.fn, buttonProps: { stop: { tooltipProps: { isVisible: true } } } }));
225
+ // isVisible, so no need for click
226
+ expect(react_2.screen.getByRole('tooltip', { name: 'Stop' })).toBeTruthy();
227
+ });
213
228
  it('can handle buttonProps props appropriately for stop', () => __awaiter(void 0, void 0, void 0, function* () {
214
229
  (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, hasStopButton: true, handleStopButton: jest.fn, buttonProps: { stop: { props: { 'aria-label': 'Test' } } } }));
215
230
  yield user_event_1.default.click(react_2.screen.getByRole('button', { name: 'Test' }));
@@ -242,6 +257,11 @@ describe('Message bar', () => {
242
257
  const input = react_2.screen.getByRole('textbox', { name: /I am listening/i });
243
258
  expect(input).toBeTruthy();
244
259
  }));
260
+ it('can handle buttonProps tooltipProps appropriately for microphone', () => {
261
+ (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, hasMicrophoneButton: true, buttonProps: { microphone: { tooltipProps: { isVisible: true } } } }));
262
+ // isVisible, so no need for click
263
+ expect(react_2.screen.getByRole('tooltip', { name: 'Use microphone' })).toBeTruthy();
264
+ });
245
265
  it('can handle buttonProps props appropriately for microphone', () => __awaiter(void 0, void 0, void 0, function* () {
246
266
  mockSpeechRecognition();
247
267
  (0, react_2.render)(react_1.default.createElement(MessageBar_1.MessageBar, { onSendMessage: jest.fn, hasMicrophoneButton: true, buttonProps: { microphone: { props: { 'aria-label': 'Test' } } } }));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { TooltipProps } from '@patternfly/react-core';
3
- export interface ResponseActionButtonProps {
2
+ import { ButtonProps, TooltipProps } from '@patternfly/react-core';
3
+ export interface ResponseActionButtonProps extends ButtonProps {
4
4
  /** Aria-label for the button. Defaults to the value of the tooltipContent if none provided */
5
5
  ariaLabel?: string;
6
6
  /** Aria-label for the button, shown when the button is clicked. Defaults to the value of ariaLabel or tooltipContent if not provided. */
@@ -26,9 +26,12 @@ export interface ActionProps extends Omit<ButtonProps, 'ref'> {
26
26
  /** Id for content controlled by the button, such as the feedback form */
27
27
  'aria-controls'?: string;
28
28
  }
29
+ type ExtendedActionProps = ActionProps & {
30
+ [key: string]: any;
31
+ };
29
32
  export interface ResponseActionProps {
30
33
  /** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
31
- actions: Record<string, ActionProps | undefined> & {
34
+ actions: Record<string, ExtendedActionProps | undefined> & {
32
35
  positive?: ActionProps;
33
36
  negative?: ActionProps;
34
37
  copy?: ActionProps;
@@ -39,14 +39,14 @@ const ResponseActions = ({ actions }) => {
39
39
  onClick && onClick(e);
40
40
  };
41
41
  return (react_1.default.createElement("div", { ref: responseActions, className: "pf-chatbot__response-actions" },
42
- positive && (react_1.default.createElement(ResponseActionButton_1.default, { 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: react_1.default.createElement(react_icons_1.OutlinedThumbsUpIcon, null), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] })),
43
- negative && (react_1.default.createElement(ResponseActionButton_1.default, { 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: react_1.default.createElement(react_icons_1.OutlinedThumbsDownIcon, null), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] })),
44
- copy && (react_1.default.createElement(ResponseActionButton_1.default, { 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: react_1.default.createElement(react_icons_1.OutlinedCopyIcon, null), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] })),
45
- share && (react_1.default.createElement(ResponseActionButton_1.default, { 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: react_1.default.createElement(react_icons_1.ExternalLinkAltIcon, null), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] })),
46
- listen && (react_1.default.createElement(ResponseActionButton_1.default, { ariaLabel: (_s = listen.ariaLabel) !== null && _s !== void 0 ? _s : 'Listen', clickedAriaLabel: (_t = listen.ariaLabel) !== null && _t !== void 0 ? _t : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_u = listen.tooltipContent) !== null && _u !== void 0 ? _u : 'Listen', clickedTooltipContent: (_v = listen.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Listening', tooltipProps: listen.tooltipProps, icon: react_1.default.createElement(react_icons_1.VolumeUpIcon, null), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] })),
42
+ positive && (react_1.default.createElement(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: react_1.default.createElement(react_icons_1.OutlinedThumbsUpIcon, null), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))),
43
+ negative && (react_1.default.createElement(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: react_1.default.createElement(react_icons_1.OutlinedThumbsDownIcon, null), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))),
44
+ copy && (react_1.default.createElement(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: react_1.default.createElement(react_icons_1.OutlinedCopyIcon, null), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))),
45
+ share && (react_1.default.createElement(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: react_1.default.createElement(react_icons_1.ExternalLinkAltIcon, null), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))),
46
+ listen && (react_1.default.createElement(ResponseActionButton_1.default, Object.assign({}, listen, { ariaLabel: (_s = listen.ariaLabel) !== null && _s !== void 0 ? _s : 'Listen', clickedAriaLabel: (_t = listen.ariaLabel) !== null && _t !== void 0 ? _t : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_u = listen.tooltipContent) !== null && _u !== void 0 ? _u : 'Listen', clickedTooltipContent: (_v = listen.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Listening', tooltipProps: listen.tooltipProps, icon: react_1.default.createElement(react_icons_1.VolumeUpIcon, null), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] }))),
47
47
  Object.keys(additionalActions).map((action) => {
48
48
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
49
- return (react_1.default.createElement(ResponseActionButton_1.default, { 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'] }));
49
+ return (react_1.default.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'] })));
50
50
  })));
51
51
  };
52
52
  exports.ResponseActions = ResponseActions;
@@ -52,6 +52,13 @@ const CUSTOM_ACTIONS = [
52
52
  }
53
53
  }
54
54
  ];
55
+ const ALL_ACTIONS_DATA_TEST = [
56
+ { type: 'positive', label: 'Good response', dataTestId: 'positive' },
57
+ { type: 'negative', label: 'Bad response', dataTestId: 'negative' },
58
+ { type: 'copy', label: 'Copy', dataTestId: 'copy' },
59
+ { type: 'share', label: 'Share', dataTestId: 'share' },
60
+ { type: 'listen', label: 'Listen', dataTestId: 'listen' }
61
+ ];
55
62
  describe('ResponseActions', () => {
56
63
  afterEach(() => {
57
64
  jest.clearAllMocks();
@@ -161,6 +168,12 @@ describe('ResponseActions', () => {
161
168
  expect(react_2.screen.getByRole('button', { name: label })).toHaveClass('test');
162
169
  });
163
170
  });
171
+ it('should be able to add custom attributes to buttons', () => {
172
+ ALL_ACTIONS_DATA_TEST.forEach(({ type, dataTestId }) => {
173
+ (0, react_2.render)(react_1.default.createElement(ResponseActions_1.default, { actions: { [type]: { onClick: jest.fn(), 'data-testid': dataTestId } } }));
174
+ expect(react_2.screen.getByTestId(dataTestId)).toBeTruthy();
175
+ });
176
+ });
164
177
  it('should be able to add custom actions', () => {
165
178
  CUSTOM_ACTIONS.forEach((action) => {
166
179
  const key = Object.keys(action)[0];
@@ -170,10 +183,12 @@ describe('ResponseActions', () => {
170
183
  onClick: action[key].onClick,
171
184
  // doing this just because it's easier to test without a regex for the button name
172
185
  ariaLabel: action[key].ariaLabel.toLowerCase(),
173
- icon: action[key].icon
186
+ icon: action[key].icon,
187
+ 'data-testid': action[key]
174
188
  }
175
189
  } }));
176
190
  expect(react_2.screen.getByRole('button', { name: key })).toBeTruthy();
191
+ expect(react_2.screen.getByTestId(action[key])).toBeTruthy();
177
192
  });
178
193
  });
179
194
  });
@@ -169,15 +169,15 @@ export const MessageBar = (_a) => {
169
169
  onChange && onChange({}, message);
170
170
  };
171
171
  const renderButtons = () => {
172
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
172
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
173
173
  if (hasStopButton && handleStopButton) {
174
- return (React.createElement(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 }, (_b = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.stop) === null || _b === void 0 ? void 0 : _b.props)));
174
+ return (React.createElement(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)));
175
175
  }
176
176
  return (React.createElement(React.Fragment, null,
177
- attachMenuProps && (React.createElement(AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_c = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _c === void 0 ? void 0 : _c.tooltipContent, isCompact: isCompact }, (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.props))),
178
- !attachMenuProps && hasAttachButton && (React.createElement(AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipContent, inputTestId: (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.inputTestId, isCompact: isCompact }, (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.props))),
179
- hasMicrophoneButton && (React.createElement(MicrophoneButton, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _h === void 0 ? void 0 : _h.tooltipContent, language: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _j === void 0 ? void 0 : _j.language, isCompact: isCompact }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _k === void 0 ? void 0 : _k.props))),
180
- (alwayShowSendButton || message) && (React.createElement(SendButton, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _l === void 0 ? void 0 : _l.tooltipContent, isCompact: isCompact }, (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _m === void 0 ? void 0 : _m.props)))));
177
+ attachMenuProps && (React.createElement(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))),
178
+ !attachMenuProps && hasAttachButton && (React.createElement(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))),
179
+ hasMicrophoneButton && (React.createElement(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))),
180
+ (alwayShowSendButton || message) && (React.createElement(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)))));
181
181
  };
182
182
  const messageBarContents = (React.createElement(React.Fragment, null,
183
183
  React.createElement("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}` },
@@ -120,6 +120,11 @@ describe('Message bar', () => {
120
120
  yield userEvent.click(screen.getByRole('button', { name: 'Send' }));
121
121
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
122
122
  }));
123
+ it('can handle buttonProps tooltipProps appropriately for send', () => {
124
+ render(React.createElement(MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { send: { tooltipProps: { isVisible: true } } } }));
125
+ // isVisible, so no need for click
126
+ expect(screen.getByRole('tooltip', { name: 'Send' })).toBeTruthy();
127
+ });
123
128
  it('can handle buttonProps props appropriately for send', () => __awaiter(void 0, void 0, void 0, function* () {
124
129
  render(React.createElement(MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { send: { props: { 'aria-label': 'Test' } } } }));
125
130
  yield userEvent.click(screen.getByRole('button', { name: 'Test' }));
@@ -184,6 +189,11 @@ describe('Message bar', () => {
184
189
  yield userEvent.click(screen.getByRole('button', { name: 'Attach' }));
185
190
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
186
191
  }));
192
+ it('can handle buttonProps tooltipProps appropriately for attach', () => {
193
+ render(React.createElement(MessageBar, { onSendMessage: jest.fn, alwayShowSendButton: true, buttonProps: { attach: { tooltipProps: { isVisible: true } } } }));
194
+ // isVisible, so no need for click
195
+ expect(screen.getByRole('tooltip', { name: 'Attach' })).toBeTruthy();
196
+ });
187
197
  it('can handle buttonProps props appropriately for attach', () => __awaiter(void 0, void 0, void 0, function* () {
188
198
  render(React.createElement(MessageBar, { onSendMessage: jest.fn, hasAttachButton: true, buttonProps: { attach: { props: { 'aria-label': 'Test' } } } }));
189
199
  yield userEvent.click(screen.getByRole('button', { name: 'Test' }));
@@ -205,6 +215,11 @@ describe('Message bar', () => {
205
215
  yield userEvent.click(screen.getByRole('button', { name: 'Stop' }));
206
216
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
207
217
  }));
218
+ it('can handle buttonProps tooltipProps appropriately for stop', () => {
219
+ render(React.createElement(MessageBar, { onSendMessage: jest.fn, hasStopButton: true, handleStopButton: jest.fn, buttonProps: { stop: { tooltipProps: { isVisible: true } } } }));
220
+ // isVisible, so no need for click
221
+ expect(screen.getByRole('tooltip', { name: 'Stop' })).toBeTruthy();
222
+ });
208
223
  it('can handle buttonProps props appropriately for stop', () => __awaiter(void 0, void 0, void 0, function* () {
209
224
  render(React.createElement(MessageBar, { onSendMessage: jest.fn, hasStopButton: true, handleStopButton: jest.fn, buttonProps: { stop: { props: { 'aria-label': 'Test' } } } }));
210
225
  yield userEvent.click(screen.getByRole('button', { name: 'Test' }));
@@ -237,6 +252,11 @@ describe('Message bar', () => {
237
252
  const input = screen.getByRole('textbox', { name: /I am listening/i });
238
253
  expect(input).toBeTruthy();
239
254
  }));
255
+ it('can handle buttonProps tooltipProps appropriately for microphone', () => {
256
+ render(React.createElement(MessageBar, { onSendMessage: jest.fn, hasMicrophoneButton: true, buttonProps: { microphone: { tooltipProps: { isVisible: true } } } }));
257
+ // isVisible, so no need for click
258
+ expect(screen.getByRole('tooltip', { name: 'Use microphone' })).toBeTruthy();
259
+ });
240
260
  it('can handle buttonProps props appropriately for microphone', () => __awaiter(void 0, void 0, void 0, function* () {
241
261
  mockSpeechRecognition();
242
262
  render(React.createElement(MessageBar, { onSendMessage: jest.fn, hasMicrophoneButton: true, buttonProps: { microphone: { props: { 'aria-label': 'Test' } } } }));
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { TooltipProps } from '@patternfly/react-core';
3
- export interface ResponseActionButtonProps {
2
+ import { ButtonProps, TooltipProps } from '@patternfly/react-core';
3
+ export interface ResponseActionButtonProps extends ButtonProps {
4
4
  /** Aria-label for the button. Defaults to the value of the tooltipContent if none provided */
5
5
  ariaLabel?: string;
6
6
  /** Aria-label for the button, shown when the button is clicked. Defaults to the value of ariaLabel or tooltipContent if not provided. */
@@ -26,9 +26,12 @@ export interface ActionProps extends Omit<ButtonProps, 'ref'> {
26
26
  /** Id for content controlled by the button, such as the feedback form */
27
27
  'aria-controls'?: string;
28
28
  }
29
+ type ExtendedActionProps = ActionProps & {
30
+ [key: string]: any;
31
+ };
29
32
  export interface ResponseActionProps {
30
33
  /** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
31
- actions: Record<string, ActionProps | undefined> & {
34
+ actions: Record<string, ExtendedActionProps | undefined> & {
32
35
  positive?: ActionProps;
33
36
  negative?: ActionProps;
34
37
  copy?: ActionProps;
@@ -33,14 +33,14 @@ export const ResponseActions = ({ actions }) => {
33
33
  onClick && onClick(e);
34
34
  };
35
35
  return (React.createElement("div", { ref: responseActions, className: "pf-chatbot__response-actions" },
36
- positive && (React.createElement(ResponseActionButton, { 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: React.createElement(OutlinedThumbsUpIcon, null), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] })),
37
- negative && (React.createElement(ResponseActionButton, { 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: React.createElement(OutlinedThumbsDownIcon, null), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] })),
38
- copy && (React.createElement(ResponseActionButton, { 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: React.createElement(OutlinedCopyIcon, null), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] })),
39
- share && (React.createElement(ResponseActionButton, { 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: React.createElement(ExternalLinkAltIcon, null), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] })),
40
- listen && (React.createElement(ResponseActionButton, { ariaLabel: (_s = listen.ariaLabel) !== null && _s !== void 0 ? _s : 'Listen', clickedAriaLabel: (_t = listen.ariaLabel) !== null && _t !== void 0 ? _t : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_u = listen.tooltipContent) !== null && _u !== void 0 ? _u : 'Listen', clickedTooltipContent: (_v = listen.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Listening', tooltipProps: listen.tooltipProps, icon: React.createElement(VolumeUpIcon, null), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] })),
36
+ positive && (React.createElement(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: React.createElement(OutlinedThumbsUpIcon, null), isClicked: activeButton === 'positive', ref: positive.ref, "aria-expanded": positive['aria-expanded'], "aria-controls": positive['aria-controls'] }))),
37
+ negative && (React.createElement(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: React.createElement(OutlinedThumbsDownIcon, null), isClicked: activeButton === 'negative', ref: negative.ref, "aria-expanded": negative['aria-expanded'], "aria-controls": negative['aria-controls'] }))),
38
+ copy && (React.createElement(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: React.createElement(OutlinedCopyIcon, null), isClicked: activeButton === 'copy', ref: copy.ref, "aria-expanded": copy['aria-expanded'], "aria-controls": copy['aria-controls'] }))),
39
+ share && (React.createElement(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: React.createElement(ExternalLinkAltIcon, null), isClicked: activeButton === 'share', ref: share.ref, "aria-expanded": share['aria-expanded'], "aria-controls": share['aria-controls'] }))),
40
+ listen && (React.createElement(ResponseActionButton, Object.assign({}, listen, { ariaLabel: (_s = listen.ariaLabel) !== null && _s !== void 0 ? _s : 'Listen', clickedAriaLabel: (_t = listen.ariaLabel) !== null && _t !== void 0 ? _t : 'Listening', onClick: (e) => handleClick(e, 'listen', listen.onClick), className: listen.className, isDisabled: listen.isDisabled, tooltipContent: (_u = listen.tooltipContent) !== null && _u !== void 0 ? _u : 'Listen', clickedTooltipContent: (_v = listen.clickedTooltipContent) !== null && _v !== void 0 ? _v : 'Listening', tooltipProps: listen.tooltipProps, icon: React.createElement(VolumeUpIcon, null), isClicked: activeButton === 'listen', ref: listen.ref, "aria-expanded": listen['aria-expanded'], "aria-controls": listen['aria-controls'] }))),
41
41
  Object.keys(additionalActions).map((action) => {
42
42
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
43
- return (React.createElement(ResponseActionButton, { 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'] }));
43
+ return (React.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'] })));
44
44
  })));
45
45
  };
46
46
  export default ResponseActions;
@@ -47,6 +47,13 @@ const CUSTOM_ACTIONS = [
47
47
  }
48
48
  }
49
49
  ];
50
+ const ALL_ACTIONS_DATA_TEST = [
51
+ { type: 'positive', label: 'Good response', dataTestId: 'positive' },
52
+ { type: 'negative', label: 'Bad response', dataTestId: 'negative' },
53
+ { type: 'copy', label: 'Copy', dataTestId: 'copy' },
54
+ { type: 'share', label: 'Share', dataTestId: 'share' },
55
+ { type: 'listen', label: 'Listen', dataTestId: 'listen' }
56
+ ];
50
57
  describe('ResponseActions', () => {
51
58
  afterEach(() => {
52
59
  jest.clearAllMocks();
@@ -156,6 +163,12 @@ describe('ResponseActions', () => {
156
163
  expect(screen.getByRole('button', { name: label })).toHaveClass('test');
157
164
  });
158
165
  });
166
+ it('should be able to add custom attributes to buttons', () => {
167
+ ALL_ACTIONS_DATA_TEST.forEach(({ type, dataTestId }) => {
168
+ render(React.createElement(ResponseActions, { actions: { [type]: { onClick: jest.fn(), 'data-testid': dataTestId } } }));
169
+ expect(screen.getByTestId(dataTestId)).toBeTruthy();
170
+ });
171
+ });
159
172
  it('should be able to add custom actions', () => {
160
173
  CUSTOM_ACTIONS.forEach((action) => {
161
174
  const key = Object.keys(action)[0];
@@ -165,10 +178,12 @@ describe('ResponseActions', () => {
165
178
  onClick: action[key].onClick,
166
179
  // doing this just because it's easier to test without a regex for the button name
167
180
  ariaLabel: action[key].ariaLabel.toLowerCase(),
168
- icon: action[key].icon
181
+ icon: action[key].icon,
182
+ 'data-testid': action[key]
169
183
  }
170
184
  } }));
171
185
  expect(screen.getByRole('button', { name: key })).toBeTruthy();
186
+ expect(screen.getByTestId(action[key])).toBeTruthy();
172
187
  });
173
188
  });
174
189
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/chatbot",
3
- "version": "6.3.0-prerelease.11",
3
+ "version": "6.3.0-prerelease.13",
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",
@@ -148,6 +148,17 @@ describe('Message bar', () => {
148
148
  await userEvent.click(screen.getByRole('button', { name: 'Send' }));
149
149
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
150
150
  });
151
+ it('can handle buttonProps tooltipProps appropriately for send', () => {
152
+ render(
153
+ <MessageBar
154
+ onSendMessage={jest.fn}
155
+ alwayShowSendButton
156
+ buttonProps={{ send: { tooltipProps: { isVisible: true } } }}
157
+ />
158
+ );
159
+ // isVisible, so no need for click
160
+ expect(screen.getByRole('tooltip', { name: 'Send' })).toBeTruthy();
161
+ });
151
162
  it('can handle buttonProps props appropriately for send', async () => {
152
163
  render(
153
164
  <MessageBar
@@ -236,6 +247,17 @@ describe('Message bar', () => {
236
247
  await userEvent.click(screen.getByRole('button', { name: 'Attach' }));
237
248
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
238
249
  });
250
+ it('can handle buttonProps tooltipProps appropriately for attach', () => {
251
+ render(
252
+ <MessageBar
253
+ onSendMessage={jest.fn}
254
+ alwayShowSendButton
255
+ buttonProps={{ attach: { tooltipProps: { isVisible: true } } }}
256
+ />
257
+ );
258
+ // isVisible, so no need for click
259
+ expect(screen.getByRole('tooltip', { name: 'Attach' })).toBeTruthy();
260
+ });
239
261
  it('can handle buttonProps props appropriately for attach', async () => {
240
262
  render(
241
263
  <MessageBar
@@ -271,6 +293,18 @@ describe('Message bar', () => {
271
293
  await userEvent.click(screen.getByRole('button', { name: 'Stop' }));
272
294
  expect(screen.getByRole('tooltip', { name: 'Test' })).toBeTruthy();
273
295
  });
296
+ it('can handle buttonProps tooltipProps appropriately for stop', () => {
297
+ render(
298
+ <MessageBar
299
+ onSendMessage={jest.fn}
300
+ hasStopButton
301
+ handleStopButton={jest.fn}
302
+ buttonProps={{ stop: { tooltipProps: { isVisible: true } } }}
303
+ />
304
+ );
305
+ // isVisible, so no need for click
306
+ expect(screen.getByRole('tooltip', { name: 'Stop' })).toBeTruthy();
307
+ });
274
308
  it('can handle buttonProps props appropriately for stop', async () => {
275
309
  render(
276
310
  <MessageBar
@@ -317,6 +351,17 @@ describe('Message bar', () => {
317
351
  const input = screen.getByRole('textbox', { name: /I am listening/i });
318
352
  expect(input).toBeTruthy();
319
353
  });
354
+ it('can handle buttonProps tooltipProps appropriately for microphone', () => {
355
+ render(
356
+ <MessageBar
357
+ onSendMessage={jest.fn}
358
+ hasMicrophoneButton
359
+ buttonProps={{ microphone: { tooltipProps: { isVisible: true } } }}
360
+ />
361
+ );
362
+ // isVisible, so no need for click
363
+ expect(screen.getByRole('tooltip', { name: 'Use microphone' })).toBeTruthy();
364
+ });
320
365
  it('can handle buttonProps props appropriately for microphone', async () => {
321
366
  mockSpeechRecognition();
322
367
  render(
@@ -279,6 +279,7 @@ export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
279
279
  onClick={handleStopButton}
280
280
  tooltipContent={buttonProps?.stop?.tooltipContent}
281
281
  isCompact={isCompact}
282
+ tooltipProps={buttonProps?.stop?.tooltipProps}
282
283
  {...buttonProps?.stop?.props}
283
284
  />
284
285
  );
@@ -292,6 +293,7 @@ export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
292
293
  isDisabled={isListeningMessage}
293
294
  tooltipContent={buttonProps?.attach?.tooltipContent}
294
295
  isCompact={isCompact}
296
+ tooltipProps={buttonProps?.attach?.tooltipProps}
295
297
  {...buttonProps?.attach?.props}
296
298
  />
297
299
  )}
@@ -302,6 +304,7 @@ export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
302
304
  tooltipContent={buttonProps?.attach?.tooltipContent}
303
305
  inputTestId={buttonProps?.attach?.inputTestId}
304
306
  isCompact={isCompact}
307
+ tooltipProps={buttonProps?.attach?.tooltipProps}
305
308
  {...buttonProps?.attach?.props}
306
309
  />
307
310
  )}
@@ -313,6 +316,7 @@ export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
313
316
  tooltipContent={buttonProps?.microphone?.tooltipContent}
314
317
  language={buttonProps?.microphone?.language}
315
318
  isCompact={isCompact}
319
+ tooltipProps={buttonProps?.microphone?.tooltipProps}
316
320
  {...buttonProps?.microphone?.props}
317
321
  />
318
322
  )}
@@ -323,6 +327,7 @@ export const MessageBar: React.FunctionComponent<MessageBarProps> = ({
323
327
  isDisabled={isSendButtonDisabled}
324
328
  tooltipContent={buttonProps?.send?.tooltipContent}
325
329
  isCompact={isCompact}
330
+ tooltipProps={buttonProps?.send?.tooltipProps}
326
331
  {...buttonProps?.send?.props}
327
332
  />
328
333
  )}
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import { Button, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
2
+ import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
3
3
 
4
- export interface ResponseActionButtonProps {
4
+ export interface ResponseActionButtonProps extends ButtonProps {
5
5
  /** Aria-label for the button. Defaults to the value of the tooltipContent if none provided */
6
6
  ariaLabel?: string;
7
7
  /** Aria-label for the button, shown when the button is clicked. Defaults to the value of ariaLabel or tooltipContent if not provided. */
@@ -41,6 +41,14 @@ const CUSTOM_ACTIONS = [
41
41
  }
42
42
  ];
43
43
 
44
+ const ALL_ACTIONS_DATA_TEST = [
45
+ { type: 'positive', label: 'Good response', dataTestId: 'positive' },
46
+ { type: 'negative', label: 'Bad response', dataTestId: 'negative' },
47
+ { type: 'copy', label: 'Copy', dataTestId: 'copy' },
48
+ { type: 'share', label: 'Share', dataTestId: 'share' },
49
+ { type: 'listen', label: 'Listen', dataTestId: 'listen' }
50
+ ];
51
+
44
52
  describe('ResponseActions', () => {
45
53
  afterEach(() => {
46
54
  jest.clearAllMocks();
@@ -181,6 +189,13 @@ describe('ResponseActions', () => {
181
189
  });
182
190
  });
183
191
 
192
+ it('should be able to add custom attributes to buttons', () => {
193
+ ALL_ACTIONS_DATA_TEST.forEach(({ type, dataTestId }) => {
194
+ render(<ResponseActions actions={{ [type]: { onClick: jest.fn(), 'data-testid': dataTestId } }} />);
195
+ expect(screen.getByTestId(dataTestId)).toBeTruthy();
196
+ });
197
+ });
198
+
184
199
  it('should be able to add custom actions', () => {
185
200
  CUSTOM_ACTIONS.forEach((action) => {
186
201
  const key = Object.keys(action)[0];
@@ -192,12 +207,14 @@ describe('ResponseActions', () => {
192
207
  onClick: action[key].onClick,
193
208
  // doing this just because it's easier to test without a regex for the button name
194
209
  ariaLabel: action[key].ariaLabel.toLowerCase(),
195
- icon: action[key].icon
210
+ icon: action[key].icon,
211
+ 'data-testid': action[key]
196
212
  }
197
213
  }}
198
214
  />
199
215
  );
200
216
  expect(screen.getByRole('button', { name: key })).toBeTruthy();
217
+ expect(screen.getByTestId(action[key])).toBeTruthy();
201
218
  });
202
219
  });
203
220
  });
@@ -36,9 +36,12 @@ export interface ActionProps extends Omit<ButtonProps, 'ref'> {
36
36
  'aria-controls'?: string;
37
37
  }
38
38
 
39
+ type ExtendedActionProps = ActionProps & {
40
+ [key: string]: any;
41
+ };
39
42
  export interface ResponseActionProps {
40
43
  /** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
41
- actions: Record<string, ActionProps | undefined> & {
44
+ actions: Record<string, ExtendedActionProps | undefined> & {
42
45
  positive?: ActionProps;
43
46
  negative?: ActionProps;
44
47
  copy?: ActionProps;
@@ -78,6 +81,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
78
81
  <div ref={responseActions} className="pf-chatbot__response-actions">
79
82
  {positive && (
80
83
  <ResponseActionButton
84
+ {...positive}
81
85
  ariaLabel={positive.ariaLabel ?? 'Good response'}
82
86
  clickedAriaLabel={positive.ariaLabel ?? 'Response recorded'}
83
87
  onClick={(e) => handleClick(e, 'positive', positive.onClick)}
@@ -95,6 +99,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
95
99
  )}
96
100
  {negative && (
97
101
  <ResponseActionButton
102
+ {...negative}
98
103
  ariaLabel={negative.ariaLabel ?? 'Bad response'}
99
104
  clickedAriaLabel={negative.ariaLabel ?? 'Response recorded'}
100
105
  onClick={(e) => handleClick(e, 'negative', negative.onClick)}
@@ -112,6 +117,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
112
117
  )}
113
118
  {copy && (
114
119
  <ResponseActionButton
120
+ {...copy}
115
121
  ariaLabel={copy.ariaLabel ?? 'Copy'}
116
122
  clickedAriaLabel={copy.ariaLabel ?? 'Copied'}
117
123
  onClick={(e) => handleClick(e, 'copy', copy.onClick)}
@@ -129,6 +135,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
129
135
  )}
130
136
  {share && (
131
137
  <ResponseActionButton
138
+ {...share}
132
139
  ariaLabel={share.ariaLabel ?? 'Share'}
133
140
  clickedAriaLabel={share.ariaLabel ?? 'Shared'}
134
141
  onClick={(e) => handleClick(e, 'share', share.onClick)}
@@ -146,6 +153,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
146
153
  )}
147
154
  {listen && (
148
155
  <ResponseActionButton
156
+ {...listen}
149
157
  ariaLabel={listen.ariaLabel ?? 'Listen'}
150
158
  clickedAriaLabel={listen.ariaLabel ?? 'Listening'}
151
159
  onClick={(e) => handleClick(e, 'listen', listen.onClick)}
@@ -163,6 +171,7 @@ export const ResponseActions: React.FunctionComponent<ResponseActionProps> = ({
163
171
  )}
164
172
  {Object.keys(additionalActions).map((action) => (
165
173
  <ResponseActionButton
174
+ {...additionalActions[action]}
166
175
  key={action}
167
176
  ariaLabel={additionalActions[action]?.ariaLabel}
168
177
  clickedAriaLabel={additionalActions[action]?.clickedAriaLabel}