Feedback · Feedback

Feedback

Quick feedback component

Demos

How to import

import { Feedback } from '@fibrous/semi-ui-react';

Basic usage

Use visible to set whether to display or not. The default feedback display content is in emoji form. The currently selected content can be obtained through onValueChange.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button, type FeedbackValue } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);
    const onValueChange = useCallback((value: FeedbackValue) => {
        console.log('emoji value', value);
    }, []);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Popup, emoji</Button>
            <Feedback title="您对本产品的评分是?" visible={visible} onOk={handleOk} onCancel={handleCancel} onValueChange={onValueChange} />
        </>
    );
}

Text type

Set type to text to get feedback in the form of a multi-line input box, and set the parameters of the multi-line input box through textAreaProps.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Popup, text</Button>
            <Feedback type="text" textAreaProps={{ maxCount: 200 }} title="您对本产品的建议是?" visible={visible} onOk={handleOk} onCancel={handleCancel} />
        </>
    );
}

Single choice feedback

Set type to radio to get feedback in the form of a single selection, and set the parameters of the radio selection through radioGroupProps. The option group has no accessible name by default; name it with aria-label in radioGroupProps.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Popup, radio</Button>
            <Feedback
                type="radio"
                radioGroupProps={{
                    options: ['访客', '开发者', '维护者'],
                }}
                title="您的身份是"
                visible={visible}
                onOk={handleOk}
                onCancel={handleCancel}
            />
        </>
    );
}

Multiple choice feedback

Set type to checkbox to obtain feedback in the form of multiple selections, and set the parameters of the multi selection through checkboxGroupProps. The option group has no accessible name by default; name it with aria-label in checkboxGroupProps.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Popup, checkbox</Button>
            <Feedback
                type="checkbox"
                checkboxGroupProps={{
                    options: ['抖音', '火山', '豆包'],
                }}
                title="您最常使用以下哪些产品?"
                visible={visible}
                onOk={handleOk}
                onCancel={handleCancel}
            />
        </>
    );
}

Customized feedback content

Set type to custom to obtain feedback with customized content, and set the feedback content through renderContent. When using custom feedback, you need to control whether the submit button is disabled or not. Users can set it through okButtonProps.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button, TextArea } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const [value, setValue] = useState('');
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);
    const onTextAreaChange = useCallback((value: string) => {
        setValue(value);
    }, []);

    const renderContent = useCallback(() => {
        return (
            <>
                <span>这是一段自定义的内容</span>
                <TextArea onChange={onTextAreaChange} />
            </>
        );
    }, [onTextAreaChange]);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Popup, custom</Button>
            <Feedback type="custom" okButtonProps={{ disabled: !value }} title="您对本产品的建议是?" visible={visible} onOk={handleOk} onCancel={handleCancel} renderContent={renderContent} />
        </>
    );
}

The form of feedback can be passed through mode, the default is popup, set to modal to get the display in the form of modal dialog box.

Code
import { useState, useCallback } from 'react';
import { Feedback, Button, type FeedbackValue } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible, setVisible] = useState(false);
    const handleOk = useCallback(() => setVisible(false), []);
    const handleCancel = useCallback(() => setVisible(false), []);
    const onValueChange = useCallback((value: FeedbackValue) => {
        console.log('emoji value', value);
    }, []);

    return (
        <>
            <Button onClick={() => setVisible(!visible)}>展示反馈: Modal, emoji</Button>
            <Feedback mode="modal" title="Why did you choose this rating?" visible={visible} onOk={handleOk} onCancel={handleCancel} onValueChange={onValueChange} />
        </>
    );
}

Feedback completion tips

After the feedback is completed, you can switch to display information to remind the user that the feedback has been completed.



Code
import { useState, useCallback, useMemo } from 'react';
import { IllustrationSuccess, IllustrationSuccessDark } from '@fibrous/semi-illustrations-react';
import { Feedback, Button, Empty, TextArea } from '@fibrous/semi-ui-react';

export default function FeedbackDemo() {
    const [visible1, setVisible1] = useState(false);
    const [value1, setValue1] = useState('');
    const [showThanks1, setShowThanks1] = useState(false);

    const [visible2, setVisible2] = useState(false);
    const [value2, setValue2] = useState('');
    const [showThanks2, setShowThanks2] = useState(false);

    const handleOk1 = useCallback(() => {
        setShowThanks1(true);
        setTimeout(() => {
            setVisible1(false);
            setTimeout(() => {
                setShowThanks1(false);
            }, 200);
        }, 1500);
    }, []);

    const hideFeedback1 = useCallback(() => {
        setVisible1(false);
    }, []);

    const onTextAreaChange1 = useCallback((value: string) => {
        setValue1(value);
    }, []);

    const handleOk2 = useCallback(() => {
        setShowThanks2(true);
        setTimeout(() => {
            setVisible2(false);
            setTimeout(() => {
                setShowThanks2(false);
            }, 200);
        }, 1500);
    }, []);

    const hideFeedback2 = useCallback(() => {
        setVisible2(false);
    }, []);

    const onTextAreaChange2 = useCallback((value: string) => {
        setValue2(value);
    }, []);

    const showThankProps = useMemo(() => ({ title: ' ', footer: null }), []);

    return (
        <div>
            <Button onClick={() => setVisible1(!visible1)}>Open Feedback: Popup, Custom</Button>
            <Feedback
                visible={visible1}
                onOk={handleOk1}
                onCancel={hideFeedback1}
                okButtonProps={{
                    disabled: !value1,
                }}
                title="What is your feedback on this product?"
                type="custom"
                {...(showThanks1 ? showThankProps : {})}
                renderContent={() => {
                    return showThanks1 ? (
                        <>
                            <Empty image={<IllustrationSuccess style={{ width: 150, height: 150 }} />} darkModeImage={<IllustrationSuccessDark style={{ width: 150, height: 150 }} />} description="感谢您的反馈" style={{ padding: 30 }} />
                        </>
                    ) : (
                        <>
                            <span>这是一段自定义的内容</span>
                            <TextArea onChange={onTextAreaChange1} />
                        </>
                    );
                }}
            />
            <br />
            <br />
            <Button onClick={() => setVisible2(!visible2)}>Open Feedback: Modal, Custom</Button>
            <Feedback
                visible={visible2}
                onOk={handleOk2}
                onCancel={hideFeedback2}
                okButtonProps={{
                    disabled: !value2,
                }}
                title="What is your feedback on this product?"
                type="custom"
                mode="modal"
                {...(showThanks2 ? showThankProps : {})}
                renderContent={() => {
                    return showThanks2 ? (
                        <>
                            <Empty image={<IllustrationSuccess style={{ width: 150, height: 150 }} />} darkModeImage={<IllustrationSuccessDark style={{ width: 150, height: 150 }} />} description="感谢您的反馈" style={{ padding: 30 }} />
                        </>
                    ) : (
                        <>
                            <span>这是一段自定义的内容</span>
                            <TextArea onChange={onTextAreaChange2} />
                        </>
                    );
                }}
            />
        </div>
    );
}

API reference

FeedbackProps

In addition to the parameters listed below, when mode is modal, FeedbackProps also supports the parameters in ModalProps. When mode is popup, FeedbackProps also supports the parameters in SideSheetProps

Props

PropertyDescriptionTypeDefault
afterCloseCalled after the panel has fully closed, in both modes.Type() => void-
bodyStyleStyle of the content area, passed through to the Modal or SideSheet of the current mode.CSSProperties-
cancelButtonPropsProps of the cancel button. In popup mode it is loading when either the built-in pending state or your loading says so, and onClick runs before the built-in handling.ButtonProps-
checkboxGroupPropsProps of the checkbox group for the checkbox type, written as the props of CheckboxGroup. The group has no accessible name by default; name it with aria-label.CheckboxGroupProps-
childrenFeedback content for type custom.React.ReactNode-
classNameClass name of the panel root, merged with the built-in class names.string-
closableWhether to show the close button. Passed through to the Modal or SideSheet of the current mode.boolean-
closeIconIcon inside the close button. Passed through to the Modal or SideSheet of the current mode.ReactNode-
closeOnEscWhether Esc closes the panel, passed through to the Modal or SideSheet of the current mode. In popup mode it must be true for Esc to call onCancel.boolean-
footerReplaces the footer that holds the default cancel and submit buttons, passed through to the Modal or SideSheet of the current mode. Passing null renders no footer.ReactNode-
getPopupContainerContainer the panel is mounted into. Passed through to the Modal or SideSheet of the current mode.Type() => HTMLElement-
heightHeight of the panel, applies in popup mode only and is passed through to the SideSheet. Popup mode uses auto by default, and a value you give wins.string | number-
keepDOMWhether the DOM is kept after closing. Passed through to the Modal or SideSheet of the current mode.boolean-
maskWhether to show the mask. Passed through to the Modal or SideSheet of the current mode. Popup mode shows no mask by default, and a value you give wins.boolean-
maskClosableWhether a click on the mask closes the panel. Passed through to the Modal or SideSheet of the current mode.boolean-
maskStyleStyle of the mask. Passed through to the Modal or SideSheet of the current mode.CSSProperties-
modeDisplay mode: popup is a non-modal panel at the bottom without a mask (SideSheet); modal is a modal dialog (Modal). Attributes Feedback does not declare go to the SideSheet or Modal of the current mode.'modal' | 'popup'"popup"
motionWhether to animate. Passed through to the Modal or SideSheet of the current mode.boolean-
okButtonPropsProps of the submit button. Without disabled, it is disabled while the content is empty; for type custom, your disabled decides whether submission is allowed. It is loading when either the built-in pending state or your loading says so. onClick runs before the built-in submission, which still runs.ButtonProps-
onCancelCalled by the cancel button, the close button and Esc; the argument is a native KeyboardEvent for Esc. In popup mode a returned Promise keeps the cancel button loading, the filled-in value is cleared only once it resolves, and kept when it rejects. Closing is controlled by visible; the component never closes itself.Type(e: React.MouseEvent | KeyboardEvent) => void | Promise<any>-
onOkCalled when the submit button is clicked, with the click event. In popup mode a returned Promise keeps the submit button loading, the filled-in value is cleared only once it resolves, and kept when it rejects; any other return clears the value at once. Closing is controlled by visible; the component never closes itself.Type(e: React.MouseEvent) => void | Promise<any>-
onValueChangeCalled when the feedback content changes with the current value: a string for text, { emoji, text } for emoji (text is the reason filled in after choosing the bad face), the value of the chosen option for radio, and an array of chosen values for checkbox.Type(value: FeedbackValue) => void-
radioGroupPropsProps of the radio group for the radio type, written as the props of RadioGroup. The group has no accessible name by default; name it with aria-label.RadioGroupProps-
refReference to the outermost container of the panel in the portal: the SideSheet's outermost element in popup mode, and the Modal's outermost element in modal mode. It is null while nothing is rendered.TypeReact.Ref<HTMLDivElement>-
renderContentWraps the feedback content: receives the built-in content (the children for type custom) and returns the wrapped node.Type(content: React.ReactNode) => React.ReactNode-
sizeSize of the panel. Passed through to the Modal or SideSheet of the current mode.Size-
styleInline style, applied to the role="dialog" element in popup mode and to the .semi-modal dialog box in modal mode.CSSProperties-
textAreaPropsProps of the multi-line input for the text and emoji types, written as the props of TextArea. Feedback takes over onChange, and your onChange is still called.TextAreaProps-
titleTitle of the panel.ReactNode-
typeType of the feedback content: emoji, text, radio, checkbox or custom. For custom, the content goes in children or is produced by renderContent.'text' | 'emoji' | 'radio' | 'checkbox' | 'custom'"emoji"
visibleWhether the panel is shown. The component never closes itself; set it to false in onOk or onCancel.boolean-
widthWidth of the panel. Passed through to the Modal or SideSheet of the current mode.string | number-
zIndexStacking order of the panel. Passed through to the Modal or SideSheet of the current mode.number-

Undeclared attributes go to the Modal or SideSheet of the current mode and land where they do there: all aria-* attributes land on the role="dialog" element, and the other undeclared attributes and native event listeners land on the outermost element inside the portal.

Accessibility

ARIA

  • The faces of the emoji type are native input type="radio" elements inside a role="radiogroup" (named by the locale entry Feedback.rating), each named by Feedback.bad, Feedback.normal or Feedback.good. The selected state is expressed by the native checked, following the APG Radio Group
  • The option groups of the radio and checkbox types have no accessible name by default; name them with aria-label in radioGroupProps or checkboxGroupProps
  • modal mode follows the APG Dialog (Modal), see Modal for details. popup mode is a SideSheet without a mask and is treated as a non-modal dialog, so aria-modal is not set

Keyboard and focus

  • The emoji group has a single Tab stop and the arrow keys move the selected face; a face shows the theme focus ring when focused with the keyboard. The radio and checkbox types follow the keyboard behavior of RadioGroup and CheckboxGroup
  • In popup mode, focus moves into the panel on open but Tab is not trapped and the rest of the page stays usable; Esc triggers cancel. On close, focus returns to the element focused before opening only if it is still inside the panel or has fallen to the body; focus the user has moved elsewhere on the page is left alone
  • In modal mode, focus moves into the dialog on open, Tab cycles within the dialog and the page behind is not interactive; focus returns to the previously focused element on close, and Esc triggers cancel