Skip to content

Commit a5123a0

Browse files
Lightning00BladeDevtools-frontend LUCI CQ
authored andcommitted
[AiAssistance] Disable send button when no text input
Also, making sure submit feedback button is disabled initially. Fixed: 399303164, 367863700 Change-Id: I2cd6fca65f587d3b4c50a13b8b22b87db5a11a50 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6306723 Auto-Submit: Samiya Caur <[email protected]> Reviewed-by: Alex Rudenko <[email protected]> Commit-Queue: Nikolay Vitkov <[email protected]>
1 parent 8eb443b commit a5123a0

File tree

6 files changed

+70
-12
lines changed

6 files changed

+70
-12
lines changed

front_end/panels/ai_assistance/AiAssistancePanel.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,35 @@ describeWithMockConnection('AI Assistance Panel', () => {
11711171
assert.strictEqual(updatedViewInput.inputPlaceholder, 'Select an element to ask a question');
11721172
});
11731173
});
1174+
1175+
it('should disable the send button when the input is empty', async () => {
1176+
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
1177+
updateHostConfig({
1178+
devToolsFreestyler: {
1179+
enabled: true,
1180+
},
1181+
});
1182+
1183+
UI.Context.Context.instance().setFlavor(
1184+
Elements.ElementsPanel.ElementsPanel, sinon.createStubInstance(Elements.ElementsPanel.ElementsPanel));
1185+
const {panel, expectViewUpdate} =
1186+
await createAiAssistancePanel({aidaAvailability: Host.AidaClient.AidaAccessPreconditions.AVAILABLE});
1187+
1188+
const updateViewInput = await expectViewUpdate(() => {
1189+
panel.handleAction('freestyler.elements-floating-button');
1190+
});
1191+
assert.isTrue(updateViewInput.isTextInputEmpty);
1192+
1193+
const updateViewInputAfterTextInputAdded = await expectViewUpdate(() => {
1194+
updateViewInput.onTextInputChange('test');
1195+
});
1196+
assert.isFalse(updateViewInputAfterTextInputAdded.isTextInputEmpty);
1197+
1198+
const updateViewInputAfterTextInputRemoved = await expectViewUpdate(() => {
1199+
updateViewInputAfterTextInputAdded.onTextInputChange('');
1200+
});
1201+
assert.isTrue(updateViewInputAfterTextInputRemoved.isTextInputEmpty);
1202+
});
11741203
});
11751204

11761205
describe('multimodal input', () => {

front_end/panels/ai_assistance/AiAssistancePanel.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
441441
accountFullName?: string,
442442
};
443443
#imageInput = '';
444+
// Used to disable send button when there is not text input.
445+
#isTextInputEmpty = true;
444446

445447
constructor(private view: View = defaultView, {aidaClient, aidaAvailability, syncInfo}: {
446448
aidaClient: Host.AidaClient.AidaClient,
@@ -809,6 +811,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
809811
emptyStateSuggestions: this.#conversation ? getEmptyStateSuggestions(this.#conversation.type) : [],
810812
inputPlaceholder: this.#getChatInputPlaceholder(),
811813
disclaimerText: this.#getDisclaimerText(),
814+
isTextInputEmpty: this.#isTextInputEmpty,
812815
onNewChatClick: this.#handleNewChatRequest.bind(this),
813816
onHistoryClick: this.#onHistoryClicked.bind(this),
814817
onDeleteClick: this.#onDeleteClicked.bind(this),
@@ -831,6 +834,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
831834
onTakeScreenshot: isAiAssistanceMultimodalInputEnabled() ? this.#handleTakeScreenshot.bind(this) : undefined,
832835
onRemoveImageInput: isAiAssistanceMultimodalInputEnabled() ? this.#handleRemoveImageInput.bind(this) :
833836
undefined,
837+
onTextInputChange: this.#handleTextInputChange.bind(this),
834838
},
835839
this.#viewOutput, this.contentElement);
836840
}
@@ -1135,6 +1139,14 @@ export class AiAssistancePanel extends UI.Panel.Panel {
11351139
this.requestUpdate();
11361140
}
11371141

1142+
#handleTextInputChange(value: string): void {
1143+
const disableSubmit = !value;
1144+
if (disableSubmit !== this.#isTextInputEmpty) {
1145+
this.#isTextInputEmpty = disableSubmit;
1146+
void this.requestUpdate();
1147+
}
1148+
}
1149+
11381150
#runAbortController = new AbortController();
11391151
#cancel(): void {
11401152
this.#runAbortController.abort();

front_end/panels/ai_assistance/components/ChatView.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describeWithEnvironment('ChatView', () => {
2121
onCancelClick: noop,
2222
onContextClick: noop,
2323
onNewConversation: noop,
24+
onTextInputChange: noop,
2425
inspectElementToggled: false,
2526
state: AiAssistance.State.CHAT_VIEW,
2627
conversationType: AiAssistance.ConversationType.STYLING,
@@ -36,6 +37,7 @@ describeWithEnvironment('ChatView', () => {
3637
emptyStateSuggestions: [],
3738
inputPlaceholder: i18n.i18n.lockedString('input placeholder'),
3839
disclaimerText: i18n.i18n.lockedString('disclaimer text'),
40+
isTextInputEmpty: true,
3941
...options,
4042
};
4143
}

front_end/panels/ai_assistance/components/ChatView.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ export interface Props {
268268
onNewConversation: () => void;
269269
onTakeScreenshot?: () => void;
270270
onRemoveImageInput?: () => void;
271+
onTextInputChange: (input: string) => void;
271272
inspectElementToggled: boolean;
272273
state: State;
273274
aidaAvailability: Host.AidaClient.AidaAccessPreconditions;
@@ -290,6 +291,7 @@ export interface Props {
290291
emptyStateSuggestions: string[];
291292
inputPlaceholder: Platform.UIString.LocalizedString;
292293
disclaimerText: Platform.UIString.LocalizedString;
294+
isTextInputEmpty: boolean;
293295
}
294296

295297
export class ChatView extends HTMLElement {
@@ -513,6 +515,7 @@ export class ChatView extends HTMLElement {
513515
multimodalInputEnabled: this.#props.multimodalInputEnabled,
514516
conversationType: this.#props.conversationType,
515517
imageInput: this.#props.imageInput,
518+
isTextInputEmpty: this.#props.isTextInputEmpty,
516519
onContextClick: this.#props.onContextClick,
517520
onInspectElementClick: this.#props.onInspectElementClick,
518521
onSubmit: this.#handleSubmit,
@@ -521,6 +524,7 @@ export class ChatView extends HTMLElement {
521524
onNewConversation: this.#props.onNewConversation,
522525
onTakeScreenshot: this.#props.onTakeScreenshot,
523526
onRemoveImageInput: this.#props.onRemoveImageInput,
527+
onTextInputChange: this.#props.onTextInputChange,
524528
})
525529
}
526530
</main>
@@ -1073,13 +1077,15 @@ function renderReadOnlySection({onNewConversation, conversationType}: {
10731077
// clang-format on
10741078
}
10751079

1076-
function renderChatInputButtons({isLoading, blockedByCrossOrigin, isTextInputDisabled, onCancel, onNewConversation}: {
1077-
isLoading: boolean,
1078-
blockedByCrossOrigin: boolean,
1079-
isTextInputDisabled: boolean,
1080-
onCancel: (ev: SubmitEvent) => void,
1081-
onNewConversation: () => void,
1082-
}): Lit.TemplateResult {
1080+
function renderChatInputButtons(
1081+
{isLoading, blockedByCrossOrigin, isTextInputDisabled, isTextInputEmpty, onCancel, onNewConversation}: {
1082+
isLoading: boolean,
1083+
blockedByCrossOrigin: boolean,
1084+
isTextInputDisabled: boolean,
1085+
isTextInputEmpty: boolean,
1086+
onCancel: (ev: SubmitEvent) => void,
1087+
onNewConversation: () => void,
1088+
}): Lit.TemplateResult {
10831089
if (isLoading) {
10841090
// clang-format off
10851091
return html`<devtools-button
@@ -1126,7 +1132,7 @@ function renderChatInputButtons({isLoading, blockedByCrossOrigin, isTextInputDis
11261132
type: 'submit',
11271133
variant: Buttons.Button.Variant.ICON,
11281134
size: Buttons.Button.Size.REGULAR,
1129-
disabled: isTextInputDisabled,
1135+
disabled: isTextInputDisabled || isTextInputEmpty,
11301136
iconName: 'send',
11311137
title: lockedString(UIStringsNotTranslate.sendButtonTitle),
11321138
jslogContext: 'send',
@@ -1207,6 +1213,7 @@ function renderChatInput({
12071213
multimodalInputEnabled,
12081214
conversationType,
12091215
imageInput,
1216+
isTextInputEmpty,
12101217
onContextClick,
12111218
onInspectElementClick,
12121219
onSubmit,
@@ -1215,6 +1222,7 @@ function renderChatInput({
12151222
onNewConversation,
12161223
onTakeScreenshot,
12171224
onRemoveImageInput,
1225+
onTextInputChange,
12181226
}: {
12191227
isLoading: boolean,
12201228
blockedByCrossOrigin: boolean,
@@ -1226,6 +1234,7 @@ function renderChatInput({
12261234
multimodalInputEnabled?: boolean,
12271235
conversationType?: ConversationType,
12281236
imageInput?: string,
1237+
isTextInputEmpty: boolean,
12291238
onContextClick: () => void ,
12301239
onInspectElementClick: () => void,
12311240
onSubmit: (ev: SubmitEvent) => void,
@@ -1234,6 +1243,7 @@ function renderChatInput({
12341243
onNewConversation: () => void,
12351244
onTakeScreenshot?: () => void,
12361245
onRemoveImageInput?: () => void,
1246+
onTextInputChange: (input: string) => void,
12371247
}): Lit.LitTemplate {
12381248
if (!conversationType) {
12391249
return Lit.nothing;
@@ -1278,14 +1288,15 @@ function renderChatInput({
12781288
wrap="hard"
12791289
maxlength="10000"
12801290
@keydown=${onTextAreaKeyDown}
1291+
@input=${(event: KeyboardEvent) => onTextInputChange((event.target as HTMLInputElement).value)}
12811292
placeholder=${inputPlaceholder}
12821293
jslog=${VisualLogging.textField('query').track({ keydown: 'Enter' })}
12831294
></textarea>
12841295
<div class="chat-input-buttons">
12851296
${renderTakeScreenshotButton({
12861297
multimodalInputEnabled, blockedByCrossOrigin, isTextInputDisabled, onTakeScreenshot
12871298
})}
1288-
${renderChatInputButtons({ isLoading, blockedByCrossOrigin, isTextInputDisabled, onCancel, onNewConversation })}
1299+
${renderChatInputButtons({ isLoading, blockedByCrossOrigin, isTextInputDisabled, isTextInputEmpty, onCancel, onNewConversation })}
12891300
</div>
12901301
</div>
12911302
</form>

front_end/panels/ai_assistance/components/UserActionRow.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,21 @@ describeWithEnvironment('UserActionRow', () => {
7878

7979
{
8080
const [viewInput] = view.lastCall.args;
81-
expect(viewInput.isSubmitButtonDisabled).equals(false);
81+
expect(viewInput.isSubmitButtonDisabled).equals(true);
8282
viewInput.onRatingClick(Host.AidaClient.Rating.POSITIVE);
8383
}
8484

8585
assert.isTrue(view.calledTwice);
8686

8787
{
8888
const [viewInput] = view.lastCall.args;
89-
expect(viewInput.isSubmitButtonDisabled).equals(false);
9089
expect(viewInput.isShowingFeedbackForm).equals(true);
9190
viewInput.onInputChange('test');
91+
}
92+
93+
{
94+
const [viewInput] = view.lastCall.args;
95+
expect(viewInput.isSubmitButtonDisabled).equals(false);
9296
viewInput.onSubmit(new SubmitEvent('submit'));
9397
}
9498

front_end/panels/ai_assistance/components/UserActionRow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export class UserActionRow extends UI.Widget.Widget implements UserActionRowWidg
130130
#feedbackValue = '';
131131
#currentRating: Host.AidaClient.Rating|undefined;
132132
#isShowingFeedbackForm = false;
133-
#isSubmitButtonDisabled = false;
133+
#isSubmitButtonDisabled = true;
134134

135135
#view: View;
136136
#viewOutput: ViewOutput = {};

0 commit comments

Comments
 (0)