import { ComponentPropsWithoutRef, ReactNode } from 'react';
import { mergeClasses } from '../_base/Button/utils';
import { IconButton } from '../IconButton/IconButton';
import './feedback-bar.css';
export type FeedbackValue = 'down' | 'up';
export interface FeedbackBarProps extends Omit<
ComponentPropsWithoutRef<'div'>,
'onChange'
> {
/**
* Extra actions after the thumbs (for example copy or retry buttons).
*/
children?: ReactNode;
/**
* Additional class names.
*/
className?: string;
/**
* Screen-reader text and tooltip for the thumbs-down button.
*/
downLabel: string;
/**
* Name of the group of buttons, read by screen readers (for example "Rate
* this response").
*/
groupLabel: string;
/**
* Called with the new choice. Pressing the chosen thumb again passes
* `null`.
*/
onValueChange?: (value: FeedbackValue | null) => void;
/**
* Replaces the thumbs with text (for example a thank-you). Announced to
* screen readers when it appears.
*/
statusLabel?: string;
/**
* Screen-reader text and tooltip for the thumbs-up button.
*/
upLabel: string;
/**
* The current choice: `up`, `down` or none.
*/
value?: FeedbackValue | null;
}
/**
* FeedbackBar UI component. Thumbs up and down for rating agent output. It
* reports the choice and does nothing else.
*/
export const FeedbackBar = ({
children,
className,
downLabel,
groupLabel,
onValueChange,
statusLabel,
upLabel,
value = null,
...props
}: FeedbackBarProps) => {
const baseClass = 'pds-feedback-bar';
const choose = (next: FeedbackValue) =>
onValueChange?.(value === next ? null : next);
return (
<div
aria-label={groupLabel}
className={mergeClasses([baseClass, className])}
role='group'
{...props}
>
{!statusLabel && (
<span className={`${baseClass}__actions`}>
<IconButton
ariaLabel={upLabel}
iconName='thumbsUp'
isPressed={value === 'up'}
onClick={() => choose('up')}
size='s'
/>
<IconButton
ariaLabel={downLabel}
iconName='thumbsDown'
isPressed={value === 'down'}
onClick={() => choose('down')}
size='s'
/>
{children}
</span>
)}
<span className={`pds-copy-xs ${baseClass}__status`} role='status'>
{statusLabel}
</span>
</div>
);
};
Feedback Bar
A small atomic component: a thumbs-up and thumbs-down pair with one piece of state, built from the local IconButton (its existing isPressed toggle, which maps to aria-pressed).
Net-new componentOpen in Storybook