-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathThumbButton.tsx
More file actions
103 lines (91 loc) · 3.46 KB
/
ThumbButton.tsx
File metadata and controls
103 lines (91 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { validateProps } from '@msinternal/botframework-webchat-react-valibot';
import { useStyles } from '@msinternal/botframework-webchat-styles/react';
import { hooks } from 'botframework-webchat-api';
import cx from 'classnames';
import React, { forwardRef, memo, useCallback, useMemo, type ForwardedRef, type KeyboardEventHandler } from 'react';
import { useRefFrom } from 'use-ref-from';
import { boolean, function_, literal, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot';
import testIds from '../../testIds';
import { Tooltip } from '../../Tooltip';
import ThumbButtonImage from './ThumbButton.Image';
import styles from './ThumbButton.module.css';
const { useLocalizer } = hooks;
const thumbButtonPropsSchema = pipe(
object({
as: union([literal('button'), literal('radio')]),
className: optional(string()),
direction: union([literal('down'), literal('up')]),
disabled: optional(boolean()),
name: string(),
onClick: optional(function_()),
pressed: optional(boolean()),
size: union([literal('small'), literal('large')]),
submitted: optional(boolean()),
title: optional(string())
}),
readonly()
);
type ThumbButtonProps = InferInput<typeof thumbButtonPropsSchema>;
function ThumbButton(props: ThumbButtonProps, ref: ForwardedRef<HTMLInputElement>) {
const { as, className, direction, disabled, name, onClick, pressed, size, submitted, title } = validateProps(
thumbButtonPropsSchema,
props
);
const localize = useLocalizer();
const onClickRef = useRefFrom(onClick);
const classNames = useStyles(styles);
const buttonTitle = useMemo(
() => title ?? localize(direction === 'down' ? 'VOTE_DISLIKE_ALT' : 'VOTE_LIKE_ALT'),
[direction, localize, title]
);
const handleClickOrChange = useCallback(() => !disabled && onClickRef.current?.(), [disabled, onClickRef]);
const handleKeyDown = useCallback<KeyboardEventHandler<HTMLInputElement>>(event => {
// Do not submit the <form> via ENTER key.
event.key === 'Enter' && event.preventDefault();
}, []);
return (
<div
className={cx(
classNames['thumb-button'],
{
[classNames['thumb-button--large']]: size === 'large',
[classNames['thumb-button--has-submitted']]: submitted
},
className
)}
>
<input
aria-disabled={disabled ? 'true' : undefined}
aria-label={buttonTitle}
className={cx(classNames['thumb-button__input'], className)}
data-testid={testIds.feedbackButton}
name={name}
onKeyDown={handleKeyDown}
ref={ref}
{...(as === 'radio'
? {
checked: pressed,
onChange: handleClickOrChange,
type: 'radio'
}
: {
'aria-pressed': pressed,
onClick: handleClickOrChange,
type: 'button'
})}
/>
<ThumbButtonImage
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-stroked'])}
direction={direction}
/>
<ThumbButtonImage
className={cx(classNames['thumb-button__image'], classNames['thumb-button__image--is-filled'])}
direction={direction}
filled={true}
/>
<Tooltip className={classNames['thumb-button__tooltip']}>{buttonTitle}</Tooltip>
</div>
);
}
export default memo(forwardRef<HTMLInputElement, ThumbButtonProps>(ThumbButton));
export { thumbButtonPropsSchema, type ThumbButtonProps };