From 13f731fa2c98e2f6d81786f6f62a0299a0b583f9 Mon Sep 17 00:00:00 2001 From: Mikhail Golbakh Date: Wed, 31 Jan 2024 11:51:23 +0300 Subject: [PATCH] feat: refactor Persona and rename it to UserLabel --- CODEOWNERS | 2 +- src/components/Avatar/README.md | 1 + src/components/Persona/Persona.tsx | 74 ---------- src/components/Persona/README.md | 120 --------------- .../Persona/__stories__/Persona.stories.tsx | 52 ------- src/components/Persona/i18n/en.json | 3 - src/components/Persona/i18n/ru.json | 3 - src/components/Persona/index.ts | 2 - src/components/Persona/types.ts | 30 ---- src/components/Persona/utils.ts | 17 --- src/components/User/README.md | 1 + src/components/UserLabel/README.md | 126 ++++++++++++++++ src/components/UserLabel/UserLabel.scss | 138 ++++++++++++++++++ src/components/UserLabel/UserLabel.tsx | 101 +++++++++++++ .../__stories__/UserLabel.stories.tsx | 60 ++++++++ .../__tests__/UserLabel.test.tsx} | 22 +-- src/components/UserLabel/i18n/en.json | 3 + .../{Persona => UserLabel}/i18n/index.ts | 2 +- src/components/UserLabel/i18n/ru.json | 3 + src/components/UserLabel/index.ts | 2 + src/components/UserLabel/types.ts | 23 +++ src/components/UserLabel/utils.ts | 17 +++ src/components/index.ts | 2 +- 23 files changed, 489 insertions(+), 315 deletions(-) delete mode 100644 src/components/Persona/Persona.tsx delete mode 100644 src/components/Persona/README.md delete mode 100644 src/components/Persona/__stories__/Persona.stories.tsx delete mode 100644 src/components/Persona/i18n/en.json delete mode 100644 src/components/Persona/i18n/ru.json delete mode 100644 src/components/Persona/index.ts delete mode 100644 src/components/Persona/types.ts delete mode 100644 src/components/Persona/utils.ts create mode 100644 src/components/UserLabel/README.md create mode 100644 src/components/UserLabel/UserLabel.scss create mode 100644 src/components/UserLabel/UserLabel.tsx create mode 100644 src/components/UserLabel/__stories__/UserLabel.stories.tsx rename src/components/{Persona/__tests__/Persona.test.tsx => UserLabel/__tests__/UserLabel.test.tsx} (75%) create mode 100644 src/components/UserLabel/i18n/en.json rename src/components/{Persona => UserLabel}/i18n/index.ts (67%) create mode 100644 src/components/UserLabel/i18n/ru.json create mode 100644 src/components/UserLabel/index.ts create mode 100644 src/components/UserLabel/types.ts create mode 100644 src/components/UserLabel/utils.ts diff --git a/CODEOWNERS b/CODEOWNERS index 77ad81ab60..45bb659065 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -23,7 +23,6 @@ /src/components/Menu @NikitaCG /src/components/Modal @amje /src/components/Pagination @jhoncool -/src/components/Persona @DaffPunks /src/components/Popover @kseniya57 /src/components/Popup @amje /src/components/Portal @amje @@ -32,6 +31,7 @@ /src/components/RadioButton @zamkovskaya /src/components/RadioGroup @zamkovskaya /src/components/User @DakEnviy +/src/components/UserLabel @DakEnviy /src/components/Select @korvin89 /src/components/Sheet @mournfulCoroner /src/components/Skeleton @SeqviriouM diff --git a/src/components/Avatar/README.md b/src/components/Avatar/README.md index 9a47370830..08cfb6db27 100644 --- a/src/components/Avatar/README.md +++ b/src/components/Avatar/README.md @@ -178,6 +178,7 @@ LANDING_BLOCK--> | aria-label | `aria-label` for avatar block | `string` | | | aria-labelledby | `aria-labelledby` for avatar block | `string` | | | className | Custom CSS class for root element | `string` | | +| style | HTML style attribute | `React.CSSProperties` | | | qa | HTML `data-qa` attribute, used in tests | `string` | | ### Image-specific diff --git a/src/components/Persona/Persona.tsx b/src/components/Persona/Persona.tsx deleted file mode 100644 index f4abf64183..0000000000 --- a/src/components/Persona/Persona.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; - -import {Envelope} from '@gravity-ui/icons'; - -import {Avatar} from '../Avatar'; -import {PersonaWrap} from '../PersonaWrap'; - -import i18n from './i18n'; -import type {PersonaProps} from './types'; -import {extractTextValue, extractTextView} from './utils'; - -export function Persona({ - size = 's', - theme = 'default', - hasBorder = theme === 'default', - type = 'person', - onClick, - onClose, - text, - image, - className, - style, - qa, -}: PersonaProps) { - const textValue = extractTextValue(text); - const textView = extractTextView(text); - const closeButtonAriaAttributes: React.AriaAttributes = { - 'aria-label': i18n('label_remove-button', {persona: textValue}), - }; - let avatar: React.ReactNode | null; - - switch (type) { - case 'person': - avatar = image ? ( - - ) : ( - - ); - break; - case 'email': - avatar = ; - break; - case 'empty': - avatar = null; - break; - } - - const handleClick = React.useCallback(() => { - onClick?.(textValue); - }, [textValue, onClick]); - - const handleClose = React.useCallback(() => { - onClose?.(textValue); - }, [textValue, onClose]); - - return ( - - {textView} - - ); -} - -Persona.displayName = 'Persona'; diff --git a/src/components/Persona/README.md b/src/components/Persona/README.md deleted file mode 100644 index 498dfd31fb..0000000000 --- a/src/components/Persona/README.md +++ /dev/null @@ -1,120 +0,0 @@ -# Persona - -The `Persona` component can be used to display users or user-related information. - -### Image - -This component can be used with a custom image. It works only with `type: "person"`. - - - - - -```tsx - -``` - - - -### Type - -Used to manage avatar appearance. Use "person" for a personalized entity and "email" for an email adresses. Use "other" for cases when you do not need any avatar. - - - - - -```tsx - - - -``` - - - -### Size - - - - - -```tsx - - -``` - - - -S: Basic size, used in most components. -N: Used when regular labels are too small. - -### Interactivity - -This component is also interactive. It can be clickable or closable. - - - - - -```tsx - alert('onClick triggered')} /> - alert('onClose triggered')} /> -``` - - - -## Properties - -| Name | Description | Type | Default | -| :-------- | :---------------------------------------------------------- | :----------------------------: | :--------: | -| text | Visible text | `string` | | -| image | Image source | `string` | | -| hasBorder | Display border | `boolean` | `true` | -| type | Avatar appearance | `"person"` `"email"` `"empty"` | `"person"` | -| size | Text size | `"s"` `"n"` | `"s"` | -| onClose | Handles click on button with cross `(text: string) => void` | `Function` | | -| onClick | Handles click on component itself `(text: string) => void` | `Function` | | -| className | Custom CSS class for root element | `string` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | diff --git a/src/components/Persona/__stories__/Persona.stories.tsx b/src/components/Persona/__stories__/Persona.stories.tsx deleted file mode 100644 index 960489a460..0000000000 --- a/src/components/Persona/__stories__/Persona.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -import {faker} from '@faker-js/faker/locale/en'; -import type {ComponentMeta, ComponentStory} from '@storybook/react'; - -import {Persona} from '../Persona'; - -export default { - title: 'Components/Data Display/Persona', - component: Persona, -} as ComponentMeta; - -const person = 'Charles Darwin'; -const email = faker.internet.email(...person.split(' ')); -const personImg = faker.internet.avatar(); - -const Template: ComponentStory = (args) => ; - -export const Default = Template.bind({}); -Default.args = { - text: person, -}; - -export const Image = Template.bind({}); -Image.args = { - text: person, - image: personImg, -}; - -export const Email = Template.bind({}); -Email.args = { - text: email, - type: 'email', -}; - -export const Empty = Template.bind({}); -Empty.args = { - text: person, - type: 'empty', -}; - -export const Clickable = Template.bind({}); -Clickable.args = { - text: person, - onClick: (text) => console.log('clicked', text), -}; - -export const Closable = Template.bind({}); -Closable.args = { - text: person, - onClose: (text) => console.log('closed', text), -}; diff --git a/src/components/Persona/i18n/en.json b/src/components/Persona/i18n/en.json deleted file mode 100644 index cd6d43cd47..0000000000 --- a/src/components/Persona/i18n/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label_remove-button": "Remove {{persona}}" -} diff --git a/src/components/Persona/i18n/ru.json b/src/components/Persona/i18n/ru.json deleted file mode 100644 index 120e2bed9b..0000000000 --- a/src/components/Persona/i18n/ru.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label_remove-button": "Удалить {{persona}}" -} diff --git a/src/components/Persona/index.ts b/src/components/Persona/index.ts deleted file mode 100644 index ebddc01408..0000000000 --- a/src/components/Persona/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {Persona} from './Persona'; -export type {PersonaProps} from './types'; diff --git a/src/components/Persona/types.ts b/src/components/Persona/types.ts deleted file mode 100644 index 9210146ca1..0000000000 --- a/src/components/Persona/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type React from 'react'; - -import type {QAProps} from '../types'; - -export type PersonaText = string | {value: string; content: React.ReactNode}; - -export type PersonaProps = { - /** Visible text node */ - text: PersonaText; - /** Image source */ - image?: string; - /** - * Visual appearance (with or without border) - * @deprecated Use `hasBorder` prop instead - */ - theme?: 'default' | 'clear'; - /** Display border */ - hasBorder?: boolean; - /** Avatar appearance */ - type?: 'person' | 'email' | 'empty'; - /** Text size */ - size?: 's' | 'n'; - /** Handle click on button with cross */ - onClose?: (text: string) => void; - /** Handle click on component itself */ - onClick?: (text: string) => void; - /** Custom CSS class for root element */ - className?: string; - style?: React.CSSProperties; -} & QAProps; diff --git a/src/components/Persona/utils.ts b/src/components/Persona/utils.ts deleted file mode 100644 index ef07849bff..0000000000 --- a/src/components/Persona/utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type {PersonaText} from './types'; - -export const extractTextValue = (text: PersonaText = '') => { - if (text && typeof text === 'object') { - return text.value; - } - - return text; -}; - -export const extractTextView = (text: PersonaText = '') => { - if (text && typeof text === 'object') { - return text.content; - } - - return text; -}; diff --git a/src/components/User/README.md b/src/components/User/README.md index 9b779009f5..4e9a0d183c 100644 --- a/src/components/User/README.md +++ b/src/components/User/README.md @@ -63,4 +63,5 @@ LANDING_BLOCK--> | aria-label | `aria-label` for user block | `string` | | | aria-labelledby | `aria-labelledby` for user block | `string` | | | className | Custom CSS class for root element | `string` | | +| style | HTML style attribute | `React.CSSProperties` | | | qa | HTML `data-qa` attribute, used in tests | `string` | | diff --git a/src/components/UserLabel/README.md b/src/components/UserLabel/README.md new file mode 100644 index 0000000000..ce9712cb80 --- /dev/null +++ b/src/components/UserLabel/README.md @@ -0,0 +1,126 @@ +# UserLabel + +The `UserLabel` component can be used to display users or user-related information. + +### Type + +Used to manage avatar appearance. Use "person" for a personalized entity and "email" for an email adresses. Use "empty" for cases when you do not need any avatar. + + + + + +```tsx + + + +``` + + + +### Avatar + +This component can be used with a custom avatar. It works only with `type: 'person'`. You are able to provide an image, a props of [Avatar](../Avatar/README.md) component or custom React node. + + + + + +```tsx +import {GraduationCap} from '@gravity-ui/icons'; + + + +``` + + + +### Size + + + + + +```tsx + + +``` + + + +S: Basic size, used in most components. +M: Used when regular labels are too small. + +### Interactivity + +This component is also interactive. It can be clickable or closable. + + + + + +```tsx + alert('onClick triggered')} /> + alert('onClose triggered')} /> +``` + + + +## Properties + +| Name | Description | Type | Default | +| :-------- | :---------------------------------------------------------- | :-------------------------------------------------------------------------: | :----------: | +| type | Avatar appearance | `'person'` `'email'` `'empty'` | `'person'` | +| avatar | User avatar | [AvatarProps](../Avatar/README.md#properties) `string` `React.ReactElement` | | +| text | Visible text | `string` | | +| size | Text size | `'s'` `'m'` | `'s'` | +| view | UserLabel view | `'outlined'` `'clear'` | `'outlined'` | +| onClick | Handles click on component itself `(text: string) => void` | `Function` | | +| onClose | Handles click on button with cross `(text: string) => void` | `Function` | | +| className | Custom CSS class for root element | `string` | | +| style | HTML style attribute | `React.CSSProperties` | | +| qa | HTML `data-qa` attribute, used in tests | `string` | | diff --git a/src/components/UserLabel/UserLabel.scss b/src/components/UserLabel/UserLabel.scss new file mode 100644 index 0000000000..044d8ddafc --- /dev/null +++ b/src/components/UserLabel/UserLabel.scss @@ -0,0 +1,138 @@ +@use '../../../styles/mixins'; +@use '../variables'; + +$block: '.#{variables.$ns}user-label'; + +#{$block} { + $transitionDuration: 0.1s; + $transitionTimingFunction: ease-in-out; + + position: relative; + z-index: 0; + display: inline-flex; + height: 28px; + border-radius: 20px; + + transition-property: background-color; + transition-duration: $transitionDuration; + transition-timing-function: $transitionTimingFunction; + + &_view_outlined { + &:after { + position: absolute; + z-index: -1; + inset: 0; + content: ''; + border: 1px solid var(--g-color-line-generic); + border-radius: 20px; + + transition-property: border-color; + transition-duration: $transitionDuration; + transition-timing-function: $transitionTimingFunction; + } + } + + &_empty { + &#{$block}_size_s { + padding-inline-start: 12px; + } + + &#{$block}_size_m { + padding-inline-start: 16px; + } + } + + &_clickable:hover { + cursor: pointer; + background-color: var(--g-color-base-simple-hover); + + &:after { + border-color: transparent; + } + } + + &__main { + @include mixins.button-reset(); + + display: inline-flex; + align-items: center; + border-radius: inherit; + padding-inline-end: 6px; + + #{$block}_closeable & { + padding-inline-end: 0; + } + + #{$block}_clickable & { + outline-offset: -1px; + + &:focus { + outline: 2px solid var(--g-color-line-focus); + } + + &:focus:not(:focus-visible) { + outline: 0; + } + } + } + + &__avatar { + --g-avatar-background-color: var(--g-color-base-generic-accent); + --g-avatar-color: var(--g-color-text-primary); + + display: flex; + + #{$block}_size_m & { + margin-inline-end: 12px; + } + + #{$block}_size_s & { + margin-inline-end: 6px; + } + } + + &__text { + #{$block}_size_m & { + @include mixins.text-body-2; + margin-inline-end: 10px; + } + + #{$block}_size_s & { + @include mixins.text-body-1; + margin-inline-end: 6px; + } + } + + &__close { + @include mixins.button-reset(); + + box-sizing: initial; + display: inline-flex; + justify-content: center; + align-items: center; + width: 16px; + cursor: pointer; + padding-inline-end: 6px; + color: var(--g-color-text-secondary); + + transition-property: color; + transition-duration: $transitionDuration; + transition-timing-function: $transitionTimingFunction; + + &:hover { + color: var(--g-color-text-primary); + } + } + + &__close-icon { + border-radius: var(--g-focus-border-radius); + + #{$block}__close:focus & { + outline: 2px solid var(--g-color-line-focus); + } + + #{$block}__close:focus:not(:focus-visible) & { + outline: 0; + } + } +} diff --git a/src/components/UserLabel/UserLabel.tsx b/src/components/UserLabel/UserLabel.tsx new file mode 100644 index 0000000000..ccc08de8a1 --- /dev/null +++ b/src/components/UserLabel/UserLabel.tsx @@ -0,0 +1,101 @@ +import React from 'react'; + +import {Envelope, Xmark} from '@gravity-ui/icons'; + +import {Avatar, type AvatarProps} from '../Avatar'; +import {Icon} from '../Icon'; +import {block} from '../utils/cn'; + +import i18n from './i18n'; +import type {UserLabelProps} from './types'; +import {extractTextValue, extractTextView} from './utils'; + +import './UserLabel.scss'; + +const COMMON_AVATAR_PROPS: Partial = { + size: 's', +}; + +const b = block('user-label'); + +export const UserLabel = ({ + type = 'person', + avatar, + text, + size = 's', + view = 'outlined', + onClick, + onClose, + className, + style, + qa, +}: UserLabelProps) => { + const textValue = extractTextValue(text); + const textView = extractTextView(text); + + const closeButtonAriaAttributes: React.AriaAttributes = { + 'aria-label': i18n('label_remove-button', {text: textValue}), + }; + + const clickable = Boolean(onClick); + const closeable = Boolean(onClose); + const MainComponent = clickable ? 'button' : 'div'; + + const handleClick = React.useCallback(() => { + onClick?.(textValue); + }, [textValue, onClick]); + + const handleClose = React.useCallback(() => { + onClose?.(textValue); + }, [textValue, onClose]); + + let avatarView: React.ReactNode = null; + + switch (type) { + case 'email': + avatarView = ; + break; + case 'empty': + avatarView = null; + break; + case 'person': + default: + if (!avatar) { + avatarView = ; + } else if (typeof avatar === 'string') { + avatarView = ; + } else if (React.isValidElement(avatar)) { + avatarView = avatar; + } else { + avatarView = ; + } + break; + } + + return ( +
+ + {avatarView ?
{avatarView}
: null} +
{textView}
+
+ {onClose ? ( + + ) : null} +
+ ); +}; diff --git a/src/components/UserLabel/__stories__/UserLabel.stories.tsx b/src/components/UserLabel/__stories__/UserLabel.stories.tsx new file mode 100644 index 0000000000..0753f7b9bd --- /dev/null +++ b/src/components/UserLabel/__stories__/UserLabel.stories.tsx @@ -0,0 +1,60 @@ +import {faker} from '@faker-js/faker/locale/en'; +import type {Meta, StoryObj} from '@storybook/react'; + +import {UserLabel} from '../UserLabel'; + +const meta: Meta = { + title: 'Components/Data Display/UserLabel', + component: UserLabel, +}; + +export default meta; + +type Story = StoryObj; + +const person = 'Charles Darwin'; +const email = faker.internet.email(...person.split(' ')); +const personImg = faker.internet.avatar(); + +export const Default: Story = { + args: { + text: person, + }, +}; + +export const Image: Story = { + args: { + avatar: { + imgUrl: personImg, + }, + text: person, + }, +}; + +export const Email: Story = { + args: { + type: 'email', + text: email, + }, +}; + +export const Empty: Story = { + args: { + type: 'empty', + text: person, + }, +}; + +export const Clickable: Story = { + args: { + text: person, + onClick: (value) => console.log('clicked', value), + }, +}; + +export const Closable: Story = { + args: { + text: person, + onClose: (value) => console.log('closed', value), + }, +}; diff --git a/src/components/Persona/__tests__/Persona.test.tsx b/src/components/UserLabel/__tests__/UserLabel.test.tsx similarity index 75% rename from src/components/Persona/__tests__/Persona.test.tsx rename to src/components/UserLabel/__tests__/UserLabel.test.tsx index e88b998c13..2257f6e1cc 100644 --- a/src/components/Persona/__tests__/Persona.test.tsx +++ b/src/components/UserLabel/__tests__/UserLabel.test.tsx @@ -4,25 +4,25 @@ import userEvent from '@testing-library/user-event'; import {queryByAttribute, render, screen} from '../../../../test-utils/utils'; import {getAvatarDisplayText} from '../../Avatar'; -import {Persona} from '../Persona'; +import {UserLabel} from '../UserLabel'; import i18n from '../i18n'; -import type {PersonaProps} from '../types'; +import type {UserLabelProps} from '../types'; import {extractTextValue} from '../utils'; const MOCKED_TEXT = 'text'; const MOCKED_TEXT_NODE_CONTENT_VALUE = 'Some view'; -const MOCKED_TEXT_NODE: PersonaProps['text'] = { +const MOCKED_TEXT_NODE: UserLabelProps['text'] = { value: MOCKED_TEXT, content:
{MOCKED_TEXT_NODE_CONTENT_VALUE}
, }; -describe('Persona', () => { +describe('UserLabel', () => { describe('text property', () => { - test.each([MOCKED_TEXT, MOCKED_TEXT_NODE])( + test.each([MOCKED_TEXT, MOCKED_TEXT_NODE])( 'should return text value as onClick argument', async (text) => { const onClick = jest.fn(); - render(); + render(); const user = userEvent.setup(); const textValue = extractTextValue(text); const displayText = getAvatarDisplayText(textValue); @@ -31,14 +31,14 @@ describe('Persona', () => { expect(onClick).toBeCalledWith(textValue); }, ); - test.each([MOCKED_TEXT, MOCKED_TEXT_NODE])( + test.each([MOCKED_TEXT, MOCKED_TEXT_NODE])( 'should return text value as onClose argument', async (text) => { const onClose = jest.fn(); - const {container} = render(); + const {container} = render(); const user = userEvent.setup(); const textValue = extractTextValue(text); - const ariaLabelValue = i18n('label_remove-button', {persona: textValue}); + const ariaLabelValue = i18n('label_remove-button', {text: textValue}); const closeButtonNode = queryByAttribute('aria-label', container, ariaLabelValue); if (!closeButtonNode) { @@ -50,11 +50,11 @@ describe('Persona', () => { }, ); test('should render text as string', () => { - render(); + render(); screen.getByText(MOCKED_TEXT); }); test('should render text as react node', () => { - render(); + render(); screen.getByText(MOCKED_TEXT_NODE_CONTENT_VALUE); }); }); diff --git a/src/components/UserLabel/i18n/en.json b/src/components/UserLabel/i18n/en.json new file mode 100644 index 0000000000..e7cf5f7775 --- /dev/null +++ b/src/components/UserLabel/i18n/en.json @@ -0,0 +1,3 @@ +{ + "label_remove-button": "Remove {{text}}" +} diff --git a/src/components/Persona/i18n/index.ts b/src/components/UserLabel/i18n/index.ts similarity index 67% rename from src/components/Persona/i18n/index.ts rename to src/components/UserLabel/i18n/index.ts index 8e73ff64a0..16a3d8957c 100644 --- a/src/components/Persona/i18n/index.ts +++ b/src/components/UserLabel/i18n/index.ts @@ -4,4 +4,4 @@ import {NAMESPACE} from '../../utils/cn'; import en from './en.json'; import ru from './ru.json'; -export default addComponentKeysets({en, ru}, `${NAMESPACE}persona-remove-button`); +export default addComponentKeysets({en, ru}, `${NAMESPACE}user-label`); \ No newline at end of file diff --git a/src/components/UserLabel/i18n/ru.json b/src/components/UserLabel/i18n/ru.json new file mode 100644 index 0000000000..966e87e9f7 --- /dev/null +++ b/src/components/UserLabel/i18n/ru.json @@ -0,0 +1,3 @@ +{ + "label_remove-button": "Удалить {{text}}" +} diff --git a/src/components/UserLabel/index.ts b/src/components/UserLabel/index.ts new file mode 100644 index 0000000000..7d8560c997 --- /dev/null +++ b/src/components/UserLabel/index.ts @@ -0,0 +1,2 @@ +export {UserLabel} from './UserLabel'; +export type {UserLabelProps} from './types'; diff --git a/src/components/UserLabel/types.ts b/src/components/UserLabel/types.ts new file mode 100644 index 0000000000..5148c43798 --- /dev/null +++ b/src/components/UserLabel/types.ts @@ -0,0 +1,23 @@ +import type React from 'react'; + +import type {DistributiveOmit} from '../../types/utils'; +import type {AvatarProps} from '../Avatar'; +import type {DOMProps, QAProps} from '../types'; + +export type UserLabelType = 'person' | 'email' | 'empty'; +export type UserLabelText = {value: string; content: React.ReactNode} | string; +export type UserLabelSize = 's' | 'm'; +export type UserLabelView = 'outlined' | 'clear'; + +export interface UserLabelProps extends DOMProps, QAProps { + type?: UserLabelType; + avatar?: + | DistributiveOmit + | string + | React.ReactElement; + text: UserLabelText; + size?: UserLabelSize; + view?: UserLabelView; + onClick?: (value: string) => void; + onClose?: (value: string) => void; +} diff --git a/src/components/UserLabel/utils.ts b/src/components/UserLabel/utils.ts new file mode 100644 index 0000000000..d9697d8a3c --- /dev/null +++ b/src/components/UserLabel/utils.ts @@ -0,0 +1,17 @@ +import type {UserLabelText} from './types'; + +export const extractTextValue = (text: UserLabelText) => { + if (typeof text === 'object') { + return text.value; + } + + return text; +}; + +export const extractTextView = (text: UserLabelText) => { + if (typeof text === 'object') { + return text.content; + } + + return text; +}; diff --git a/src/components/index.ts b/src/components/index.ts index d9b69db807..04407d597d 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -25,7 +25,7 @@ export * from './Loader'; export * from './Menu'; export * from './Modal'; export * from './Pagination'; -export * from './Persona'; +export * from './UserLabel'; export * from './Popover'; export * from './Popup'; export * from './Portal';