Skip to content

Commit 3805480

Browse files
docs(ChatInput): document the attached feedback variant
1 parent 0930cbe commit 3805480

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

  • packages/blade/src/components/ChatInput/_decisions

packages/blade/src/components/ChatInput/_decisions/decisions.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ type ChatInputProps = {
230230
*/
231231
accessibilityLabel?: string;
232232

233+
/**
234+
* Attaches a feedback prompt to the top of the composer, on a shared surface. Web only.
235+
* Omit it and the composer is unchanged. See "Attached Feedback Prompt" below.
236+
*/
237+
feedback?: ChatInputFeedbackProps;
238+
233239
/**
234240
* Test ID for automation testing
235241
*/
@@ -449,6 +455,79 @@ The uploading guard prevents `onSubmit` from receiving a not-yet-ready file in `
449455
450456
When submit is disabled because of an error or uploading file, no additional tooltip or `errorText` is surfaced on the submit button itself. The visual feedback is already present on the `FileUploadItem` chip (red error state, spinner for uploading). Adding a redundant tooltip or `errorText` slot message would duplicate that signal without adding clarity. If a consumer needs custom messaging they can use the `validationState` / `errorText` props.
451457
458+
## Attached Feedback Prompt
459+
460+
Passing a `feedback` object attaches a `ChatFeedback` flow to the composer's top edge, the two
461+
sharing one tinted surface so they read as a single object. There is no `showFeedback` boolean —
462+
passing the object is the switch, the same way `Tooltip` has no `showTitle`. Omit it and the
463+
composer renders byte-for-byte as it does without the feature: the surface properties are only
464+
emitted when the prop is present, rather than set to transparent and zero.
465+
466+
```ts
467+
type ChatInputFeedbackProps = Pick<
468+
ChatFeedbackProps,
469+
'question' | 'moodConfig' | 'moodIcons' | 'isDisabled' | 'onMoodSelect' | 'onSubmit' | 'onDismiss'
470+
> & {
471+
/** @default true */
472+
isVisible?: boolean;
473+
/** The tag that collects free text instead of standing alone. @default 'Other' */
474+
freeTextTag?: string;
475+
/** Placeholder while the composer is collecting that text. @default 'Anything else? (optional)' */
476+
commentPlaceholder?: string;
477+
};
478+
```
479+
480+
`isFullWidth`, `isSubmitHidden`, `controlsRef`, `comment` and `onTagsChange` are deliberately not
481+
forwarded: they describe how the flow is laid out and driven, which is the composer's business
482+
rather than the caller's.
483+
484+
### Why the prompt lives inside ChatInput
485+
486+
The obvious composition — render `ChatFeedback` above a `ChatInput` — is silently broken. The
487+
composer keeps its validation region mounted directly above the card even when there is no error,
488+
and as a full-width transparent box it swallows clicks aimed at anything stacked there. Measured
489+
against a mood scale placed in that space, it covered the lower two-thirds of every button: hover
490+
fired late and a click near the middle of a control did nothing, with nothing on screen to explain
491+
why.
492+
493+
Owning the prompt here means the layer it sits on is decided in the same file as the layer the
494+
error region sits on, so the two cannot be composed into conflict.
495+
496+
### The composer takeover
497+
498+
Picking `freeTextTag` hands the composer over to the feedback flow:
499+
500+
| | |
501+
|---|---|
502+
| Placeholder | becomes `commentPlaceholder` |
503+
| Chat draft | stashed on entry, restored on exit |
504+
| Action bar | upload link replaced by a dismissable `Feedback` tag and an `esc to cancel` hint |
505+
| Focus | moves to the composer |
506+
| Enter | submits the feedback; the chat path is **blocked**, not redirected |
507+
| The flow's own tick | hidden — the composer's send arrow is the submit |
508+
| Exits | Esc · the tag's dismiss · submitting · deselecting the tag · the prompt going away |
509+
510+
Two properties here are silent when wrong, and both have unit tests:
511+
512+
**The draft belongs to the user.** Without stashing it, picking the tag destroys a half-written
513+
message with no warning and no undo.
514+
515+
**Enter must never reach the chat.** Sending someone's candid feedback to the assistant as a prompt
516+
is not a recoverable mistake, so the path is blocked outright rather than merely redirected.
517+
518+
**Every exit also releases the tag.** Leaving it selected strands the user: the tick stays hidden
519+
because the only tag picked is the free-text one, and the composer has gone back to chatting — a
520+
choice made with no way left to send it. This was reached by three separate routes during
521+
development (Esc, submit, and the back control) before the exit was made common to all of them.
522+
523+
### Reading the mode from a ref
524+
525+
`ChatFeedback`'s callbacks can be invoked from a memoised closure belonging to an earlier render, so
526+
a state copy read inside them may say the mode is off when it is on. The mode is mirrored in a ref
527+
and read from there. Leaving the mode is also re-entrant — releasing the tag fires `onTagsChange`,
528+
which routes back into the same exit — so the exit returns early when the ref says it has already
529+
run, rather than restoring an already-cleared draft over the one just put back.
530+
452531
## Accessibility
453532
454533
- **Keyboard Navigation:**

0 commit comments

Comments
 (0)