Skip to content

Commit

Permalink
fix: simplified ReactionProps's tooltip + removed content and `…
Browse files Browse the repository at this point in the history
…title` from it
  • Loading branch information
Ruminat committed Jul 11, 2024
1 parent 17d7cfe commit 855b5d4
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 63 deletions.
6 changes: 3 additions & 3 deletions src/components/Reactions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ const YourComponent = () => {
() =>
Object.entries(usersReacted).map(
([value, users]): ReactionProps => ({
...option[value as keyof typeof option],
value,
counter: users.length,
selected: users.some(({name}) => name === currentUser.name),
}),
),
[usersReacted],
);

// You can then handle clicking on a reaction with chaning the inital mapping,
// You can then handle clicking on a reaction with changing the inital mapping,
// and the array of ReactionProps will change accordingly
const onClickReaction = React.useCallback(
(value: string) => {
Expand Down Expand Up @@ -97,7 +97,7 @@ For more code examples go to [Reactions.stories.tsx](https://github.com/gravity-
| `className` | `string` | | | HTML class attribute |
| `style` | `React.CSSProperties` | | | HTML style attribute |

**ReactionProps** (single reaction props) extends `Palette`'s `PaletteOption`:
**ReactionProps** (single reaction props) extends `Palette`'s `PaletteOption` `disabled` and `value` props:

| Property | Type | Required | Default | Description |
| :--------- | :--------------------- | :------: | :------ | :------------------------------------------------------------ |
Expand Down
37 changes: 8 additions & 29 deletions src/components/Reactions/Reaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {block} from '../utils/cn';
import {useReactionsContext} from './context';
import {useReactionsPopup} from './hooks';

export interface ReactionProps
extends Pick<PaletteOption, 'disabled' | 'title' | 'value' | 'content'> {
export interface ReactionProps extends Pick<PaletteOption, 'disabled' | 'value'> {
/**
* Should be true when the user used this reaction.
*/
Expand All @@ -22,28 +21,10 @@ export interface ReactionProps
* If present, when a user hovers over the reaction, a popover appears with `tooltip.content`.
* Can be used to display users who used this reaction.
*/
tooltip?: ReactionTooltipProps;
tooltip?: React.ReactNode;
}

export interface ReactionTooltipProps
extends Pick<PopoverProps, 'strategy' | 'placement' | 'modifiers'> {
/**
* Tooltip's content.
*/
content: React.ReactNode;
/**
* Tooltip content's HTML class attribute.
*/
className?: string;
/**
* Fires when the `onMouseLeave` callback is called.
* Usage example:
* you have some popup inside a tooltip, you hover on it, you don't want the tooltip to be closed because of that.
*/
canClosePopup?: () => boolean;
}

interface ReactionInnerProps {
interface ReactionInnerProps extends Pick<PaletteOption, 'content'> {
reaction: ReactionProps;
size: ButtonSize;
onClick?: (value: string) => void;
Expand All @@ -61,8 +42,8 @@ const popupDefaultPlacement: PopoverProps['placement'] = [
const b = block('reactions');

export function Reaction(props: ReactionInnerProps) {
const {value, disabled, selected, content, counter, tooltip} = props.reaction;
const {size, onClick} = props;
const {value, disabled, selected, counter, tooltip} = props.reaction;
const {size, content, onClick} = props;

const onClickCallback = React.useCallback(() => onClick?.(value), [onClick, value]);

Expand Down Expand Up @@ -94,15 +75,13 @@ export function Reaction(props: ReactionInnerProps) {

{currentHoveredReaction && currentHoveredReaction.reaction.value === value ? (
<Popup
contentClassName={b('popup')}
anchorRef={currentHoveredReaction.ref}
contentClassName={b('popup', tooltip.className)}
placement={tooltip.placement ?? popupDefaultPlacement}
strategy={tooltip.strategy}
modifiers={tooltip.modifiers}
placement={popupDefaultPlacement}
open={currentHoveredReaction.open}
hasArrow
>
{tooltip.content}
{tooltip}
</Popup>
) : null}
</div>
Expand Down
19 changes: 19 additions & 0 deletions src/components/Reactions/Reactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Flex,
Icon,
Palette,
PaletteOption,
PaletteProps,
Popover,
QAProps,
Expand Down Expand Up @@ -65,6 +66,21 @@ export function Reactions({
ReactionsContextTooltipProps | undefined
>(undefined);

const paletteOptionsMap = React.useMemo(
() =>
palette.options
? palette.options.reduce<Record<PaletteOption['value'], PaletteOption>>(
(acc, current) => {
// eslint-disable-next-line no-param-reassign
acc[current.value] = current;
return acc;
},
{},
)
: {},
[palette.options],
);

const paletteValue = React.useMemo(
() => reactions.filter((reaction) => reaction.selected).map((reaction) => reaction.value),
[reactions],
Expand Down Expand Up @@ -100,9 +116,12 @@ export function Reactions({
<Flex className={b(null, className)} style={style} gap={1} wrap={true} qa={qa}>
{/* Reactions' list */}
{reactions.map((reaction) => {
const content = paletteOptionsMap[reaction.value]?.content ?? '?';

return (
<Reaction
key={reaction.value}
content={content}
reaction={disabled ? {...reaction, disabled} : reaction}
size={size}
onClick={onClickReaction}
Expand Down
10 changes: 3 additions & 7 deletions src/components/Reactions/__tests__/mock/mockHooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
ReactionsMockUser,
reactionsPalletteMockOption as option,
reactionsPalletteMockOptions as options,
reactionsPalletteMockOption,
reactionsMockUser as user,
} from './mockData';

Expand Down Expand Up @@ -42,9 +41,8 @@ const renderUsersReacted = (users: ReactionsMockUser[]) => {
);
};

const getTooltip = (users: ReactionsMockUser[]): ReactionProps['tooltip'] => ({
content: renderUsersReacted(users),
});
const getTooltip = (users: ReactionsMockUser[]): ReactionProps['tooltip'] =>
renderUsersReacted(users);

export function useMockReactions(): ReactionsProps {
const [usersReacted, setUsersReacted] = React.useState({
Expand All @@ -60,9 +58,7 @@ export function useMockReactions(): ReactionsProps {
() =>
Object.entries(usersReacted).map(
([value, users]): ReactionProps => ({
...reactionsPalletteMockOption[
value as keyof typeof reactionsPalletteMockOption
],
value,
counter: users.length,
tooltip: getTooltip(users),
selected: users.some(({name}) => name === currentUser.name),
Expand Down
28 changes: 4 additions & 24 deletions src/components/Reactions/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@ export function useReactionsPopup(
ref: React.RefObject<HTMLButtonElement>,
) {
const {value} = reaction;
const canClosePopup = reaction.tooltip?.canClosePopup;

const {openedTooltip: currentHoveredReaction, setOpenedTooltip: setCurrentHoveredReaction} =
useReactionsContext();

const {delayedCall: setDelayedOpen, clearTimeoutRef: clearOpenTimeout} = useTimeoutRef();
const {delayedCall: setDelayedClose, clearTimeoutRef: clearCloseTimeout} = useTimeoutRef();
const {delayedCall: setDelayedCloseRetry, clearTimeoutRef: clearCloseRetryTimeout} =
useTimeoutRef();

const open = useStableCallback(() => {
setCurrentHoveredReaction({reaction, open: true, ref});
Expand All @@ -39,7 +36,6 @@ export function useReactionsPopup(
});

const focus = useStableCallback(() => {
clearCloseRetryTimeout();
clearCloseTimeout();
setCurrentHoveredReaction({reaction, open: false, ref});

Expand All @@ -56,35 +52,19 @@ export function useReactionsPopup(
setDelayedClose(close, DELAY.closeTimeout);
});

const fireClosePopup = useStableCallback(() => {
clearCloseRetryTimeout();

if (canClosePopup ? canClosePopup() : true) {
delayedClosePopup();
} else {
setDelayedCloseRetry(fireClosePopup, DELAY.closeTimeout);
}
});

const onMouseEnter: React.MouseEventHandler<HTMLDivElement> = delayedOpenPopup;

const onMouseLeave: React.MouseEventHandler<HTMLDivElement> = fireClosePopup;

const windowFocusCallback = useStableCallback(() => {
if (currentHoveredReaction?.reaction.value === value && currentHoveredReaction.open) {
setCurrentHoveredReaction({...currentHoveredReaction, open: false});
}
});
const onMouseLeave: React.MouseEventHandler<HTMLDivElement> = delayedClosePopup;

React.useEffect(() => {
// When the tab gets focus we need to hide the popup,
// because the user might have changed the cursor position.
window.addEventListener('focus', windowFocusCallback);
window.addEventListener('focus', close);

return () => {
window.removeEventListener('focus', windowFocusCallback);
window.removeEventListener('focus', close);
};
}, [windowFocusCallback]);
}, [close]);

return {onMouseEnter, onMouseLeave};
}
Expand Down

0 comments on commit 855b5d4

Please sign in to comment.