From 6eccd446342564612a63623d8a435bec59551b67 Mon Sep 17 00:00:00 2001 From: Vlad Furman Date: Thu, 5 Sep 2024 14:25:45 +0300 Subject: [PATCH] feat(Reactions): add `addButtonPlacement` property (#215) --- src/components/Reactions/README.md | 44 ++++++++------- src/components/Reactions/Reactions.tsx | 54 +++++++++++-------- .../__stories__/Reactions.stories.tsx | 15 ++++++ 3 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/components/Reactions/README.md b/src/components/Reactions/README.md index b3971ec..4e78ed4 100644 --- a/src/components/Reactions/README.md +++ b/src/components/Reactions/README.md @@ -1,14 +1,13 @@ ## Reactions -Component for user reactions (e.g. 👍, 😊, 😎 etc) as new GitHub comments for example. +Component for user reactions (e.g. 👍, 😊, 😎 etc) as in GitHub comments for example. ### Usage example ```typescript import React from 'react'; -import {PaletteOption} from '@gravity-ui/uikit'; -import {ReactionState, Reactions} from '@gravity-ui/components'; +import {Reactions, ReactionProps, ReactionState} from '@gravity-ui/components'; const user = { spongeBob: {name: 'Sponge Bob'}, @@ -20,18 +19,18 @@ const currentUser = user.spongeBob; const option = { 'thumbs-up': {content: '👍', value: 'thumbs-up'}, cool: {content: '😎', value: 'cool'}, -} satisfies Record; +} satisfies Record; -const options = Object.values(option); +const options: ReactionProps[] = Object.values(option); -const YourComponent = () => { +export const YourComponent = () => { // You can set up a mapping: reaction.value -> users reacted const [usersReacted, setUsersReacted] = React.useState({ [option.cool.value]: [user.spongeBob], }); // And then convert that mapping into an array of ReactionState - const reactions = React.useMemo( + const reactionsState = React.useMemo( () => Object.entries(usersReacted).map( ([value, users]): ReactionState => ({ @@ -44,7 +43,7 @@ const YourComponent = () => { ); // You can then handle clicking on a reaction with changing the inital mapping, - // and the array of ReactionState will change accordingly + // and the reactionsState array will change accordingly const onToggle = React.useCallback( (value: string) => { if (!usersReacted[value]) { @@ -74,9 +73,7 @@ const YourComponent = () => { [usersReacted], ); - return ( - - ); + return ; }; ``` @@ -86,18 +83,19 @@ For more code examples go to [Reactions.stories.tsx](https://github.com/gravity- **ReactionsProps** (main component props — Reactions' list): -| Property | Type | Required | Default | Description | -| :--------------- | :------------------------------------------ | :------: | :------ | :--------------------------------------------------------------------------------------------- | -| `className` | `string` | | | HTML `class` attribute | -| `onToggle` | `(value: string) => void` | | | Fires when a user clicks on a Reaction (in a Palette or in the Reactions' list) | -| `paletteProps` | `ReactionsPaletteProps` | `true` | | Notifications' palette props — it's a `Palette` component with available reactions to the user | -| `qa` | `string` | | | `qa` attribute for testing | -| `reactions` | `PaletteOption[]` | `true` | | List of all available reactions | -| `reactionsState` | `ReactionState[]` | `true` | | List of reactions that were used | -| `readOnly` | `boolean` | | `false` | readOnly state (usage example: only signed in users can react) | -| `renderTooltip` | `(state: ReactionState) => React.ReactNode` | | | Reaction's tooltip with the list of reacted users for example | -| `size` | `ButtonSize` | | `m` | Buttons's size | -| `style` | `React.CSSProperties` | | | HTML `style` attribute | +| Property | Type | Required | Default | Description | +| :------------------- | :------------------------------------------ | :------: | :------ | :--------------------------------------------------------------------------------------------- | +| `addButtonPlacement` | `'start' or 'end'` | | `'end'` | Position of the "Add reaction" button. | +| `className` | `string` | | | HTML `class` attribute | +| `onToggle` | `(value: string) => void` | | | Fires when a user clicks on a Reaction (in a Palette or in the Reactions' list) | +| `paletteProps` | `ReactionsPaletteProps` | `true` | | Notifications' palette props — it's a `Palette` component with available reactions to the user | +| `qa` | `string` | | | `qa` attribute for testing | +| `reactions` | `PaletteOption[]` | `true` | | List of all available reactions | +| `reactionsState` | `ReactionState[]` | `true` | | List of reactions that were used | +| `readOnly` | `boolean` | | `false` | readOnly state (usage example: only signed in users can react) | +| `renderTooltip` | `(state: ReactionState) => React.ReactNode` | | | Reaction's tooltip with the list of reacted users for example | +| `size` | `ButtonSize` | | `m` | Buttons's size | +| `style` | `React.CSSProperties` | | | HTML `style` attribute | **ReactionState** (single reaction props): diff --git a/src/components/Reactions/Reactions.tsx b/src/components/Reactions/Reactions.tsx index 08e783e..8453ad5 100644 --- a/src/components/Reactions/Reactions.tsx +++ b/src/components/Reactions/Reactions.tsx @@ -46,6 +46,12 @@ export interface ReactionsProps extends Pick, QAProps, DOM * Reactions' readonly state (when a user is unable to react for some reason). */ readOnly?: boolean; + /** + * Position of the "Add reaction" button. + * + * @default 'end' + */ + addButtonPlacement?: 'start' | 'end'; /** * If present, when a user hovers over the reaction, a popover appears with renderTooltip(state) content. * Can be used to display users who used this reaction. @@ -74,6 +80,7 @@ export function Reactions({ paletteProps, readOnly, qa, + addButtonPlacement = 'end', renderTooltip, onToggle, }: ReactionsProps) { @@ -122,6 +129,28 @@ export function Reactions({ [paletteProps, reactions, paletteValue, size, onUpdatePalette], ); + const addReactionButton = readOnly ? null : ( + + + + ); + return ( + {addButtonPlacement === 'start' ? addReactionButton : null} + {/* Reactions' list */} {reactionsState.map((reaction) => { const content = paletteOptionsMap[reaction.value]?.content ?? '?'; @@ -146,28 +177,7 @@ export function Reactions({ ); })} - {/* Add reaction button */} - {readOnly ? null : ( - - - - )} + {addButtonPlacement === 'end' ? addReactionButton : null} ); diff --git a/src/components/Reactions/__stories__/Reactions.stories.tsx b/src/components/Reactions/__stories__/Reactions.stories.tsx index 435b4b5..c1f9bb1 100644 --- a/src/components/Reactions/__stories__/Reactions.stories.tsx +++ b/src/components/Reactions/__stories__/Reactions.stories.tsx @@ -64,3 +64,18 @@ export const Size: StoryFn = () => { ); }; + +export const AddButtonPlacement: StoryFn = () => { + return ( + + + Start + + + + End + + + + ); +};